Refactor radar system
This commit is contained in:
@@ -27,9 +27,7 @@ public:
|
||||
|
||||
void initialize_live_plot() const;
|
||||
void initialize_csv(std::size_t channel_count, bool has_di1_trace) const;
|
||||
void append_csv_packet(const CapturePacket& packet,
|
||||
double frame_freq_hz,
|
||||
std::size_t& global_frame_index) const;
|
||||
void append_csv_packet(const CapturePacket& packet) const;
|
||||
|
||||
void update_live_plot(const CapturePacket& packet,
|
||||
std::size_t packets_seen,
|
||||
|
||||
@@ -234,12 +234,20 @@ std::string csv_spool_path(const std::string& csv_path) {
|
||||
return replace_extension(csv_path, ".capture_spool.bin");
|
||||
}
|
||||
|
||||
void finish_file(std::ofstream& stream, const std::string& path) {
|
||||
stream.close();
|
||||
if (!stream) {
|
||||
fail_write("Cannot finish writing file: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
void write_text_file(const std::string& path, const std::string& text) {
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
if (!file) {
|
||||
fail_write("Cannot open file for writing: " + path);
|
||||
}
|
||||
file << text;
|
||||
finish_file(file, path);
|
||||
}
|
||||
|
||||
void write_live_data_script(const std::string& path, const std::string& data_json) {
|
||||
@@ -584,6 +592,7 @@ void write_live_html_document(const std::string& path, const std::string& data_s
|
||||
<< " </script>\n"
|
||||
<< "</body>\n"
|
||||
<< "</html>\n";
|
||||
finish_file(html, path);
|
||||
}
|
||||
|
||||
[[noreturn]] void fail_write(const std::string& message) {
|
||||
@@ -629,11 +638,14 @@ void CaptureFileWriter::initialize_live_plot() const {
|
||||
}
|
||||
|
||||
void CaptureFileWriter::initialize_csv(std::size_t channel_count, bool has_di1_trace) const {
|
||||
if (channel_count != 1U && channel_count != 2U) {
|
||||
fail_write("Capture channel count must be 1 or 2");
|
||||
}
|
||||
std::ofstream csv_file(csv_path_, std::ios::binary | std::ios::trunc);
|
||||
if (!csv_file) {
|
||||
fail_write("Cannot prepare CSV output file: " + csv_path_);
|
||||
}
|
||||
csv_file << "";
|
||||
finish_file(csv_file, csv_path_);
|
||||
|
||||
const std::string spool_path = csv_spool_path(csv_path_);
|
||||
std::ofstream spool(spool_path, std::ios::binary | std::ios::trunc);
|
||||
@@ -647,16 +659,15 @@ void CaptureFileWriter::initialize_csv(std::size_t channel_count, bool has_di1_t
|
||||
static_cast<uint32_t>(has_di1_trace ? 1U : 0U)
|
||||
};
|
||||
spool.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||
if (!spool) {
|
||||
fail_write("Cannot initialize CSV spool: " + spool_path);
|
||||
}
|
||||
finish_file(spool, spool_path);
|
||||
}
|
||||
|
||||
void CaptureFileWriter::append_csv_packet(const CapturePacket& packet,
|
||||
double frame_freq_hz,
|
||||
std::size_t& global_frame_index) const {
|
||||
(void) frame_freq_hz;
|
||||
(void) global_frame_index;
|
||||
void CaptureFileWriter::append_csv_packet(const CapturePacket& packet) const {
|
||||
if ((packet.channel_count != 1U && packet.channel_count != 2U)
|
||||
|| (packet.channel_count == 2U && packet.ch1.size() != packet.ch2.size())
|
||||
|| (packet.has_di1_trace && packet.di1.size() != packet.ch1.size())) {
|
||||
fail_write("Capture packet channel lengths are inconsistent");
|
||||
}
|
||||
|
||||
const std::string spool_path = csv_spool_path(csv_path_);
|
||||
std::ofstream spool(spool_path, std::ios::binary | std::ios::app);
|
||||
@@ -689,9 +700,7 @@ void CaptureFileWriter::append_csv_packet(const CapturePacket& packet,
|
||||
static_cast<std::streamsize>(frames * sizeof(uint8_t)));
|
||||
}
|
||||
}
|
||||
if (!spool) {
|
||||
fail_write("Cannot append packet to CSV spool: " + spool_path);
|
||||
}
|
||||
finish_file(spool, spool_path);
|
||||
}
|
||||
|
||||
void CaptureFileWriter::update_live_plot(const CapturePacket& packet,
|
||||
@@ -763,7 +772,7 @@ void CaptureFileWriter::finalize_csv_from_spool(double frame_freq_hz) const {
|
||||
CsvSpoolPacketHeader packet_header{};
|
||||
spool.read(reinterpret_cast<char*>(&packet_header), sizeof(packet_header));
|
||||
if (!spool) {
|
||||
if (spool.eof()) {
|
||||
if (spool.eof() && spool.gcount() == 0) {
|
||||
break;
|
||||
}
|
||||
fail_write("Cannot read packet header from CSV spool: " + spool_path);
|
||||
@@ -773,6 +782,17 @@ void CaptureFileWriter::finalize_csv_from_spool(double frame_freq_hz) const {
|
||||
const std::size_t channel_count = static_cast<std::size_t>(packet_header.channel_count);
|
||||
const std::size_t frames = static_cast<std::size_t>(packet_header.frame_count);
|
||||
const bool has_di1_trace = packet_header.has_di1_trace != 0U;
|
||||
if (channel_count != 1U && channel_count != 2U) {
|
||||
fail_write("Invalid channel count in CSV spool: " + spool_path);
|
||||
}
|
||||
const auto data_position = spool.tellg();
|
||||
spool.seekg(0, std::ios::end);
|
||||
const auto remaining = spool.tellg() - data_position;
|
||||
spool.seekg(data_position);
|
||||
const auto bytes_per_frame = channel_count * sizeof(double) + (has_di1_trace ? 1U : 0U);
|
||||
if (!spool || remaining < 0 || packet_header.frame_count > static_cast<uint64_t>(remaining) / bytes_per_frame) {
|
||||
fail_write("Truncated or invalid packet data in CSV spool: " + spool_path);
|
||||
}
|
||||
|
||||
if (!header_written) {
|
||||
csv << "packet_index,frame_index,global_frame_index,time_s,packet_time_s,ch1_v";
|
||||
@@ -839,6 +859,13 @@ void CaptureFileWriter::finalize_csv_from_spool(double frame_freq_hz) const {
|
||||
}
|
||||
|
||||
csv.flush();
|
||||
if (!csv) {
|
||||
fail_write("Cannot flush CSV output; capture spool retained: " + csv_path_);
|
||||
}
|
||||
csv.close();
|
||||
if (!csv) {
|
||||
fail_write("Cannot close CSV output; capture spool retained: " + csv_path_);
|
||||
}
|
||||
std::remove(spool_path.c_str());
|
||||
}
|
||||
|
||||
@@ -1016,4 +1043,5 @@ void CaptureFileWriter::write_svg(const std::vector<CapturePacket>& packets,
|
||||
<< "\" font-size=\"14\" font-family=\"Segoe UI, Arial, sans-serif\" fill=\"#203040\">DI1</text>\n";
|
||||
}
|
||||
file << "</svg>\n";
|
||||
finish_file(file, svg_path_);
|
||||
}
|
||||
|
||||
@@ -156,7 +156,8 @@ uint32_t parse_u32(const std::string& text, const std::string& field_name) {
|
||||
const std::string clean = trim_copy(text);
|
||||
char* end = nullptr;
|
||||
const auto value = std::strtoull(clean.c_str(), &end, 0);
|
||||
if ((end == clean.c_str()) || (*end != '\0') || (value > std::numeric_limits<uint32_t>::max())) {
|
||||
if ((!clean.empty() && clean.front() == '-') || (end == clean.c_str())
|
||||
|| (*end != '\0') || (value > std::numeric_limits<uint32_t>::max())) {
|
||||
fail("Invalid integer for " + field_name + ": " + text);
|
||||
}
|
||||
return static_cast<uint32_t>(value);
|
||||
@@ -191,15 +192,6 @@ uint32_t parse_ipv4(const std::string& text) {
|
||||
return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
|
||||
}
|
||||
|
||||
std::string ipv4_to_string(uint32_t ip_addr) {
|
||||
std::ostringstream out;
|
||||
out << ((ip_addr >> 24) & 0xFF) << '.'
|
||||
<< ((ip_addr >> 16) & 0xFF) << '.'
|
||||
<< ((ip_addr >> 8) & 0xFF) << '.'
|
||||
<< (ip_addr & 0xFF);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
uint32_t parse_range(const std::string& text) {
|
||||
const double value = parse_double(text, "range");
|
||||
if (std::fabs(value - 10.0) < 1e-9) {
|
||||
@@ -2033,8 +2025,14 @@ int run(const Config& cfg) {
|
||||
}
|
||||
expect_ok(api, api.StreamsEnable(device.hnd, enabled_streams), "Enable data streams");
|
||||
|
||||
const double target_frame_count = (cfg.duration_ms / 1000.0) * actual_frame_freq_hz;
|
||||
if (!std::isfinite(target_frame_count) || target_frame_count < 0.0
|
||||
|| target_frame_count >= static_cast<double>(std::numeric_limits<long long>::max())
|
||||
|| target_frame_count > static_cast<double>(std::numeric_limits<std::size_t>::max())) {
|
||||
fail("Capture duration and frame clock produce an unsupported frame count");
|
||||
}
|
||||
const std::size_t target_frames = std::max<std::size_t>(
|
||||
1, static_cast<std::size_t>(std::llround((cfg.duration_ms / 1000.0) * actual_frame_freq_hz)));
|
||||
1, static_cast<std::size_t>(std::llround(target_frame_count)));
|
||||
|
||||
std::cout << "Capture settings:\n"
|
||||
<< " clock source: " << sync_mode_to_string(cfg.sync_mode, false) << "\n"
|
||||
@@ -2306,7 +2304,6 @@ int run(const Config& cfg) {
|
||||
if (!fast_tty_avg_stream_mode) {
|
||||
current_packet.reset(target_frames, cfg.channel_count);
|
||||
}
|
||||
std::size_t csv_global_frame_index = 0;
|
||||
std::size_t packet_avg_steps = 0;
|
||||
std::size_t fast_packet_frames = 0;
|
||||
uint64_t packet_clock_count = 0;
|
||||
@@ -2776,7 +2773,7 @@ int run(const Config& cfg) {
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
writer->append_csv_packet(packet, actual_frame_freq_hz, csv_global_frame_index);
|
||||
writer->append_csv_packet(packet);
|
||||
packets.push_back(std::move(packet));
|
||||
if ((cfg.svg_history_packets != 0U) && (packets.size() > cfg.svg_history_packets)) {
|
||||
packets.pop_front();
|
||||
|
||||
@@ -196,7 +196,7 @@ TtyProtocolWriter::TtyProtocolWriter(std::string path, std::size_t ring_capacity
|
||||
: path_(std::move(path)),
|
||||
impl_(std::make_unique<Impl>(ring_capacity_bytes)) {
|
||||
if (is_character_device_path(path_)) {
|
||||
impl_->fd = ::open(path_.c_str(), O_WRONLY | O_NOCTTY);
|
||||
impl_->fd = ::open(path_.c_str(), O_WRONLY | O_NOCTTY | O_NONBLOCK);
|
||||
if (impl_->fd < 0) {
|
||||
throw std::runtime_error(io_error("Cannot open tty output", path_));
|
||||
}
|
||||
@@ -233,7 +233,20 @@ TtyProtocolWriter::TtyProtocolWriter(std::string path, std::size_t ring_capacity
|
||||
}
|
||||
}
|
||||
|
||||
impl_->worker = std::thread([this]() { worker_loop(); });
|
||||
try {
|
||||
const auto flags = ::fcntl(impl_->fd, F_GETFL);
|
||||
if (flags < 0 || ::fcntl(impl_->fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
throw std::runtime_error(io_error("Cannot make tty output nonblocking", path_));
|
||||
}
|
||||
impl_->worker = std::thread([this]() { worker_loop(); });
|
||||
} catch (...) {
|
||||
if (impl_->owns_link) {
|
||||
::unlink(path_.c_str());
|
||||
}
|
||||
close_fd_if_open(impl_->slave_fd);
|
||||
close_fd_if_open(impl_->fd);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
TtyProtocolWriter::~TtyProtocolWriter() {
|
||||
@@ -363,12 +376,12 @@ void TtyProtocolWriter::shutdown() {
|
||||
impl_->stop_requested = true;
|
||||
}
|
||||
|
||||
close_fd_if_open(impl_->fd);
|
||||
impl_->data_ready_cv.notify_all();
|
||||
|
||||
if (impl_->worker.joinable()) {
|
||||
impl_->worker.join();
|
||||
}
|
||||
close_fd_if_open(impl_->fd);
|
||||
}
|
||||
|
||||
void TtyProtocolWriter::enqueue_frame(uint16_t word0, uint16_t word1, uint16_t word2, uint16_t word3) {
|
||||
@@ -402,6 +415,12 @@ void TtyProtocolWriter::worker_loop() {
|
||||
const std::uint8_t* bytes = write_batch.data();
|
||||
std::size_t remaining = batch_count * kFrameByteCount;
|
||||
while (remaining != 0U) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
if (impl_->stop_requested) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const ssize_t written = ::write(impl_->fd, bytes, remaining);
|
||||
if (written < 0) {
|
||||
if (errno == EINTR) {
|
||||
|
||||
Reference in New Issue
Block a user