...
This commit is contained in:
parent
f8060cfa40
commit
1195393fa8
@ -32,13 +32,9 @@ else()
|
||||
find_package(ASIO)
|
||||
endif()
|
||||
|
||||
find_package(spdlog CONFIG)
|
||||
# set(SPDLOG_USE_STD_FORMAT ON)
|
||||
# set(SPDLOG_FMT_EXTERNAL OFF )
|
||||
# set(SPDLOG_MASTER_PROJECT OFF)
|
||||
# set(SPDLOG_BUILD_EXAMPLE OFF)
|
||||
# find_package(spdlog CONFIG)
|
||||
|
||||
# set(spdlog_FOUND FALSE )
|
||||
set(spdlog_FOUND FALSE )
|
||||
if (NOT spdlog_FOUND)
|
||||
message(STATUS "SPDLOG libarary was not found! Try to download it!")
|
||||
|
||||
@ -70,6 +66,9 @@ if (NOT spdlog_FOUND)
|
||||
find_package(spdlog CONFIG)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(SPDLOG_USE_STD_FORMAT)
|
||||
add_compile_definitions(SPDLOG_FMT_EXTERNAL=0)
|
||||
|
||||
option(WITH_TESTS "Build tests" ON)
|
||||
|
||||
# Mount client-to-server communication protocol
|
||||
@ -91,4 +90,6 @@ if (WITH_TESTS)
|
||||
set(CNTR_PROTO_TEST_APP cntr_proto_test)
|
||||
add_executable(${CNTR_PROTO_TEST_APP} tests/cntr_proto_test.cpp)
|
||||
target_link_libraries(${CNTR_PROTO_TEST_APP} ${CNTR_PROTO_LIB} spdlog::spdlog_header_only)
|
||||
# target_link_libraries(${CNTR_PROTO_TEST_APP} ${CNTR_PROTO_LIB})
|
||||
# target_include_directories(${CNTR_PROTO_TEST_APP} PUBLIC ${SPDLOG_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@ -9,7 +9,9 @@
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/local/seq_packet_protocol.hpp>
|
||||
#include <asio/local/stream_protocol.hpp>
|
||||
#include <asio/read.hpp>
|
||||
#include <asio/serial_port.hpp>
|
||||
#include <asio/streambuf.hpp>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
@ -32,13 +34,33 @@ template <typename T>
|
||||
static constexpr bool is_serial_proto = std::derived_from<T, asio::serial_port>;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_tcp_proto = std::derived_from<T, asio::ip::tcp::endpoint>;
|
||||
static constexpr bool is_tcp_proto = std::derived_from<typename T::protocol_type, asio::ip::tcp>;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_local_stream_proto = std::derived_from<T, asio::local::stream_protocol::endpoint>;
|
||||
static constexpr bool is_local_stream_proto =
|
||||
std::derived_from<typename T::protocol_type, asio::local::stream_protocol>;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_local_seqpack_proto = std::derived_from<T, asio::local::seq_packet_protocol::endpoint>;
|
||||
static constexpr bool is_local_seqpack_proto =
|
||||
std::derived_from<typename T::protocol_type, asio::local::seq_packet_protocol>;
|
||||
|
||||
// template <typename T>
|
||||
// static constexpr bool is_tcp_proto = std::derived_from<T, asio::ip::tcp::endpoint>;
|
||||
|
||||
// template <typename T>
|
||||
// static constexpr bool is_local_stream_proto = std::derived_from<T, asio::local::stream_protocol::endpoint>;
|
||||
|
||||
// template <typename T>
|
||||
// static constexpr bool is_local_seqpack_proto = std::derived_from<T, asio::local::seq_packet_protocol::endpoint>;
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_time_duration_c = requires {
|
||||
[]<class RT, class PT>(std::type_identity<std::chrono::duration<RT, PT>>) {
|
||||
|
||||
}(std::type_identity<std::remove_cvref_t<T>>());
|
||||
};
|
||||
|
||||
|
||||
} // namespace traits
|
||||
|
||||
@ -46,6 +68,9 @@ static constexpr bool is_local_seqpack_proto = std::derived_from<T, asio::local:
|
||||
class MccMountServer
|
||||
{
|
||||
public:
|
||||
static constexpr std::chrono::duration DEFAULT_RCV_TIMEOUT = std::chrono::hours(12);
|
||||
static constexpr std::chrono::duration DEFAULT_SND_TIMEOUT = std::chrono::milliseconds(2000);
|
||||
|
||||
MccMountServer(asio::io_context& ctx, std::shared_ptr<spdlog::logger> logger)
|
||||
: _asioContext(ctx), _serverLogger(std::move(logger))
|
||||
{
|
||||
@ -109,7 +134,7 @@ public:
|
||||
try {
|
||||
asio::ip::tcp::resolver res(_asioContext);
|
||||
|
||||
auto r_result = co_await res.async_resolve(endpoint.host(), endpoint.portView(), asio::deferred);
|
||||
auto r_result = co_await res.async_resolve(endpoint.host(), endpoint.portView(), asio::use_awaitable);
|
||||
|
||||
for (auto const& epn : r_result) {
|
||||
exit_flag = co_await listen(epn);
|
||||
@ -134,56 +159,64 @@ public:
|
||||
co_return true;
|
||||
}
|
||||
|
||||
asio::awaitable<bool> listen(traits::mcc_endpoint_c auto endpoint)
|
||||
template <traits::mcc_endpoint_c EpnT>
|
||||
asio::awaitable<void> listen(EpnT endpoint)
|
||||
{
|
||||
using epn_t = std::decay_t<decltype(endpoint)>;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
if constexpr (traits::is_serial_proto<epn_t>) {
|
||||
// first, check if port is openned
|
||||
// first, check if port is open
|
||||
if (!endpoint.is_open()) {
|
||||
if (ec) {
|
||||
// ??????????
|
||||
_serverLogger->error("Serial port was not open! Do not start waiting for commands!");
|
||||
co_return false;
|
||||
}
|
||||
} else {
|
||||
asio::co_spawn(_asioContext, startSession(std::move(endpoint)), asio::detached);
|
||||
}
|
||||
|
||||
_serialPorts.emplace_back(std::move(endpoint));
|
||||
|
||||
} else if constexpr (traits::is_tcp_proto<epn_t> || traits::is_local_stream_proto<epn_t> ||
|
||||
traits::is_local_seqpack_proto<epn_t>) {
|
||||
try {
|
||||
std::stringstream st;
|
||||
|
||||
_serverLogger->debug("Create connection acceptor ...");
|
||||
auto acc = epn_t::protocol_type::acceptor(_asioContext, endpoint);
|
||||
st << acc.local_endpoint();
|
||||
_serverLogger->info("Try to start listening at <{}> endpoint ...", st.str());
|
||||
|
||||
if constexpr (traits::is_tcp_proto<epn_t>) {
|
||||
_tcpAcceptors.emplace_back(std::move(acc));
|
||||
_tcpAcceptors.emplace_back(&acc);
|
||||
} else if constexpr (traits::is_local_stream_proto<epn_t>) {
|
||||
_localStreamAcceptors.emplace_back(std::move(acc));
|
||||
_localStreamAcceptors.emplace_back(&acc);
|
||||
} else if constexpr (traits::is_local_seqpack_proto<epn_t>) {
|
||||
_localSeqpackAcceptors.emplace_back(std::move(acc));
|
||||
_localSeqpackAcceptors.emplace_back(&acc);
|
||||
} else {
|
||||
static_assert(false, "INVALID ENDPOINT!!!");
|
||||
}
|
||||
|
||||
// start accepting connections
|
||||
for (;;) {
|
||||
auto sock = co_await acc.async_accept(asio::use_awaitable);
|
||||
// start new client session
|
||||
asio::co_spawn(_asioContext, startSession(std::move(sock)), asio::detached);
|
||||
}
|
||||
|
||||
|
||||
} catch (const std::system_error& err) {
|
||||
_serverLogger->error("An error occured while creating of connection acceptor! ec = '{}'", err.what());
|
||||
_serverLogger->error("An error occured while trying to start accepting connections! ec = '{}'",
|
||||
err.what());
|
||||
}
|
||||
} else {
|
||||
static_assert(false, "INVALID ENDPOINT!!!");
|
||||
}
|
||||
|
||||
co_return true;
|
||||
}
|
||||
|
||||
|
||||
// close listening on all endpoints
|
||||
void stop()
|
||||
void stopListening()
|
||||
{
|
||||
std::error_code ec;
|
||||
size_t N = 0, M = 0;
|
||||
@ -197,94 +230,53 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (_serialPorts.size()) {
|
||||
_serverLogger->debug("Close serial ports ...");
|
||||
auto close_func = [this](auto& acc_ptrs, std::string_view desc) {
|
||||
size_t N = 0, M = 0;
|
||||
std::error_code ec;
|
||||
|
||||
for (auto& s_port : _serialPorts) {
|
||||
s_port.close(ec);
|
||||
if (ec) {
|
||||
_serverLogger->error("Cannot close serial port! ec = '{}'", ec.message());
|
||||
++M;
|
||||
if (acc_ptrs.size()) {
|
||||
_serverLogger->info("Close {} acceptors ...", desc);
|
||||
|
||||
for (auto& acc : acc_ptrs) {
|
||||
acc->close(ec);
|
||||
if (ec) {
|
||||
_serverLogger->error("Cannot close {} acceptor! ec = '{}'", desc, ec.message());
|
||||
++M;
|
||||
}
|
||||
++N;
|
||||
}
|
||||
++N;
|
||||
|
||||
_serverLogger->debug("{} from {} {} acceptors were closed!", M, N, desc);
|
||||
|
||||
// pointers are invalidated here, so clear its container
|
||||
acc_ptrs.clear();
|
||||
}
|
||||
};
|
||||
|
||||
_serverLogger->debug("{} from {} serial ports were closed!", M, N);
|
||||
close_func(_tcpAcceptors, "TCP socket");
|
||||
close_func(_localStreamAcceptors, "local stream socket");
|
||||
close_func(_localSeqpackAcceptors, "local seqpack socket");
|
||||
|
||||
_serialPorts.clear();
|
||||
}
|
||||
|
||||
if (_tcpAcceptors.size()) {
|
||||
_serverLogger->debug("Close TCP listening sockets ...");
|
||||
N = 0;
|
||||
M = 0;
|
||||
|
||||
for (auto& acc : _tcpAcceptors) {
|
||||
acc.close(ec);
|
||||
if (ec) {
|
||||
_serverLogger->error("Cannot close TCP socket! ec = '{}'", ec.message());
|
||||
++M;
|
||||
}
|
||||
++N;
|
||||
}
|
||||
|
||||
_serverLogger->debug("{} from {} TCP sockets were closed!", M, N);
|
||||
|
||||
_tcpAcceptors.clear();
|
||||
}
|
||||
|
||||
if (_localStreamAcceptors.size()) {
|
||||
_serverLogger->debug("Close local stream listening sockets ...");
|
||||
N = 0;
|
||||
M = 0;
|
||||
|
||||
for (auto& acc : _localStreamAcceptors) {
|
||||
acc.close(ec);
|
||||
if (ec) {
|
||||
_serverLogger->error("Cannot close local stream socket! ec = '{}'", ec.message());
|
||||
++M;
|
||||
}
|
||||
++N;
|
||||
}
|
||||
|
||||
_serverLogger->debug("{} from {} local stream sockets were closed!", M, N);
|
||||
|
||||
_localStreamAcceptors.clear();
|
||||
}
|
||||
|
||||
if (_localSeqpackAcceptors.size()) {
|
||||
_serverLogger->debug("Close local seqpack listening sockets ...");
|
||||
N = 0;
|
||||
M = 0;
|
||||
|
||||
for (auto& acc : _localSeqpackAcceptors) {
|
||||
acc.close(ec);
|
||||
if (ec) {
|
||||
_serverLogger->error("Cannot close local seqpack socket! ec = '{}'", ec.message());
|
||||
++M;
|
||||
}
|
||||
++N;
|
||||
}
|
||||
|
||||
_serverLogger->debug("{} from {} local seqpack sockets were closed!", M, N);
|
||||
|
||||
_localSeqpackAcceptors.clear();
|
||||
}
|
||||
|
||||
_serverLogger->info("The all server listening endpoints were closed!");
|
||||
}
|
||||
|
||||
void disconnectClients() {}
|
||||
|
||||
private:
|
||||
asio::io_context& _asioContext;
|
||||
std::shared_ptr<spdlog::logger> _serverLogger;
|
||||
|
||||
std::vector<asio::serial_port> _serialPorts;
|
||||
std::vector<asio::ip::tcp::acceptor> _tcpAcceptors;
|
||||
std::vector<asio::local::stream_protocol::acceptor> _localStreamAcceptors;
|
||||
std::vector<asio::local::seq_packet_protocol::acceptor> _localSeqpackAcceptors;
|
||||
std::vector<asio::serial_port*> _serialPorts;
|
||||
|
||||
std::vector<asio::ip::tcp::acceptor*> _tcpAcceptors;
|
||||
std::vector<asio::local::stream_protocol::acceptor*> _localStreamAcceptors;
|
||||
std::vector<asio::local::seq_packet_protocol::acceptor*> _localSeqpackAcceptors;
|
||||
|
||||
std::vector<asio::ip::tcp::socket*> _tcpSockets;
|
||||
std::vector<asio::local::stream_protocol::socket*> _localStreamSockets;
|
||||
std::vector<asio::local::seq_packet_protocol::socket*> _localSeqpackSockets;
|
||||
|
||||
asio::awaitable<void> startSession() {}
|
||||
|
||||
// helpers
|
||||
template <typename OptT, typename... OptTs>
|
||||
@ -315,6 +307,66 @@ private:
|
||||
setSerialOpts(s_port, std::forward<OptTs>(opts)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <traits::mcc_time_duration_c RCVT, traits::mcc_time_duration_c SNDT>
|
||||
asio::awaitable<void> startSession(auto socket,
|
||||
RCVT&& rcv_timeout = DEFAULT_RCV_TIMEOUT,
|
||||
SNDT&& snd_timeout = DEFAULT_SND_TIMEOUT)
|
||||
{
|
||||
using sock_t = std::decay_t<decltype(socket)>;
|
||||
|
||||
auto watchdog = []() -> asio::awaitable<void> {};
|
||||
|
||||
asio::streambuf sbuff;
|
||||
size_t nbytes;
|
||||
std::stringstream st;
|
||||
|
||||
st << socket.remote_endpoint();
|
||||
|
||||
try {
|
||||
if constexpr (traits::is_serial_proto<sock_t>) {
|
||||
_serialPorts.emplace_back(&socket);
|
||||
|
||||
|
||||
} else if constexpr (traits::is_tcp_proto<sock_t>) {
|
||||
_tcpSockets.emplace_back(&socket);
|
||||
} else if constexpr (traits::is_local_stream_proto<sock_t>) {
|
||||
_localStreamSockets.emplace_back(&socket);
|
||||
} else if constexpr (traits::is_local_seqpack_proto<sock_t>) {
|
||||
_localSeqpackSockets.emplace_back(&socket);
|
||||
} else {
|
||||
static_assert(false, "INVALID SOCKET TTYPE!!!");
|
||||
}
|
||||
|
||||
|
||||
for (;;) {
|
||||
if constexpr (traits::is_serial_proto<sock_t>) {
|
||||
nbytes = 1024;
|
||||
} else {
|
||||
nbytes = socket.available();
|
||||
}
|
||||
|
||||
auto buff = sbuff.prepare(nbytes ? nbytes : 1);
|
||||
|
||||
if constexpr (traits::is_local_seqpack_proto<sock_t>) {
|
||||
asio::socket_base::message_flags oflags;
|
||||
nbytes = co_await socket.async_receive(buff, &oflags, asio::use_awaitable);
|
||||
|
||||
if (!nbytes) { // EOF!
|
||||
_serverLogger->info("It seems client ({}) closed the connection!", st.str());
|
||||
co_return;
|
||||
}
|
||||
} else {
|
||||
nbytes = co_await asio::async_read(socket, buff, asio::transfer_at_least(1), asio::use_awaitable);
|
||||
}
|
||||
|
||||
sbuff.commit(nbytes);
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mcc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user