test new format
This commit is contained in:
125
main.cpp
125
main.cpp
@ -15,6 +15,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "capture_file_writer.h"
|
#include "capture_file_writer.h"
|
||||||
|
#include "tty_protocol_writer.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -26,6 +27,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -91,6 +93,7 @@ struct Config {
|
|||||||
std::string svg_path = "capture.svg";
|
std::string svg_path = "capture.svg";
|
||||||
std::string live_html_path = "live_plot.html";
|
std::string live_html_path = "live_plot.html";
|
||||||
std::string live_json_path = "live_plot.json";
|
std::string live_json_path = "live_plot.json";
|
||||||
|
std::optional<std::string> tty_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[noreturn]] void fail(const std::string& message) {
|
[[noreturn]] void fail(const std::string& message) {
|
||||||
@ -371,7 +374,7 @@ void print_help(const char* exe_name) {
|
|||||||
<< " [internal_ref_hz:2000000]\n"
|
<< " [internal_ref_hz:2000000]\n"
|
||||||
<< " [di1:zero|trace|ignore]\n"
|
<< " [di1:zero|trace|ignore]\n"
|
||||||
<< " [duration_ms:100] [packet_limit:0] [csv:capture.csv] [svg:capture.svg]\n"
|
<< " [duration_ms:100] [packet_limit:0] [csv:capture.csv] [svg:capture.svg]\n"
|
||||||
<< " [live_html:live_plot.html] [live_json:live_plot.json]\n"
|
<< " [live_html:live_plot.html] [live_json:live_plot.json] [tty:/dev/ttyADC_data]\n"
|
||||||
<< " [recv_block:32768] [stats_period_ms:1000] [live_update_period_ms:1000] [svg_history_packets:50] [start_wait_ms:10000]\n"
|
<< " [recv_block:32768] [stats_period_ms:1000] [live_update_period_ms:1000] [svg_history_packets:50] [start_wait_ms:10000]\n"
|
||||||
<< " [buffer_words:8388608] [step_words:32768]\n"
|
<< " [buffer_words:8388608] [step_words:32768]\n"
|
||||||
<< " [pullup_syn1] [pullup_syn2] [pulldown_conv_in] [pulldown_start_in]\n"
|
<< " [pullup_syn1] [pullup_syn2] [pulldown_conv_in] [pulldown_start_in]\n"
|
||||||
@ -402,6 +405,7 @@ void print_help(const char* exe_name) {
|
|||||||
<< " buffer_words:8388608 -> input stream buffer size in 32-bit words\n"
|
<< " buffer_words:8388608 -> input stream buffer size in 32-bit words\n"
|
||||||
<< " step_words:32768 -> input stream transfer step in 32-bit words\n"
|
<< " step_words:32768 -> input stream transfer step in 32-bit words\n"
|
||||||
<< " live_html/live_json -> live graph files updated as packets arrive\n"
|
<< " live_html/live_json -> live graph files updated as packets arrive\n"
|
||||||
|
<< " tty:/dev/ttyADC_data -> write binary step frames to a Linux/POSIX tty path\n"
|
||||||
<< " If sample_clock_hz is omitted together with clock:internal, the maximum ADC speed is used\n"
|
<< " If sample_clock_hz is omitted together with clock:internal, the maximum ADC speed is used\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Differential physical channel mapping:\n"
|
<< "Differential physical channel mapping:\n"
|
||||||
@ -577,6 +581,10 @@ Config parse_args(int argc, char** argv) {
|
|||||||
cfg.live_json_path = arg.substr(10);
|
cfg.live_json_path = arg.substr(10);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (starts_with(arg, "tty:")) {
|
||||||
|
cfg.tty_path = arg.substr(4);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
fail("Unknown argument: " + arg);
|
fail("Unknown argument: " + arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,6 +624,9 @@ Config parse_args(int argc, char** argv) {
|
|||||||
if (cfg.input_buffer_words < cfg.recv_block_words) {
|
if (cfg.input_buffer_words < cfg.recv_block_words) {
|
||||||
cfg.input_buffer_words = cfg.recv_block_words;
|
cfg.input_buffer_words = cfg.recv_block_words;
|
||||||
}
|
}
|
||||||
|
if (cfg.tty_path && (cfg.channel_count != 2U)) {
|
||||||
|
fail("tty output requires channels:2");
|
||||||
|
}
|
||||||
if (sync_uses_di_syn1(cfg.sync_mode) && sync_uses_di_syn1(cfg.sync_start_mode)) {
|
if (sync_uses_di_syn1(cfg.sync_mode) && sync_uses_di_syn1(cfg.sync_start_mode)) {
|
||||||
fail("clock and start cannot both use DI_SYN1; use start_in or immediate start");
|
fail("clock and start cannot both use DI_SYN1; use start_in or immediate start");
|
||||||
}
|
}
|
||||||
@ -857,6 +868,48 @@ struct PacketAccumulator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TtyStepAccumulator {
|
||||||
|
double sum_ch1 = 0.0;
|
||||||
|
double sum_ch2 = 0.0;
|
||||||
|
uint32_t count_ch1 = 0;
|
||||||
|
uint32_t count_ch2 = 0;
|
||||||
|
uint32_t next_step_index = 1;
|
||||||
|
|
||||||
|
void clear_step() {
|
||||||
|
sum_ch1 = 0.0;
|
||||||
|
sum_ch2 = 0.0;
|
||||||
|
count_ch1 = 0;
|
||||||
|
count_ch2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_packet() {
|
||||||
|
clear_step();
|
||||||
|
next_step_index = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_sample(uint32_t lch, double raw_code) {
|
||||||
|
if (lch == 0U) {
|
||||||
|
sum_ch1 += raw_code;
|
||||||
|
++count_ch1;
|
||||||
|
} else if (lch == 1U) {
|
||||||
|
sum_ch2 += raw_code;
|
||||||
|
++count_ch2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_complete_step() const {
|
||||||
|
return (count_ch1 != 0U) && (count_ch2 != 0U);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int16_t pack_raw_code_to_int16(double avg_raw_code) {
|
||||||
|
const double scaled =
|
||||||
|
avg_raw_code * 32767.0 / static_cast<double>(X502_ADC_SCALE_CODE_MAX);
|
||||||
|
const long long rounded = std::llround(scaled);
|
||||||
|
const long long clamped = std::clamp<long long>(rounded, -32768LL, 32767LL);
|
||||||
|
return static_cast<int16_t>(clamped);
|
||||||
|
}
|
||||||
|
|
||||||
struct ConsoleCtrlGuard {
|
struct ConsoleCtrlGuard {
|
||||||
bool installed = false;
|
bool installed = false;
|
||||||
|
|
||||||
@ -1039,11 +1092,16 @@ int run(const Config& cfg) {
|
|||||||
<< " ADC range: +/-" << range_to_volts(cfg.range) << " V\n"
|
<< " ADC range: +/-" << range_to_volts(cfg.range) << " V\n"
|
||||||
<< " max frames per packet per channel: " << target_frames << "\n";
|
<< " max frames per packet per channel: " << target_frames << "\n";
|
||||||
|
|
||||||
|
std::unique_ptr<TtyProtocolWriter> tty_writer;
|
||||||
|
if (cfg.tty_path) {
|
||||||
|
tty_writer = std::make_unique<TtyProtocolWriter>(*cfg.tty_path);
|
||||||
|
}
|
||||||
CaptureFileWriter writer(cfg.csv_path, cfg.svg_path, cfg.live_html_path, cfg.live_json_path);
|
CaptureFileWriter writer(cfg.csv_path, cfg.svg_path, cfg.live_html_path, cfg.live_json_path);
|
||||||
writer.initialize_live_plot();
|
writer.initialize_live_plot();
|
||||||
writer.initialize_csv(cfg.channel_count, cfg.di1_mode == Di1Mode::Trace);
|
writer.initialize_csv(cfg.channel_count, cfg.di1_mode == Di1Mode::Trace);
|
||||||
std::cout << " live plot html: " << writer.live_html_path() << "\n"
|
std::cout << " live plot html: " << writer.live_html_path() << "\n"
|
||||||
<< " live plot data: " << writer.live_json_path() << "\n"
|
<< " live plot data: " << writer.live_json_path() << "\n"
|
||||||
|
<< " tty step output: " << (cfg.tty_path ? *cfg.tty_path : std::string("disabled")) << "\n"
|
||||||
<< " recv block words: " << cfg.recv_block_words << "\n"
|
<< " recv block words: " << cfg.recv_block_words << "\n"
|
||||||
<< " input step words: " << cfg.input_step_words << "\n"
|
<< " input step words: " << cfg.input_step_words << "\n"
|
||||||
<< " input buffer words: " << cfg.input_buffer_words << "\n"
|
<< " input buffer words: " << cfg.input_buffer_words << "\n"
|
||||||
@ -1062,11 +1120,14 @@ int run(const Config& cfg) {
|
|||||||
const uint32_t read_capacity_words = std::max(cfg.recv_block_words, cfg.input_step_words);
|
const uint32_t read_capacity_words = std::max(cfg.recv_block_words, cfg.input_step_words);
|
||||||
std::vector<uint32_t> raw(read_capacity_words);
|
std::vector<uint32_t> raw(read_capacity_words);
|
||||||
std::vector<double> adc_buffer(read_capacity_words);
|
std::vector<double> adc_buffer(read_capacity_words);
|
||||||
|
std::vector<double> adc_raw_buffer(read_capacity_words);
|
||||||
std::vector<uint32_t> din_buffer(read_capacity_words);
|
std::vector<uint32_t> din_buffer(read_capacity_words);
|
||||||
std::deque<double> pending_adc;
|
std::deque<double> pending_adc;
|
||||||
|
std::deque<double> pending_adc_raw;
|
||||||
std::deque<uint32_t> pending_din;
|
std::deque<uint32_t> pending_din;
|
||||||
std::deque<CapturePacket> packets;
|
std::deque<CapturePacket> packets;
|
||||||
PacketAccumulator current_packet;
|
PacketAccumulator current_packet;
|
||||||
|
TtyStepAccumulator tty_step;
|
||||||
current_packet.reset(target_frames, cfg.channel_count);
|
current_packet.reset(target_frames, cfg.channel_count);
|
||||||
std::size_t csv_global_frame_index = 0;
|
std::size_t csv_global_frame_index = 0;
|
||||||
|
|
||||||
@ -1145,9 +1206,29 @@ int run(const Config& cfg) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current_packet.reset(target_frames, cfg.channel_count);
|
current_packet.reset(target_frames, cfg.channel_count);
|
||||||
|
if (tty_writer) {
|
||||||
|
tty_step.reset_packet();
|
||||||
|
tty_writer->emit_packet_start();
|
||||||
|
}
|
||||||
packet_active = true;
|
packet_active = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto emit_tty_step = [&]() {
|
||||||
|
if (!tty_writer || !tty_step.has_complete_step()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tty_step.next_step_index >= 0xFFFFU) {
|
||||||
|
fail("TTY protocol step index overflow within packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
const double ch1_avg = tty_step.sum_ch1 / static_cast<double>(tty_step.count_ch1);
|
||||||
|
const double ch2_avg = tty_step.sum_ch2 / static_cast<double>(tty_step.count_ch2);
|
||||||
|
tty_writer->emit_step(static_cast<uint16_t>(tty_step.next_step_index),
|
||||||
|
pack_raw_code_to_int16(ch1_avg),
|
||||||
|
pack_raw_code_to_int16(ch2_avg));
|
||||||
|
++tty_step.next_step_index;
|
||||||
|
};
|
||||||
|
|
||||||
auto finish_packet = [&](PacketCloseReason reason) {
|
auto finish_packet = [&](PacketCloseReason reason) {
|
||||||
const std::size_t frames = current_packet.frame_count(cfg.channel_count);
|
const std::size_t frames = current_packet.frame_count(cfg.channel_count);
|
||||||
if (frames != 0U) {
|
if (frames != 0U) {
|
||||||
@ -1230,6 +1311,7 @@ int run(const Config& cfg) {
|
|||||||
|
|
||||||
packet_active = false;
|
packet_active = false;
|
||||||
current_packet.reset(target_frames, cfg.channel_count);
|
current_packet.reset(target_frames, cfg.channel_count);
|
||||||
|
tty_step.clear_step();
|
||||||
};
|
};
|
||||||
|
|
||||||
while (!stop_loop_requested) {
|
while (!stop_loop_requested) {
|
||||||
@ -1309,7 +1391,24 @@ int run(const Config& cfg) {
|
|||||||
}
|
}
|
||||||
expect_ok(api, process_err, "Process ADC+DIN data");
|
expect_ok(api, process_err, "Process ADC+DIN data");
|
||||||
|
|
||||||
if ((adc_count == 0U) && (din_count == 0U)) {
|
uint32_t raw_adc_count = 0;
|
||||||
|
if (tty_writer) {
|
||||||
|
raw_adc_count = static_cast<uint32_t>(adc_raw_buffer.size());
|
||||||
|
const int32_t raw_process_err = api.ProcessData(device.hnd,
|
||||||
|
raw.data(),
|
||||||
|
static_cast<uint32_t>(recvd),
|
||||||
|
0,
|
||||||
|
adc_raw_buffer.data(),
|
||||||
|
&raw_adc_count,
|
||||||
|
nullptr,
|
||||||
|
nullptr);
|
||||||
|
expect_ok(api, raw_process_err, "Process raw ADC data");
|
||||||
|
if (raw_adc_count != adc_count) {
|
||||||
|
fail("Raw ADC processing returned a different sample count than voltage processing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((adc_count == 0U) && (din_count == 0U) && (!tty_writer || (raw_adc_count == 0U))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1321,19 +1420,31 @@ int run(const Config& cfg) {
|
|||||||
for (uint32_t i = 0; i < adc_count; ++i) {
|
for (uint32_t i = 0; i < adc_count; ++i) {
|
||||||
pending_adc.push_back(adc_buffer[i]);
|
pending_adc.push_back(adc_buffer[i]);
|
||||||
}
|
}
|
||||||
|
if (tty_writer) {
|
||||||
|
for (uint32_t i = 0; i < raw_adc_count; ++i) {
|
||||||
|
pending_adc_raw.push_back(adc_raw_buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (uint32_t i = 0; i < din_count; ++i) {
|
for (uint32_t i = 0; i < din_count; ++i) {
|
||||||
pending_din.push_back(din_buffer[i]);
|
pending_din.push_back(din_buffer[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pending_adc.size() > 1000000U) || (pending_din.size() > 1000000U)) {
|
if ((pending_adc.size() > 1000000U) ||
|
||||||
|
(pending_din.size() > 1000000U) ||
|
||||||
|
(tty_writer && (pending_adc_raw.size() > 1000000U))) {
|
||||||
fail("Internal backlog grew too large while aligning ADC and DIN samples");
|
fail("Internal backlog grew too large while aligning ADC and DIN samples");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!pending_adc.empty() &&
|
while (!pending_adc.empty() &&
|
||||||
!pending_din.empty() &&
|
!pending_din.empty() &&
|
||||||
|
(!tty_writer || !pending_adc_raw.empty()) &&
|
||||||
!stop_loop_requested) {
|
!stop_loop_requested) {
|
||||||
const double adc_value = pending_adc.front();
|
const double adc_value = pending_adc.front();
|
||||||
pending_adc.pop_front();
|
pending_adc.pop_front();
|
||||||
|
const double adc_raw_value = tty_writer ? pending_adc_raw.front() : 0.0;
|
||||||
|
if (tty_writer) {
|
||||||
|
pending_adc_raw.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
const uint32_t din_value = pending_din.front();
|
const uint32_t din_value = pending_din.front();
|
||||||
pending_din.pop_front();
|
pending_din.pop_front();
|
||||||
@ -1391,6 +1502,11 @@ int run(const Config& cfg) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tty_writer && di1_changed) {
|
||||||
|
emit_tty_step();
|
||||||
|
tty_step.clear_step();
|
||||||
|
}
|
||||||
|
|
||||||
const uint32_t lch = next_lch;
|
const uint32_t lch = next_lch;
|
||||||
next_lch = (next_lch + 1U) % cfg.channel_count;
|
next_lch = (next_lch + 1U) % cfg.channel_count;
|
||||||
|
|
||||||
@ -1409,6 +1525,9 @@ int run(const Config& cfg) {
|
|||||||
|
|
||||||
if (current_packet.channels[lch].size() < target_frames) {
|
if (current_packet.channels[lch].size() < target_frames) {
|
||||||
current_packet.channels[lch].push_back(stored_value);
|
current_packet.channels[lch].push_back(stored_value);
|
||||||
|
if (tty_writer) {
|
||||||
|
tty_step.add_sample(lch, adc_raw_value);
|
||||||
|
}
|
||||||
++current_packet.stored_samples;
|
++current_packet.stored_samples;
|
||||||
++total_stored_adc_samples;
|
++total_stored_adc_samples;
|
||||||
++stats_stored_adc_samples;
|
++stats_stored_adc_samples;
|
||||||
|
|||||||
128
tty_protocol_writer.cpp
Normal file
128
tty_protocol_writer.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#include "tty_protocol_writer.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
TtyProtocolWriter::TtyProtocolWriter(std::string path) : path_(std::move(path)) {
|
||||||
|
throw std::runtime_error("tty output is supported only on Linux/POSIX");
|
||||||
|
}
|
||||||
|
|
||||||
|
TtyProtocolWriter::~TtyProtocolWriter() = default;
|
||||||
|
|
||||||
|
TtyProtocolWriter::TtyProtocolWriter(TtyProtocolWriter&& other) noexcept = default;
|
||||||
|
|
||||||
|
TtyProtocolWriter& TtyProtocolWriter::operator=(TtyProtocolWriter&& other) noexcept = default;
|
||||||
|
|
||||||
|
void TtyProtocolWriter::emit_packet_start() const {}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::emit_step(uint16_t index, int16_t ch1_avg, int16_t ch2_avg) const {
|
||||||
|
(void) index;
|
||||||
|
(void) ch1_avg;
|
||||||
|
(void) ch2_avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& TtyProtocolWriter::path() const {
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::write_frame(uint16_t word0, uint16_t word1, uint16_t word2, uint16_t word3) const {
|
||||||
|
(void) word0;
|
||||||
|
(void) word1;
|
||||||
|
(void) word2;
|
||||||
|
(void) word3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::close_fd() noexcept {}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string io_error(const std::string& action, const std::string& path) {
|
||||||
|
std::ostringstream out;
|
||||||
|
out << action << " '" << path << "': " << std::strerror(errno);
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TtyProtocolWriter::TtyProtocolWriter(std::string path) : path_(std::move(path)) {
|
||||||
|
fd_ = ::open(path_.c_str(), O_WRONLY | O_NOCTTY);
|
||||||
|
if (fd_ < 0) {
|
||||||
|
throw std::runtime_error(io_error("Cannot open tty output", path_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TtyProtocolWriter::~TtyProtocolWriter() {
|
||||||
|
close_fd();
|
||||||
|
}
|
||||||
|
|
||||||
|
TtyProtocolWriter::TtyProtocolWriter(TtyProtocolWriter&& other) noexcept
|
||||||
|
: path_(std::move(other.path_)),
|
||||||
|
fd_(other.fd_) {
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TtyProtocolWriter& TtyProtocolWriter::operator=(TtyProtocolWriter&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
close_fd();
|
||||||
|
path_ = std::move(other.path_);
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::emit_packet_start() const {
|
||||||
|
write_frame(0x000A, 0xFFFF, 0xFFFF, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::emit_step(uint16_t index, int16_t ch1_avg, int16_t ch2_avg) const {
|
||||||
|
write_frame(0x000A,
|
||||||
|
index,
|
||||||
|
static_cast<uint16_t>(ch1_avg),
|
||||||
|
static_cast<uint16_t>(ch2_avg));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& TtyProtocolWriter::path() const {
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::write_frame(uint16_t word0, uint16_t word1, uint16_t word2, uint16_t word3) const {
|
||||||
|
const uint16_t frame[4] = {word0, word1, word2, word3};
|
||||||
|
const std::uint8_t* bytes = reinterpret_cast<const std::uint8_t*>(frame);
|
||||||
|
std::size_t remaining = sizeof(frame);
|
||||||
|
|
||||||
|
while (remaining != 0U) {
|
||||||
|
const ssize_t written = ::write(fd_, bytes, remaining);
|
||||||
|
if (written < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(io_error("Cannot write tty frame to", path_));
|
||||||
|
}
|
||||||
|
if (written == 0) {
|
||||||
|
throw std::runtime_error("tty write returned 0 bytes for '" + path_ + "'");
|
||||||
|
}
|
||||||
|
bytes += static_cast<std::size_t>(written);
|
||||||
|
remaining -= static_cast<std::size_t>(written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TtyProtocolWriter::close_fd() noexcept {
|
||||||
|
if (fd_ >= 0) {
|
||||||
|
::close(fd_);
|
||||||
|
fd_ = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
29
tty_protocol_writer.h
Normal file
29
tty_protocol_writer.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class TtyProtocolWriter {
|
||||||
|
public:
|
||||||
|
explicit TtyProtocolWriter(std::string path);
|
||||||
|
~TtyProtocolWriter();
|
||||||
|
|
||||||
|
TtyProtocolWriter(const TtyProtocolWriter&) = delete;
|
||||||
|
TtyProtocolWriter& operator=(const TtyProtocolWriter&) = delete;
|
||||||
|
TtyProtocolWriter(TtyProtocolWriter&& other) noexcept;
|
||||||
|
TtyProtocolWriter& operator=(TtyProtocolWriter&& other) noexcept;
|
||||||
|
|
||||||
|
void emit_packet_start() const;
|
||||||
|
void emit_step(uint16_t index, int16_t ch1_avg, int16_t ch2_avg) const;
|
||||||
|
|
||||||
|
const std::string& path() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void write_frame(uint16_t word0, uint16_t word1, uint16_t word2, uint16_t word3) const;
|
||||||
|
void close_fd() noexcept;
|
||||||
|
|
||||||
|
std::string path_;
|
||||||
|
#ifndef _WIN32
|
||||||
|
int fd_ = -1;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user