IRSOL
C++ code implementing socket server for interacting with Baumer camera.
app.cpp
Go to the documentation of this file.
2
3#include "irsol/logging.hpp"
6#include "irsol/utils.hpp"
7
8#include <sstream>
9
10namespace irsol {
11namespace server {
12
14 : m_port(port)
15 , m_acceptor(
16 m_port,
17 std::bind(&App::addClient, this, std::placeholders::_1, std::placeholders::_2))
18 , m_cameraInterface(std::make_unique<camera::Interface>(camera::Interface::HalfResolution()))
19 , m_frameCollector(std::make_unique<frame_collector::FrameCollector>(*m_cameraInterface.get()))
20 , m_messageHandler(std::make_unique<handlers::MessageHandler>())
21{
23}
24
25bool
27{
28 if(!m_acceptor.isOpen()) {
29 IRSOL_LOG_ERROR("Failed to open acceptor on port {}: {}", m_port, m_acceptor.error());
30 return false;
31 }
32 IRSOL_LOG_DEBUG("Starting accept thread");
34 IRSOL_LOG_INFO("Server started successfully");
35 return true;
36}
37
38void
40{
41 IRSOL_LOG_INFO("Stopping server");
43
44 if(m_acceptThread.joinable()) {
45 IRSOL_LOG_DEBUG("Joining accept thread");
46 m_acceptThread.join();
47 }
48 m_frameCollector->stop();
49 IRSOL_LOG_INFO("Server stopped");
50}
51
52std::shared_ptr<ClientSession>
54{
55 std::scoped_lock<std::mutex> lock(m_clientsMutex);
56 auto it = m_clients.find(clientId);
57 return it != m_clients.end() ? it->second : nullptr;
58}
59
60void
62 protocol::OutMessage&& message,
63 const std::optional<irsol::types::client_id_t>& excludeClient)
64{
65 // Serialize the message only once, and distribute to all clients
66 auto serializedMessage = irsol::protocol::Serializer::serialize(std::move(message));
67 IRSOL_LOG_DEBUG("Broadcasting serialized message {}", serializedMessage.toString());
68 std::scoped_lock<std::mutex> lock(m_clientsMutex);
69
70 for(const auto& [clientId, session] : m_clients) {
71 if(excludeClient && clientId == *excludeClient) {
72 continue; // Skip the sender if specified
73 }
74 try {
75 std::scoped_lock<std::mutex> sessionLock(session->socketMutex());
76 session->handleSerializedMessage(serializedMessage);
77 } catch(const std::exception& ex) {
78 IRSOL_LOG_WARN("Failed to send broadcast to client {}: {}", clientId, ex.what());
79 }
80 }
81 IRSOL_LOG_DEBUG("Broadcasting complete.");
82}
83
84void
86{
87 auto session = std::make_shared<ClientSession>(clientId, std::move(sock), *this);
88 std::scoped_lock<std::mutex> lock(m_clientsMutex);
90 "Registering new client connection from {} with id {}",
91 session->socket().address().to_string(),
92 clientId);
93 {
94 m_clients.insert({clientId, session});
96 "Client {} added to session list, total clients: {}", clientId, m_clients.size());
97 }
98
99 IRSOL_LOG_INFO("Starting client session thread for client with id {}", clientId);
100 std::thread([session, this]() {
101 IRSOL_LOG_DEBUG("Thread for client with ID {} started", session->id());
102
103 try {
104 session->run();
105 } catch(std::exception& e) {
107 "Error in client session thread for client with id {}: {}", session->id(), e.what());
108 }
109 IRSOL_LOG_DEBUG("Thread for client with ID {} finished", session->id());
110 removeClient(session->id());
111 })
112 .detach();
113}
114
115void
117{
118 std::scoped_lock<std::mutex> lock(m_clientsMutex);
119 auto clientIt = m_clients.find(clientId);
120 if(clientIt == m_clients.end()) {
121 IRSOL_LOG_ERROR("Client '{}' not found in session list", clientId);
122 } else {
123 auto client = clientIt->second;
124 m_frameCollector->deregisterClient(client->id());
125 m_clients.erase(clientIt);
127 "Client {} removed from session list, remaining clients: {}", clientId, m_clients.size());
128 }
129}
130
131void
133{
134 // Build a context to pass to all handlers
135 auto ctx = std::make_shared<irsol::server::handlers::Context>(*this);
136
137 // Register message handlers for specific message types
138 registerMessageHandler<protocol::Inquiry, handlers::InquiryFrameRateHandler>("fr", ctx);
139 registerMessageHandler<protocol::Assignment, handlers::AssignmentFrameRateHandler>("fr", ctx);
140 registerMessageHandler<protocol::Assignment, handlers::AssignmentInputSequenceLengthHandler>(
141 "isl", ctx);
142 registerMessageHandler<protocol::Assignment, handlers::AssignmentIntegrationTimeHandler>(
143 "it", ctx);
144 registerMessageHandler<protocol::Inquiry, handlers::InquiryIntegrationTimeHandler>("it", ctx);
145 registerMessageHandler<protocol::Inquiry, handlers::InquiryInputSequenceLengthHandler>(
146 "isl", ctx);
147 registerMessageHandler<protocol::Command, handlers::CommandAbortHandler>("abort", ctx);
148 registerMessageHandler<protocol::Command, handlers::CommandGIHandler>("gi", ctx);
149 registerMessageHandler<protocol::Command, handlers::CommandGISHandler>("gis", ctx);
150 registerMessageHandler<protocol::Inquiry, handlers::InquiryImgLeftHandler>("img_l", ctx);
151 registerMessageHandler<protocol::Inquiry, handlers::InquiryImgTopHandler>("img_t", ctx);
152 registerMessageHandler<protocol::Inquiry, handlers::InquiryImgWidthHandler>("img_w", ctx);
153 registerMessageHandler<protocol::Inquiry, handlers::InquiryImgHeightHandler>("img_h", ctx);
154 registerMessageHandler<protocol::Assignment, handlers::AssignmentImgLeftHandler>("img_l", ctx);
155 registerMessageHandler<protocol::Assignment, handlers::AssignmentImgTopHandler>("img_t", ctx);
156 registerMessageHandler<protocol::Assignment, handlers::AssignmentImgWidthHandler>("img_w", ctx);
157 registerMessageHandler<protocol::Assignment, handlers::AssignmentImgHeightHandler>("img_h", ctx);
158
159 registerLambdaHandler<protocol::Command>(
160 "image_data",
161 ctx,
162 [](
163 std::shared_ptr<handlers::Context> ctx,
164 std::shared_ptr<irsol::server::ClientSession> client,
165 protocol::Command&& cmd) -> std::vector<protocol::OutMessage> {
166 std::vector<protocol::OutMessage> result;
167 auto& cam = ctx->app.camera();
168 auto img = cam.captureImage(std::chrono::milliseconds(10000));
169
170 if(img.IsEmpty()) {
171 IRSOL_NAMED_LOG_ERROR(client->id(), "Failed to capture image.");
172 result.emplace_back(irsol::protocol::Error::from(cmd, "Failed to capture image"));
173 return result;
174 }
175
176 uint32_t width = static_cast<uint32_t>(img.GetWidth());
177 uint32_t height = static_cast<uint32_t>(img.GetHeight());
178 size_t dataSize = img.GetSize();
179
180 const void* imageBuffer = img.GetImageData();
181 std::vector<irsol::types::byte_t> rawData(dataSize);
182 memcpy(rawData.data(), imageBuffer, dataSize);
183
184 result.emplace_back(
185 irsol::protocol::ImageBinaryData(std::move(rawData), {height, width}, {}));
186 return result;
187 });
188}
189
190} // namespace server
191} // namespace irsol
Main server application managing client connections and camera streaming.
static internal::SerializedMessage serialize(OutMessage &&msg)
Serialize an irsol::protocol::OutMessage variant into a serialized protocol message.
Main server application that manages client connections and camera streaming.
Definition app.hpp:46
const irsol::types::port_t m_port
TCP port on which the server listens.
Definition app.hpp:121
irsol::server::internal::ClientSessionAcceptor m_acceptor
Acceptor that handles incoming client connections.
Definition app.hpp:124
std::thread m_acceptThread
Thread running the connection acceptor loop.
Definition app.hpp:127
void addClient(const irsol::types::client_id_t &clientId, irsol::types::socket_t &&sock)
Adds a new client session.
Definition app.cpp:85
std::unique_ptr< frame_collector::FrameCollector > m_frameCollector
Frame collector for capturing and broadcasting camera frames.
Definition app.hpp:139
std::shared_ptr< ClientSession > getClientSession(const irsol::types::client_id_t &clientId)
Retrieves an active client session.
Definition app.cpp:53
void broadcastMessage(protocol::OutMessage &&message, const std::optional< irsol::types::client_id_t > &excludeClient=std::nullopt)
Broadcasts a message to all connected clients.
Definition app.cpp:61
void stop()
Stops the server.
Definition app.cpp:39
bool start()
Starts the server.
Definition app.cpp:26
void registerMessageHandlers()
Registers standard message handlers.
Definition app.cpp:132
client_map_t m_clients
Map of connected clients.
Definition app.hpp:133
App(irsol::types::port_t port)
Constructs the App.
Definition app.cpp:13
void removeClient(const irsol::types::client_id_t &clientId)
Removes a client session.
Definition app.cpp:116
std::mutex m_clientsMutex
Mutex for protecting access to m_clients.
Definition app.hpp:130
bool isOpen() const
Checks if the acceptor is actively listening.
Definition acceptor.cpp:27
std::string error() const
Gets the current error message, if any.
Definition acceptor.cpp:20
void run()
Starts the accept loop.
Definition acceptor.cpp:44
#define IRSOL_LOG_INFO(...)
Logs an info-level message using the default logger.
Definition logging.hpp:92
#define IRSOL_LOG_ERROR(...)
Logs an error-level message using the default logger.
Definition logging.hpp:94
#define IRSOL_LOG_WARN(...)
Logs a warning-level message using the default logger.
Definition logging.hpp:93
#define IRSOL_LOG_DEBUG(...)
Logs a debug-level message using the default logger.
Definition logging.hpp:91
std::variant< Success, BinaryDataBuffer, ImageBinaryData, ColorImageBinaryData, Error > OutMessage
Variant type representing any outgoing message.
Definition variants.hpp:149
Logging utilities and configuration for the irsol library.
std::string client_id_t
Represents a unique client identifier. Typically used to identify connected clients by string IDs.
Definition types.hpp:55
uint16_t port_t
Represents a network port number. Typically used to specify TCP or UDP ports.
Definition types.hpp:48
sockpp::tcp_socket socket_t
Alias for a TCP socket.
Definition types.hpp:87
Represents a command invocation in the protocol.
Definition command.hpp:33
Represents a binary data object within the protocol.
Definition binary.hpp:107
auto result
General utility functions used throughout the irsol library.