feat(opcua): zero-config native A&C alarms -> faults#511
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds zero-config native OPC-UA Alarms & Conditions → SOVD faults support to the ros2_medkit_opcua plugin via a new auto_alarms node-map configuration. This extends the plugin’s alarm ingestion beyond hand-written event_alarms mappings by auto-deriving faults from observed condition events, while preserving explicit-mapping precedence and avoiding duplicate subscriptions.
Changes:
- Implemented
auto_alarmsparsing inNodeMap(bool + map forms, defaults, validation, unknown-key warnings) plus helpers for auto fault-code derivation, filtering, and severity mapping. - Updated
OpcuaPollerto subscribe/replay effective alarm sources (explicit + optional synthetic auto source), filter non-condition “system message” events, and supportauto_clearsemantics. - Added/extended unit tests and wired a new gtest target to cover the pure-function selection/filters and YAML behavior; updated plugin README documentation.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ros2_medkit_plugins/ros2_medkit_opcua/src/opcua_poller.cpp | Adds effective alarm-source selection, non-condition event filtering, and auto-derived fault handling in the event + replay paths. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/opcua_poller.hpp | Exposes/notes new pure helpers and per-observation clear-gating override. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/src/node_map.cpp | Parses auto_alarms, warns on unknown keys, and registers the fallback fault-bearing entity. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/node_map.hpp | Introduces AutoAlarmsConfig/AutoAlarmsSeverityBands and declares auto-derivation helpers. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/test/test_node_map.cpp | Adds unit tests for fault-code derivation, filters, severity bands, and YAML parsing/defaults. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/test/test_opcua_poller.cpp | New tests for effective_alarm_sources() precedence and is_condition_event() system-message filtering. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/CMakeLists.txt | Registers the new test_opcua_poller gtest target and assigns a test domain. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/README.md | Documents auto_alarms configuration and behavior. |
bburda
reviewed
Jul 10, 2026
bburda
previously approved these changes
Jul 11, 2026
Add auto_alarms to the node map: subscribe to a server EventNotifier (default Server object) and auto-derive a fault per condition with no per-alarm event_alarms mapping. fault_code from ConditionName/SourceName, falling back to a hash of SourceNode+EventType+Message so Siemens alarms sharing one SourceNode don't collapse. Filters non-condition system messages (null ConditionId). Explicit event_alarms still take precedence. Composes with #509 (discovery) and #510 (auto_browse).
…mments - AutoAlarmsConfig::entity_id doc (node_map.hpp) and the poller comment describing its fallback (opcua_poller.cpp) both said the default was the node map's bare component_id / PLC root component; NodeMap::load() actually defaults it to "<component_id>_alarms" to avoid an entity ID collision with the Component entity. Comments now match the code. - apply_condition_state()'s require_confirm_for_clear doc (opcua_poller.hpp) described the auto-derived-alarm caller as passing '!auto_clear complemented'; the actual on_event()/read_fallback_replay() logic forces false (plus acked_state=true) when auto_clear is true, and otherwise passes the poller-wide config_.require_confirm_for_clear unchanged - not a value derived from auto_clear at all. - README: auto_clear: false does not gate clearing through an event_alarms-mapping-provided acknowledge_fault operation (auto-derived alarms never get one); clearing is gated by the OPC-UA server's own Acked/Confirmed condition state, set by an HMI/SCADA system or another OPC-UA client. - clang-format-18 pass on touched files.
Enables zero-config native A&C with no node_map file: endpoint (from discovery) + auto_alarms: true is enough. The JSON/ROS param overlays the node-map YAML auto_alarms config block (param wins, mirroring auto_browse); explicit event_alarms mappings still win over auto-derivation at the poller. finalize_auto_alarms_overlay re-derives the default entity + parsed source NodeId and rebuilds entity_defs so the alarms App is discoverable with no map.
…edup Derive and store the auto fault_code/entity once at first observation of a ConditionId and deliver the stored values, so a condition's raise and clear always carry one code across the live, replay and reconcile paths. Fold the SourceNode into fault_code tiers 1/2 so same-named conditions on different sources stay distinct, and compare NodeIds canonically so equivalent spellings dedup and auto events already handled by an explicit event_alarms source do not double-fire.
a892c79 to
2d44396
Compare
bburda
approved these changes
Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #514
Summary
Zero-config native OPC-UA Alarms & Conditions: subscribe to the server's alarm events and surface them as faults with no per-alarm
event_alarmsmapping. Completes "find a PLC (#509) -> tree (#510) -> native faults, zero config". Default off.What's included
auto_alarms: baretrueor a map (source_node_iddefaulti=2253,entity_iddefault<component_id>_alarms,auto_clear,severity_bands,include/excludesubstring filters). Unknown keys warn.derive_auto_fault_code, pure):PLC_ALARM_<slug(ConditionName)>->slug(SourceName)->hash(SourceNode+EventType+Message). Message is folded into the hash tier because Siemens multiplexes every Program_Alarm of one FB through a single SourceNode with empty names, distinguished only by message.ConditionIdare dropped (Part 9 - only real Conditions carry one), so a "CPU not in RUN" system message on the Server EventNotifier raises no false fault.event_alarmsmappings resolve first;auto_alarmsadds one synthetic mapping-less source for the rest (no duplicate subscriptions).Tests
27 gtest cases (
test_node_map, newtest_opcua_poller): derivation tiers, message disambiguation, filters, severity bands, YAML parse, precedence, system-message filtering. Full suite green;clang-format-18clean.Hardware-tested
Real Siemens (
opc.tcp://192.168.1.10:4840, zeroevent_alarms): zero-config subscription live, real "CPU not in RUN" system message correctly filtered (no false fault). Full raise->derive->clear proven against a real OPC-UA session (repotest_alarm_server):Overpressure/SensorLost->PLC_ALARM_OVERPRESSURE(CRITICAL) /PLC_ALARM_SENSORLOST(INFO) with correct message/entity, cleared on clear. Fixed along the way:auto_clearmust force both Ack+Confirm gates (auto alarms have no acknowledge op); defaultentity_id=<component_id>_alarms(bare component_id collided with the Component entity in the SOVD merge).