ros2_medkit_fault_detection
This section contains design documentation for the ros2_medkit_fault_detection package.
Overview
The ros2_medkit_fault_detection package provides a single, protocol-agnostic
model for turning a raw value read from any source (OPC UA, S7, Modbus, ADS, …)
into the set of faults that value implies. Every protocol plugin lowers its own
configuration onto this shared model, so a threshold, a status-word bit, or a
fault-code register is mapped to a SOVD fault identically regardless of the
transport it came from.
The package is a header-only INTERFACE library (cxx_std_17). Protocol
plugins are built as MODULE libraries that resolve gateway symbols from the
host process at load time (--unresolved-symbols=ignore-all); a separately
linked shared library would not be present in that host, so the detection logic
is compiled directly into each plugin instead.
Detection modes
A single mapped point carries exactly one of three composable detection rules,
expressed as a DetectionRule variant:
ThresholdRuleNumeric above/below a setpoint. A boolean point honours the direction, so a normally-closed contact or a watchdog-OK bit can alarm on
false.StatusWordRuleDecode named bits of an integer status register, one fault per bit. An optional source-register
widthmasks the raw value to that many low bits before decode, so a signed register read whose sign bit is set does not light up spurious high bits.EnumMapRuleMap a fault-code register value to at most one fault code and text. An optional catch-all keeps a non-ok value that matches no configured code visible rather than reading as healthy.
Evaluator contract
evaluate(value, rule) is a pure function of its inputs with no ROS or
protocol dependencies, which makes it trivially unit-testable. It returns the
full set of faults the rule governs, each flagged active or inactive, so a
transition tracker can clear a previous code and raise a new one in the same
cycle.
Undecidable input holds state. A value that cannot be coerced for the rule - a non-finite double (NaN / inf from a disconnected analog sensor or a div-by-zero on the PLC), a string, or a failed numeric conversion - yields an empty result. The evaluator emits no signal, so a transition tracker preserves the prior state instead of clearing a standing fault. A bad read must never mask a real alarm.
Transition tracking and global uniqueness
FaultTransitionTracker layers stateful raise/clear edge detection on top of
the evaluator. It returns only the signals whose active state changed since the
last call, which a plugin forwards to the fault manager as report / clear.
The tracker is keyed by fault_code alone, matching the fault manager, which
also keys and clears faults by code alone. A single tracker may therefore be
shared across many points only if every fault_code is globally unique; two
points emitting the same code would alternately raise and clear it each cycle.
Consumers that share one tracker (for example the OPC UA poller across all
node-map entries and event alarms) must enforce that uniqueness at
config-load time and reject a colliding configuration before anything runs.