Diagnostics Messages (C++)
System health and safety types in horus::msg::. Include via <horus/msg/diagnostics.hpp>.
Quick Reference
| Type | Key Fields | Use Case |
|---|---|---|
Heartbeat | node_name[32], node_id (u32), sequence, alive (u8), uptime (double, s), timestamp_ns | Node liveness |
EmergencyStop | engaged (u8), reason[64], source[32], auto_reset | Safety stop |
DiagnosticStatus | level (0-3), code (u32), message[128], component[32] | Component health |
ResourceUsage | cpu_percent, memory_bytes, memory_percent, disk_bytes, disk_percent, network_tx_bytes, network_rx_bytes, temperature, thread_count | System resources |
NodeHeartbeat | state, health, tick_count, target_rate_hz, actual_rate_hz, error_count, last_tick_timestamp, heartbeat_timestamp | Per-node tick/health record (epoch-SECOND timestamps) |
SafetyStatus | enabled, estop_engaged, watchdog_ok, limits_ok, comms_ok (all u8), mode (0/1/2), fault_code | Safety system state |
DiagnosticValue | key[32], value[64], value_type | Key-value diagnostic |
DiagnosticReport | component[32], values[16], value_count, level | Multi-value report |
Heartbeat
Published periodically to prove a node is alive:
class HeartbeatPublisher : public horus::Node {
public:
HeartbeatPublisher() : Node("heartbeat_pub") {
hb_pub_ = advertise<horus::msg::Heartbeat>("heartbeat");
}
void tick() override {
if (++tick_ % 100 != 0) return; // 1 Hz at 100 Hz scheduler
horus::msg::Heartbeat hb{};
std::strncpy(reinterpret_cast<char*>(hb.node_name), "motor_ctrl", 31);
hb.node_id = 7; // numeric node ID (uint32_t)
hb.sequence = seq_++;
hb.alive = true;
hb.uptime = 12.5; // seconds since startup
hb_pub_->send(hb);
}
private:
horus::Publisher<horus::msg::Heartbeat>* hb_pub_;
uint64_t seq_ = 0;
int tick_ = 0;
};
EmergencyStop
The most safety-critical message. engaged=1 means all actuators must stop immediately:
// Trigger e-stop
horus::msg::EmergencyStop estop{};
estop.engaged = 1;
std::strncpy(reinterpret_cast<char*>(estop.reason), "Obstacle < 10cm", 63);
std::strncpy(reinterpret_cast<char*>(estop.source), "safety_monitor", 31);
estop.auto_reset = 0; // manual reset required
estop_pub_->send(estop);
// Clear e-stop
horus::msg::EmergencyStop clear{};
clear.engaged = 0;
estop_pub_->send(clear);
Convention: Every actuator node must subscribe to "emergency.stop" and zero outputs when engaged == 1.
DiagnosticReport — Multi-Value Health Check
horus::msg::DiagnosticReport report{};
std::strncpy(reinterpret_cast<char*>(report.component), "motor_driver", 31);
report.level = 1; // StatusLevel: 0=OK, 1=WARN, 2=ERROR, 3=FATAL
report.value_count = 3;
// Value 0: temperature
std::strncpy(reinterpret_cast<char*>(report.values[0].key), "temperature", 31);
std::strncpy(reinterpret_cast<char*>(report.values[0].value), "72.5", 63);
report.values[0].value_type = 2; // float
// Value 1: current
std::strncpy(reinterpret_cast<char*>(report.values[1].key), "current_amps", 31);
std::strncpy(reinterpret_cast<char*>(report.values[1].value), "3.2", 63);
report.values[1].value_type = 2;
// Value 2: status
std::strncpy(reinterpret_cast<char*>(report.values[2].key), "status", 31);
std::strncpy(reinterpret_cast<char*>(report.values[2].value), "overheating", 63);
report.values[2].value_type = 0; // string
See Also
- Tutorial 3: Full Robot System (C++) — full safety monitor implementation publishing
EmergencyStopon"emergency.stop" - Guide: Real-Time — watchdog and miss policies