This commit is contained in:
awe
2026-04-09 13:52:10 +03:00
parent 9211fc4276
commit e4b8824960

202
main.cpp
View File

@ -19,6 +19,8 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdlib>
@ -34,6 +36,11 @@
#include <string>
#include <vector>
#ifndef _WIN32
#include <csignal>
#include <dlfcn.h>
#endif
namespace {
enum class StopMode {
@ -662,18 +669,87 @@ Config parse_args(int argc, char** argv) {
return cfg;
}
#ifdef _WIN32
using ModuleHandle = HMODULE;
#else
using ModuleHandle = void*;
#endif
std::string dynamic_loader_error() {
#ifdef _WIN32
return "unknown error";
#else
const char* error = dlerror();
return ((error != nullptr) && (*error != '\0')) ? std::string(error) : std::string("unknown error");
#endif
}
ModuleHandle open_library(const char* path) {
#ifdef _WIN32
return LoadLibraryA(path);
#else
dlerror();
return dlopen(path, RTLD_LAZY | RTLD_LOCAL);
#endif
}
void close_library(ModuleHandle module) {
#ifdef _WIN32
if (module != nullptr) {
FreeLibrary(module);
}
#else
if (module != nullptr) {
dlclose(module);
}
#endif
}
ModuleHandle load_library_or_fail(const std::vector<std::string>& candidates,
const std::string& description) {
std::string last_error = "no candidates provided";
for (const auto& candidate : candidates) {
ModuleHandle module = open_library(candidate.c_str());
if (module != nullptr) {
return module;
}
last_error = dynamic_loader_error();
}
std::ostringstream out;
out << "Cannot load " << description << ". Tried:";
for (const auto& candidate : candidates) {
out << " " << candidate;
}
out << ". Last error: " << last_error;
fail(out.str());
}
template <typename Fn>
Fn load_symbol(HMODULE module, const char* name) {
Fn load_symbol(ModuleHandle module, const char* name) {
#ifdef _WIN32
const auto addr = GetProcAddress(module, name);
if (addr == nullptr) {
fail(std::string("GetProcAddress failed for symbol: ") + name);
}
return reinterpret_cast<Fn>(addr);
#else
dlerror();
void* addr = dlsym(module, name);
const char* error = dlerror();
if ((addr == nullptr) || (error != nullptr)) {
std::ostringstream out;
out << "dlsym failed for symbol " << name << ": "
<< ((error != nullptr) ? error : "unknown error");
fail(out.str());
}
return reinterpret_cast<Fn>(addr);
#endif
}
struct Api {
HMODULE x502_module = nullptr;
HMODULE e502_module = nullptr;
ModuleHandle x502_module = nullptr;
ModuleHandle e502_module = nullptr;
decltype(&X502_Create) Create = nullptr;
decltype(&X502_Free) Free = nullptr;
@ -709,14 +785,20 @@ struct Api {
decltype(&E502_OpenByIpAddr) OpenByIpAddr = nullptr;
Api() {
x502_module = LoadLibraryA("x502api.dll");
if (x502_module == nullptr) {
fail("Cannot load x502api.dll");
}
e502_module = LoadLibraryA("e502api.dll");
if (e502_module == nullptr) {
fail("Cannot load e502api.dll");
}
x502_module = load_library_or_fail(
#ifdef _WIN32
{"x502api.dll"},
#else
{"libx502api.so", "x502api.so", "./libx502api.so", "./x502api.so"},
#endif
"x502 API library");
e502_module = load_library_or_fail(
#ifdef _WIN32
{"e502api.dll"},
#else
{"libe502api.so", "e502api.so", "./libe502api.so", "./e502api.so"},
#endif
"e502 API library");
Create = load_symbol<decltype(Create)>(x502_module, "X502_Create");
Free = load_symbol<decltype(Free)>(x502_module, "X502_Free");
@ -753,12 +835,8 @@ struct Api {
}
~Api() {
if (e502_module != nullptr) {
FreeLibrary(e502_module);
}
if (x502_module != nullptr) {
FreeLibrary(x502_module);
}
close_library(e502_module);
close_library(x502_module);
}
};
@ -782,6 +860,19 @@ constexpr uint32_t kE502DiSyn2Mask =
(static_cast<uint32_t>(1U) << 13U) | (static_cast<uint32_t>(1U) << 17U);
constexpr uint32_t kE502Digital1Mask = (static_cast<uint32_t>(1U) << 0U);
using TickMs = uint64_t;
TickMs tick_count_ms() {
#ifdef _WIN32
return static_cast<TickMs>(GetTickCount64());
#else
using namespace std::chrono;
return static_cast<TickMs>(
duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count());
#endif
}
#ifdef _WIN32
volatile LONG g_console_stop_requested = 0;
BOOL WINAPI console_ctrl_handler(DWORD ctrl_type) {
@ -795,6 +886,17 @@ BOOL WINAPI console_ctrl_handler(DWORD ctrl_type) {
bool console_stop_requested() {
return InterlockedCompareExchange(&g_console_stop_requested, 0, 0) != 0;
}
#else
volatile std::sig_atomic_t g_console_stop_requested = 0;
void console_ctrl_handler(int) {
g_console_stop_requested = 1;
}
bool console_stop_requested() {
return g_console_stop_requested != 0;
}
#endif
enum class PacketCloseReason {
ExternalStopEdge,
@ -913,16 +1015,52 @@ int16_t pack_raw_code_to_int16(double avg_raw_code) {
struct ConsoleCtrlGuard {
bool installed = false;
#ifndef _WIN32
bool sigint_installed = false;
bool sigterm_installed = false;
bool sigabrt_installed = false;
struct sigaction old_sigint {};
struct sigaction old_sigterm {};
struct sigaction old_sigabrt {};
#endif
ConsoleCtrlGuard() {
#ifdef _WIN32
InterlockedExchange(&g_console_stop_requested, 0);
installed = SetConsoleCtrlHandler(console_ctrl_handler, TRUE) != 0;
#else
g_console_stop_requested = 0;
struct sigaction action {};
action.sa_handler = console_ctrl_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigint_installed = sigaction(SIGINT, &action, &old_sigint) == 0;
sigterm_installed = sigaction(SIGTERM, &action, &old_sigterm) == 0;
sigabrt_installed = sigaction(SIGABRT, &action, &old_sigabrt) == 0;
installed = sigint_installed && sigterm_installed && sigabrt_installed;
#endif
}
~ConsoleCtrlGuard() {
#ifdef _WIN32
if (installed) {
SetConsoleCtrlHandler(console_ctrl_handler, FALSE);
}
InterlockedExchange(&g_console_stop_requested, 0);
#else
if (sigint_installed) {
sigaction(SIGINT, &old_sigint, nullptr);
}
if (sigterm_installed) {
sigaction(SIGTERM, &old_sigterm, nullptr);
}
if (sigabrt_installed) {
sigaction(SIGABRT, &old_sigabrt, nullptr);
}
g_console_stop_requested = 0;
#endif
}
};
@ -1140,11 +1278,11 @@ int run(const Config& cfg) {
uint32_t next_lch = 0;
bool di1_initialized = false;
bool di1_prev_level = false;
const ULONGLONG start_wait_deadline = GetTickCount64() + cfg.start_wait_ms;
const ULONGLONG capture_loop_start = GetTickCount64();
ULONGLONG stats_window_start = capture_loop_start;
ULONGLONG last_stats_print = capture_loop_start;
ULONGLONG last_live_update = 0;
const TickMs start_wait_deadline = tick_count_ms() + cfg.start_wait_ms;
const TickMs capture_loop_start = tick_count_ms();
TickMs stats_window_start = capture_loop_start;
TickMs last_stats_print = capture_loop_start;
TickMs last_live_update = 0;
uint64_t total_raw_words = 0;
uint64_t total_adc_samples = 0;
@ -1162,7 +1300,7 @@ int run(const Config& cfg) {
uint64_t stats_completed_packets = 0;
auto print_stats = [&](bool final_report) {
const ULONGLONG now = GetTickCount64();
const TickMs now = tick_count_ms();
const double elapsed_s = std::max(1e-9, static_cast<double>(now - stats_window_start) / 1000.0);
const double mb_per_s = (static_cast<double>(stats_raw_words) * sizeof(uint32_t)) / elapsed_s / 1000.0 / 1000.0;
const double adc_samples_per_s = static_cast<double>(stats_adc_samples) / elapsed_s;
@ -1281,12 +1419,12 @@ int run(const Config& cfg) {
}
const double elapsed_capture_s =
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0);
std::max(1e-9, static_cast<double>(tick_count_ms() - capture_loop_start) / 1000.0);
const double packets_per_second =
(elapsed_capture_s >= 0.1)
? (static_cast<double>(total_completed_packets) / elapsed_capture_s)
: 0.0;
const ULONGLONG now = GetTickCount64();
const TickMs now = tick_count_ms();
const bool should_update_live =
(cfg.live_update_period_ms == 0U) ||
(last_live_update == 0U) ||
@ -1345,7 +1483,7 @@ int run(const Config& cfg) {
stats_raw_words += static_cast<uint64_t>(recvd);
}
if (recvd == 0) {
if (!capture_started && (GetTickCount64() >= start_wait_deadline)) {
if (!capture_started && (tick_count_ms() >= start_wait_deadline)) {
std::ostringstream message;
message << "Timeout before first ADC data. start="
<< sync_mode_to_string(cfg.sync_start_mode, true)
@ -1558,7 +1696,7 @@ int run(const Config& cfg) {
stop_loop_requested = true;
}
if ((cfg.stats_period_ms != 0U) && ((GetTickCount64() - last_stats_print) >= cfg.stats_period_ms)) {
if ((cfg.stats_period_ms != 0U) && ((tick_count_ms() - last_stats_print) >= cfg.stats_period_ms)) {
print_stats(false);
}
}
@ -1592,20 +1730,20 @@ int run(const Config& cfg) {
std::cout << "Average stats: "
<< "MB/s=" << std::fixed << std::setprecision(3)
<< ((static_cast<double>(total_raw_words) * sizeof(uint32_t)) /
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0) /
std::max(1e-9, static_cast<double>(tick_count_ms() - capture_loop_start) / 1000.0) /
1000.0 / 1000.0)
<< ", ADC samples/s="
<< (static_cast<double>(total_adc_samples) /
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0))
std::max(1e-9, static_cast<double>(tick_count_ms() - capture_loop_start) / 1000.0))
<< ", DIN samples/s="
<< (static_cast<double>(total_din_samples) /
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0))
std::max(1e-9, static_cast<double>(tick_count_ms() - capture_loop_start) / 1000.0))
<< ", frames/s per channel="
<< (static_cast<double>(total_completed_frames) /
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0))
std::max(1e-9, static_cast<double>(tick_count_ms() - capture_loop_start) / 1000.0))
<< ", packets/s="
<< (static_cast<double>(total_completed_packets) /
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0))
std::max(1e-9, static_cast<double>(tick_count_ms() - capture_loop_start) / 1000.0))
<< ", packets captured=" << total_completed_packets;
if (cfg.di1_mode == Di1Mode::ZeroOnChange) {
std::cout << ", zeroed on DI1 change="