18 socket_ = std::make_unique<daq_comms::transport::UDPSocket>(bind_address, bind_port);
31 uint16_t source_port = 0;
35 if (received < 0 && !socket_->
last_error().empty()) {
36 static size_t error_count = 0;
37 if (error_count++ < 5) {
38 std::cerr <<
"[Pipeline] Socket receive error: " <<
socket_->last_error()
45 if (received <
static_cast<ssize_t
>(
sizeof(daq::PacketHeader))) {
49 daq::PacketHeader peek{};
53 daq::PacketHeader header{};
54 daq::EnvironmentalDataPacket data{};
56 received !=
static_cast<ssize_t
>(
sizeof(header) +
sizeof(data)) ||
58 !std::isfinite(data.temperature_c) || !std::isfinite(data.humidity_rh) ||
59 data.humidity_rh < 0.0f || data.humidity_rh > 100.0f || data.pressure_pa == 0) {
69 daq::PacketHeader sensor_header;
70 std::vector<daq::SensorDataChunkCollection>
chunks;
73 static size_t parse_fail_count = 0;
74 if (++parse_fail_count <= 10 || parse_fail_count % 500 == 0) {
75 std::cerr <<
"[Pipeline] SENSOR_DATA parse failed #" << parse_fail_count
85 auto now = std::chrono::steady_clock::now();
87 std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch())
90 batch.
sequence_id = sensor_header.timestamp & 0xFFFF;
94 for (
const auto& dp :
chunk.datapoints) {
107 daq::PacketHeader st_header;
108 uint8_t adc_good = 0;
109 std::vector<daq::SelfTestResult> st_results;
111 st_header, adc_good, st_results)) {
112 std::cerr <<
"[Pipeline] SELF_TEST parse failed from " <<
last_source_ip_ << std::endl;
117 auto now = std::chrono::steady_clock::now();
119 std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
124 parsed.
packet_type =
static_cast<uint8_t
>(st_header.packet_type);
125 parsed.
version = st_header.version;
128 parsed.
num_sensors =
static_cast<uint8_t
>(st_results.size());
130 for (
const auto&
r : st_results) {
131 parsed.
results.push_back({
r.sensor_id,
r.result});
133 batch.
self_tests.push_back(std::move(parsed));
161 const uint8_t* data,
size_t size) {
164 return socket_->send_to(dest_ip, dest_port, data, size);
195std::optional<SensorFramePipeline::EnvironmentalSample>
static void enable(board_bringup_t *b, uint16_t device_id)
std::vector< uint8_t > receive_buffer_
std::vector< uint8_t > last_heartbeat_buffer_
static constexpr size_t RECEIVE_BUFFER_SIZE
std::string last_heartbeat_source_ip_
std::string last_error() const
std::unique_ptr< daq_comms::transport::UDPSocket > socket_
SensorFramePipeline(const std::string &bind_address, uint16_t bind_port)
std::optional< EnvironmentalSample > get_last_environmental()
bool set_broadcast(bool enable)
std::string last_source_ip_
std::optional< daq_comms::protocol::SensorBatch > poll()
std::vector< uint8_t > last_log_buffer_
std::optional< LastLog > get_last_log()
std::optional< EnvironmentalSample > last_environmental_
std::optional< LastHeartbeat > get_last_heartbeat()
ssize_t send_to(const std::string &dest_ip, uint16_t dest_port, const uint8_t *data, size_t size)
std::string last_log_source_ip_
#define DIABLO_COMMS_VERSION
std::vector< daq::SensorDataChunkCollection > chunks
bool parse_self_test_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, uint8_t &adc_good_out, std::vector< SelfTestResult > &results_out)
Parses a Self Test packet from buffer.
bool parse_environmental_data_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, EnvironmentalDataPacket &data_out)
Parses an Environmental Data packet from buffer.
bool parse_sensor_data_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, std::vector< SensorDataChunkCollection > &chunks_out)
Parses a Sensor Data packet from buffer into chunk collections.
Motorized ball valve position: raw ADC counts -> % open.
Parsed DAQv2 SELF_TEST payload (host-side mirror; filled from daq::parse_self_test_packet).
std::vector< Result > results
uint32_t sample_timestamp_ms
uint64_t frame_timestamp_ns
std::vector< RawPTSample > pt_samples
std::vector< ParsedSelfTestPacket > self_tests