37 uint8_t ip_range_start, uint8_t ip_range_end) {
43 std::cout <<
"[BoardDiscovery] Initialized on interface: " <<
network_interface_ << std::endl;
58 std::cout <<
"[BoardDiscovery] Starting discovery (mode="
75 std::cout <<
"[BoardDiscovery] Discovery stopped" << std::endl;
79 const std::string& source_ip) {
80 daq::PacketHeader hdr;
81 daq::BoardHeartbeatPacket hb;
87 signature.
board_id =
static_cast<uint32_t
>(hb.board_id);
94 board.signature = signature;
95 board.current_ip = source_ip;
96 board.last_seen = std::chrono::steady_clock::now();
97 board.is_configured =
false;
102 std::hash<std::string> hasher;
103 uint32_t ip_hash =
static_cast<uint32_t
>(hasher(source_ip));
104 std::ostringstream
mac;
105 mac << std::hex << std::setw(2) << std::setfill(
'0') << ((ip_hash >> 16) & 0xFF) <<
":"
106 << std::hex << std::setw(2) << std::setfill(
'0') << ((ip_hash >> 8) & 0xFF) <<
":"
107 << std::hex << std::setw(2) << std::setfill(
'0') << (ip_hash & 0xFF) <<
":" << std::hex
108 << std::setw(2) << std::setfill(
'0') << ((ip_hash >> 24) & 0xFF) <<
":" << std::hex
109 << std::setw(2) << std::setfill(
'0') << ((signature.
board_id >> 8) & 0xFF) <<
":"
110 << std::hex << std::setw(2) << std::setfill(
'0') << (signature.
board_id & 0xFF);
116 uint8_t board_id_octet = hb.board_id;
118 std::string assigned_ip;
121 assigned_ip = it->second;
129 board.current_ip = assigned_ip;
130 std::cout <<
"[BoardDiscovery] Assigned IP " << assigned_ip <<
" to "
136 stats_.boards_discovered++;
137 stats_.last_discovery = std::chrono::steady_clock::now();
146 const std::string& source_ip) {
147 daq::PacketHeader hdr;
148 std::vector<daq::SensorDataChunkCollection>
chunks;
163 std::map<uint8_t, bool> sensor_ids;
164 for (
const auto& dp :
chunks[0].datapoints) {
165 sensor_ids[dp.sensor_id] = (dp.data != 0);
167 if (sensor_ids.empty()) {
171 std::vector<SensorInfo> sensors;
172 for (
const auto& [sensor_id, is_active] : sensor_ids) {
174 info.sensor_type = board_type_wire;
175 info.channel_id = sensor_id;
176 info.sensor_count = 1;
177 info.is_active = is_active;
178 info.quality = is_active ? 255 : 0;
179 sensors.push_back(
info);
184 board.sensors = sensors;
185 board.active_sensors = sensors.size();
186 board.last_seen = std::chrono::steady_clock::now();
187 stats_.sensors_detected += sensors.size();
190 std::hash<std::string> hasher;
191 sig.
board_id =
static_cast<uint32_t
>(hasher(source_ip));
198 board.signature = sig;
199 board.current_ip = source_ip;
200 board.sensors = sensors;
201 board.last_seen = std::chrono::steady_clock::now();
202 board.is_configured =
false;
204 board.max_sensors = sensors.size();
205 board.active_sensors = sensors.size();
208 stats_.boards_discovered++;
214 std::vector<SensorInfo> sensors;
215 daq::PacketHeader hdr;
216 std::vector<daq::SensorDataChunkCollection>
chunks;
220 std::map<uint8_t, bool> sensor_ids;
221 for (
const auto& dp :
chunks[0].datapoints) {
222 sensor_ids[dp.sensor_id] = (dp.data != 0);
224 for (
const auto& [sensor_id, is_active] : sensor_ids) {
226 info.sensor_type = 0;
227 info.channel_id = sensor_id;
228 info.sensor_count = 1;
229 info.is_active = is_active;
230 info.quality = is_active ? 255 : 0;
231 sensors.push_back(
info);
237 const std::vector<SensorInfo>& sensors)
const {
238 if (sensors.empty()) {
239 return BoardType::UNKNOWN;
243 std::map<uint8_t, size_t> type_counts;
244 for (
const auto& sensor : sensors) {
245 type_counts[sensor.sensor_type]++;
248 if (type_counts.size() == 1) {
250 switch (type_counts.begin()->first) {
252 return BoardType::PT_BOARD;
254 return BoardType::LC_BOARD;
256 return BoardType::RTD_BOARD;
258 return BoardType::TC_BOARD;
260 return BoardType::MIXED_BOARD;
262 return BoardType::UNKNOWN;
266 return BoardType::MIXED_BOARD;
281 it->second.current_ip = ip;
299 size_t last_dot =
base_ip_.rfind(
'.');
300 std::string base =
base_ip_.substr(0, last_dot);
302 return base +
"." + std::to_string(ip_octet);
318 it->second.last_seen =
board.last_seen;
319 if (!
board.sensors.empty()) {
320 it->second.sensors =
board.sensors;
321 it->second.active_sensors =
board.sensors.size();
323 if (!
board.current_ip.empty()) {
324 it->second.current_ip =
board.current_ip;
332 std::vector<DiscoveredBoard> boards;
334 boards.push_back(
board);
347 return board_it->second;
353 std::map<std::string, std::map<std::string, std::string>>
config;
356 size_t board_idx = 0;
358 std::string board_key =
"board_" + std::to_string(board_idx++);
363 if (!sensor_config.empty()) {
364 config[board_key +
".sensors"] = sensor_config;
373 std::map<std::string, std::string>
config;
378 config[
"board_id"] =
"0x" + std::to_string(
board.signature.board_id);
379 config[
"board_type"] = std::to_string(
board.signature.board_type);
380 config[
"max_sensors"] = std::to_string(
board.max_sensors);
381 config[
"active_sensors"] = std::to_string(
board.active_sensors);
387 const std::vector<SensorInfo>& sensors)
const {
388 std::map<std::string, std::string>
config;
391 std::map<uint8_t, std::vector<SensorInfo>> sensors_by_type;
392 for (
const auto& sensor : sensors) {
393 sensors_by_type[sensor.sensor_type].push_back(sensor);
396 for (
const auto& [type, sensor_list] : sensors_by_type) {
397 std::string type_name;
412 type_name =
"unknown_" + std::to_string(type);
416 config[type_name +
"_enabled"] =
"true";
417 config[type_name +
"_count"] = std::to_string(sensor_list.size());
421 for (
size_t i = 0; i < sensor_list.size(); ++i) {
424 channels += std::to_string(sensor_list[i].channel_id);
434 std::cout <<
"[BoardDiscovery] Scanning network..." << std::endl;
439 std::cout <<
"[BoardDiscovery] Listening for board announcements..." << std::endl;
static const uint32_t channels[BOARD_ADC_COUNT]
static board_sleep_info_t info
void start_discovery(DiscoveryMode mode=DiscoveryMode::HYBRID)
Start discovery process.
std::atomic< bool > discovery_active_
std::vector< SensorInfo > detect_sensors_from_packet(const uint8_t *data, size_t size) const
std::string network_interface_
std::map< std::string, BoardSignature > ip_to_signature_
std::map< BoardSignature, std::string > signature_to_ip_
BoardType detect_board_type(const std::vector< SensorInfo > &sensors) const
void add_or_update_board(const DiscoveredBoard &board)
void set_static_ip_for_board(uint8_t board_id, const std::string &ip)
Set static IP for board_id (from config).
std::optional< std::string > assign_ip(const BoardSignature &signature)
Assign IP address to board based on signature.
std::map< BoardSignature, DiscoveredBoard > discovered_boards_
void process_board_announcement(const uint8_t *data, size_t size, const std::string &source_ip)
Process incoming board announcement packet.
std::map< std::string, std::map< std::string, std::string > > generate_config()
Generate configuration from discovered boards.
void register_discovery_callback(std::function< void(const DiscoveredBoard &)> callback)
Register callback for board discovery events.
std::string calculate_ip_from_signature(const BoardSignature &signature) const
void stop_discovery()
Stop discovery process.
std::map< std::string, std::string > generate_board_config(const DiscoveredBoard &board) const
void process_sensor_data(const uint8_t *data, size_t size, const std::string &source_ip)
Process sensor data packet to detect sensors.
std::optional< DiscoveredBoard > get_board_by_ip(const std::string &ip)
Get board by IP address.
bool initialize(const std::string &network_interface, const std::string &base_ip, uint8_t ip_range_start=100, uint8_t ip_range_end=200)
Initialize discovery system.
void listen_for_announcements()
std::map< uint8_t, std::string > static_ip_overrides_
std::map< std::string, std::string > generate_sensor_config(const std::vector< SensorInfo > &sensors) const
std::vector< std::function< void(const DiscoveredBoard &)> > discovery_callbacks_
DiscoveryMode current_mode_
bool is_ip_available(const std::string &ip) const
std::vector< DiscoveredBoard > get_discovered_boards()
Get all discovered boards.
std::vector< daq::SensorDataChunkCollection > chunks
bool parse_board_heartbeat_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, BoardHeartbeatPacket &data_out)
Parses a Board Heartbeat 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.
constexpr uint8_t kPressureTransducer
constexpr uint8_t kUnknown
Legacy DAQv2 wire numeric board_type (Elodin / discovery); heartbeat body no longer carries type.
std::string calculate_ip_from_mac(const std::string &mac_address, const std::string &base_ip, uint8_t ip_range_start, uint8_t ip_range_end)
Deterministic last-octet from MAC string "aa:bb:cc:dd:ee:ff" into base_ip range.
Motorized ball valve position: raw ADC counts -> % open.
Board signature/identifier structure.
std::string to_string() const
Discovered board information.
Detected sensor information from board.
std::string board(const std::string &name, const std::string &id, bool enabled=true)