25#include <opencv2/opencv.hpp>
36 if(signal == SIGTERM || signal == SIGINT) {
37 IRSOL_LOG_INFO(
"Signal {} received, shutting down gracefully...", signal);
47 ss <<
"fr=" << std::to_string(fps) <<
"\n";
54 auto res = conn.read(&ch, 1);
55 if(res.value() <= 0) {
67 ss <<
"isl=" << std::to_string(isl) <<
"\n";
74 auto res = conn.read(&ch, 1);
75 if(res.value() <= 0) {
83 std::string expectedIslResponse =
"isl=" + std::to_string(isl);
84 if(response != expectedIslResponse) {
86 "Expected response for ISL command is '{}', but got '{}'", expectedIslResponse, response);
94std::optional<std::vector<std::pair<size_t, cv::Mat>>>
97 auto t0 = irsol::types::clock_t::now();
98 std::vector<irsol::types::timepoint_t> retrieveTimes;
102 std::vector<std::pair<size_t, cv::Mat>>
result;
105 std::string headerTitle;
108 auto res = conn.read(&ch, 1);
109 if(res.value() <= 0) {
113 if(ch ==
'\n' || ch ==
'=')
118 if(headerTitle ==
"gis;") {
121 }
else if(headerTitle ==
"isn") {
125 isnStr.insert(isnStr.end(), ch);
127 uint64_t isn = std::stoull(isnStr);
129 }
else if(headerTitle ==
"img") {
133 uint32_t imageHeight{0};
134 uint32_t imageWidth{0};
138 auto res = conn.read(&ch, 1);
139 if(res.value() <= 0) {
152 std::string heightStr;
155 heightStr.insert(heightStr.end(), ch);
157 imageHeight = std::stol(heightStr);
159 std::string widthStr;
162 widthStr.insert(widthStr.end(), ch);
164 imageWidth = std::stol(widthStr);
173 uint64_t expectedSize = imageHeight * imageWidth * 2;
174 std::vector<uint8_t> buffer(expectedSize);
176 size_t totalRead = 0;
177 while(totalRead < expectedSize) {
178 auto res = conn.read(buffer.data() + totalRead, expectedSize - totalRead);
179 if(res.value() <= 0) {
180 IRSOL_LOG_ERROR(
"Failed to read image data (read {} of {})", totalRead, expectedSize);
183 totalRead += res.value();
186 retrieveTimes.push_back(irsol::types::clock_t::now());
194 auto res = conn.read(&ch, 1);
201 auto t1 = irsol::types::clock_t::now();
202 auto totalDuration = t1 - t0;
204 "Took {} to retrieve {} frames -> fps {}",
207 1000000.0 *
result.size() /
208 std::chrono::duration_cast<std::chrono::microseconds>(totalDuration).count());
209 for(
size_t i = 0; i < retrieveTimes.size() - 1; ++i) {
210 auto start = retrieveTimes[i];
211 auto end = retrieveTimes[i + 1];
212 auto dt = end - start;
214 "Dts: {} -> fps: {}",
216 1000000.0 / std::chrono::duration_cast<std::chrono::microseconds>(dt).count());
221std::optional<irsol::types::connector_t>
223 const std::string& host,
225 std::chrono::seconds retryTimeout = std::chrono::seconds(1))
232 "Failed to connect to server at {}:{}: {}, retrying in {} seconds",
236 retryTimeout.count());
237 std::this_thread::sleep_for(retryTimeout);
240 return std::move(conn);
247run(
double inFps, uint64_t sequenceLength)
252 std::string server_host =
"localhost";
255 sockpp::initialize();
261 conn = std::move(connOpt.value());
267 if(
auto res = conn.read_timeout(std::chrono::seconds(10)); !res) {
268 IRSOL_LOG_ERROR(
"Error setting TCP read timeout: {}", res.error_message());
277 bool firstFrame =
true;
287 if(!imagesOpt.has_value()) {
289 std::this_thread::sleep_for(std::chrono::seconds(1));
293 auto images = std::move(imagesOpt.value());
294 size_t imageIndex = 0;
295 for(
const auto& [imageId, image] : images) {
299 std::this_thread::sleep_for(std::chrono::seconds(1));
306 "Image {} received: {}x{} with {} channels",
316 "ImageID: " + std::to_string(imageId),
318 cv::FONT_HERSHEY_COMPLEX,
328 "ImageIndex: " + std::to_string(imageIndex),
330 cv::FONT_HERSHEY_COMPLEX,
339 cv::imshow(
"Viewer", image);
340 int key = cv::waitKey(300) & 0xFF;
341 if(key == 27 || key ==
'q') {
350 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
366 args::ArgumentParser parser(
"TCP client viewer");
367 args::HelpFlag help(parser,
"help",
"Display this help menu", {
'h',
"help"});
368 args::ValueFlag<double> listen_fps_flag(parser,
"fps",
"The FPS at which to listen.", {
'f'});
369 args::ValueFlag<uint64_t> isl_flag(
370 parser,
"isl",
"The input sequence length at which to listen.", {
'i'});
372 parser.ParseCLI(argc, argv);
373 }
catch(args::Help) {
376 }
catch(args::ParseError e) {
377 IRSOL_LOG_ERROR(
"Error parsing command-line arguments: {}\n", e.what(), parser.Help());
379 }
catch(args::ValidationError e) {
380 IRSOL_LOG_ERROR(
"Error parsing command-line arguments: {}\n", e.what(), parser.Help());
383 double listen_fps = 0.5;
384 if(listen_fps_flag) {
385 listen_fps = args::get(listen_fps_flag);
389 isl = args::get(isl_flag);
392 run(listen_fps, isl);
void initAssertHandler()
Initializes the assertion handler system.
#define IRSOL_LOG_INFO(...)
Logs an info-level message using the default logger.
#define IRSOL_LOG_ERROR(...)
Logs an error-level message using the default logger.
#define IRSOL_LOG_WARN(...)
Logs a warning-level message using the default logger.
#define IRSOL_LOG_DEBUG(...)
Logs a debug-level message using the default logger.
void initLogging(const char *fileSinkFilename="logs/irsol.log", std::optional< spdlog::level::level_enum > minLogLevel=std::nullopt)
Initializes the irsol logging system.
cv::Mat createCvMatFromIrsolServerBuffer(unsigned char *data, size_t rows, size_t cols)
Creates an OpenCV cv::Mat from a raw buffer received from the IRSOL server.
uint16_t port_t
Represents a network port number. Typically used to specify TCP or UDP ports.
sockpp::tcp_connector connector_t
Alias for the TCP client connector type.
std::string bytesToString(const std::vector< irsol::types::byte_t > &input)
Converts a std::vector of irsol::types::byte_t to a std::string.
std::string durationToString(irsol::types::duration_t dr)
Converts a duration to a human-readable string.
static constexpr uint64_t max()
Returns the maximum representable pixel value for this bit depth.
static constexpr irsol::types::byte_t SOH
Start of Header (SOH) byte: 0x01.
static constexpr irsol::types::byte_t STX
Start of Text (STX) byte: 0x02.
std::optional< irsol::types::connector_t > createConnectionWithRetry(const std::string &host, irsol::types::port_t port, std::chrono::seconds retryTimeout=std::chrono::seconds(1))
bool configureGis(irsol::types::connector_t &conn, double fps, uint64_t isl)
void run(double inFps, uint64_t sequenceLength)
std::optional< std::vector< std::pair< size_t, cv::Mat > > > queryImages(irsol::types::connector_t &conn)
std::atomic< bool > g_terminate
void signalHandler(int signal)