Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged ros2_medkit_opcua at Robotics Stack Exchange
Package Summary
| Version | 0.7.0 |
| License | Apache-2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/selfpatch/ros2_medkit.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-09-13 |
| Dev Status | DEVELOPED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- mfaferek93
Authors
- mfaferek93
ros2_medkit_opcua
Gateway plugin that bridges OPC-UA capable PLCs (OpenPLC, Siemens S7, Beckhoff, Allen-Bradley, etc.) into the SOVD entity tree. Enables unified diagnostics for mixed ROS 2 + industrial PLC deployments through a single REST API, with PLC alarms routed to ros2_medkit_fault_manager and numeric PLC values optionally bridged to ROS 2 std_msgs/Float32 topics.
Follows the same plugin pattern as ros2_medkit_graph_provider: implements GatewayPlugin + IntrospectionProvider against the get_routes() plugin API, loaded at runtime by ros2_medkit_gateway via dlopen.
What it does
- Connects to any OPC-UA capable PLC server over
opc.tcp - Emits SOVD entities (area, component, apps) from a YAML-driven node map, or
discovers them automatically by browsing the live address space
(
auto_browse, see below) - zero node-map config required - Exposes PLC values as the
x-plc-datavendor collection - Ships read-only by default: the shipped binary contains no compiled path that writes a value or acknowledges a condition, and exports nothing from the OPC UA stack (see Read-only and write-capable builds)
- In a write-capable build, allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state, poll metrics and the build’s write surface via
x-plc-status - Maps threshold-based PLC alarms to SOVD faults on the owning entity
- Optionally publishes numeric PLC values to ROS 2
std_msgs/Float32topics
Architecture
OPC-UA (TCP :4840)
PLC Runtime <─────────────────────────> OPC-UA Plugin (.so)
IEC 61131-3 program │
Cyclic execution (100ms) │ Polls all configured nodes
Variables exposed as OPC-UA nodes │ Maps to SOVD entity tree
│ Alarm thresholds -> fault reporting
│
▼
ros2_medkit Gateway
REST API :8080
│
┌──────┴──────┐
│ │
SOVD REST Fleet Gateway
(direct) (aggregation)
│ │
Dashboard Multi-device view
The plugin connects to any PLC with an OPC-UA server over TCP. No ROS 2 dependency between the plugin and the PLC - communication is pure OPC-UA. The plugin is loaded by the gateway at runtime via dlopen() and registers vendor REST endpoints for PLC data access and control.
SOVD Entity Model
The plugin creates a hierarchical entity tree from a YAML node map configuration:
Area: plc_systems
└── Component: openplc_runtime
├── App: tank_process
│ Data: tank_level (mm), tank_temperature (C), tank_pressure (bar)
│ Faults: PLC_HIGH_TEMP, PLC_LOW_LEVEL, PLC_OVERPRESSURE
├── App: fill_pump
│ Data: pump_speed (%)
│ Operations: set_pump_speed
└── App: drain_valve
Data: valve_position (%)
Operations: set_valve_position
Asset identity (x-medkit.identity)
The plugin auto-populates the PLC component’s asset-identity nameplate from the
live server and serves it as x-medkit.identity on the component (see
docs/api/rest.rst for the JSON shape). Two sources are read, best-effort:
-
Server/ServerStatus/BuildInfo(present on every compliant server):ManufacturerName,ProductName,SoftwareVersion,BuildNumber(carried as theextra.buildNumberkey). - The OPC UA DI companion nameplate (
http://opcfoundation.org/UA/DI/, only when the server exposes that namespace):Manufacturer,Model,SerialNumber,HardwareRevision,SoftwareRevisionfrom the first device underObjects/DeviceSetthat carries identification properties. DI values are device-specific and win over the server-level BuildInfo per field.
The read happens once per session - on the first introspect after a connect - and is refreshed after every reconnect: the cached nameplate is tied to the OPC UA session, so a PLC reboot, firmware update, or device swap is picked up on the next discovery refresh instead of latching the first read forever.
Trust-gated precedence. How the live nameplate ranks against a
hand-authored manifest identity: block depends on whether the session
authenticates the server:
- Secured channel (
security_mode: Sign/SignAndEncrypt) andreject_untrusted: true: the component is tagged with the protocol sourceopcua, which outranksmanifestin the identity merge - an authenticated live read overrides stale manifest fields. - Anything else (
SecurityPolicy=None, orreject_untrusted: false/ accept-any cert): the nameplate is spoofable by a rogue endpoint, so the component keeps the genericpluginsource, which ranks belowmanifest- the read fills fields the operator left empty but never overrides them.
Per-field _provenance records opcua in both cases, so consumers always see
where a value was read even when it merged with low authority.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
0.7.0 (2026-08-27)
- Config-less discovery. A read-only network scan finds the OPC UA
server instead of requiring its endpoint up front
(#509),
auto_browsewalks the address space recursively and builds the SOVD tree from it (#510), and identity, writability and fault triggers are read from the device itself rather than declared in a node map (#544) - Native OPC UA Alarms and Conditions become medkit faults with no
configuration
(#511). Alarms
are routed to distinct faults by message substring
(#506),
severityis accepted as an alias inevent_alarms(#501), and events that are not conditions are dropped from the alarm path instead of being reported as alarms (#552) - Asset identity is populated from the device nameplate, including
order_code(#492, #499) -
PLC_COMMS_LOSTis raised when the connection to the PLC drops, so a lost link is a reported fault rather than an absence of data (#507) - Security and reconnect hardening: a secure connection profile, replay of alarm state after a reconnect, and correct handling of several simultaneous alarms (#485)
- Fixed a data race between the REST thread and the poll thread on the pending-report buffer that crashed the gateway with a SIGSEGV (#519, #521)
- Fault detection moved onto the shared
ros2_medkit_fault_detectionevaluator, and the node map enforces global fault-code uniqueness so two rules cannot claim the same code (#486) - This package is still excluded from the rosdistro binary release pending vendoring of open62541pp (#366)
- Contributors: \@bburda, \@mfaferek93
0.6.0 (2026-06-22)
- No functional changes; version bump for the coordinated 0.6.0 release.
- Contributors: \@bburda
0.5.0 (2026-06-08)
- Native OPC-UA Part 9
AlarmConditionTypeevent subscription. The plugin now subscribes to vendor-defined alarms (Siemens S7-1500Program_Alarm/ ProDiag, Beckhoff TF6100, CodeSys 3.5+, Rockwell via FactoryTalk Linx) and bridges each event into the SOVD fault lifecycle. Configured via a new top-levelevent_alarms:block in the node map YAML; mutually exclusive per entry with the existing threshold-basedalarmform. (issue #386) - New SOVD operations on entities that host alarm sources:
acknowledge_faultinvokes the inheritedAcknowledgemethod on the liveConditionId(i=9111, EventId tracked per Part 9 §5.7.3);confirm_faultinvokesConfirm(i=9113). Both accept an optionalcommentrendered asLocalizedTexton the server. -
OpcuaClientgainsadd_event_monitored_item/remove_event_monitored_item/call_methodand a generation counter that filters callbacks fired from defunct subscriptions after a reconnect. Heap-ownedEventCallbackContextresolves the open62541pp / raw-C lifetime hazard. - Header-only
AlarmStateMachinemappingEnabledState x ShelvingState x ActiveState x AckedState x ConfirmedState x BranchIdto SOVDCONFIRMED / HEALED / CLEARED / Suppressed. Full transition table documented indesign/index.rst. -
ConditionRefresh(Server method i=3875) is invoked on subscribe and on every reconnect, withRefreshStartEvent/RefreshEndEventbracketing tracked for diagnostics. - New
test_alarm_serverfixture (open62541-based, full namespace 0 + alarms enabled) emits AlarmConditionType events on stdin commands; integration testrun_alarm_tests.shruns in CI alongside the existing OpenPLC threshold suite. The fixture builds by default via the workspacecolcon build(gated onMEDKIT_OPCUA_BUILD_ALARM_SERVERwhich defaults to ON;ExternalProject_Addrebuilds open62541 withUA_NAMESPACE_ZERO=FULLand alarms ON, with a serial sub-build to dodge the upstream-jrace onnamespace0_generated.c). - New CTest wrapper
test_alarm_server_smokeboots the fixture on an ephemeral port and runs the asyncua smoke test against it; skips with CTest exit 77 (treated as pass) whenasyncuais not importable, so iterating on plugin code without the Python dependency does not fail the suite. - Contributors: \@mfaferek93, \@bburda
0.4.0 (2026-04-11)
- Initial release
-
OpcuaPluginimplementation ofGatewayPluginandIntrospectionProviderthat bridges OPC-UA capable PLCs into the
File truncated at 100 lines see the full file