Package Summary
| Version | 0.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93
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.6.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-07-03 |
| 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
- Exposes PLC values as the
x-plc-datavendor collection - Allows writing setpoints via
x-plc-operationswith type-aware coercion and range validation - Reports the connection state and poll metrics 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.
Note: identity (model, serial number, firmware/software versions) is served
unauthenticated on the default gateway configuration (auth.enabled defaults
to false) like every other discovery resource - it is a device fingerprint,
useful for reconnaissance. Enable gateway authentication (auth.enabled: true)
or front the API with an authenticating proxy to gate access to it.
File truncated at 100 lines see the full file
Changelog for package ros2_medkit_opcua
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 SOVD entity tree - REST endpoints via the new
get_routes()plugin API:x-plc-data,x-plc-operations,x-plc-status - Vendor capabilities registered per entity - only PLC-backed apps and
the PLC runtime component advertise the
x-plc-*endpoints - Full OPC 10000-6 section 5.3.1.10 node identifier support (
i=numeric,s=string,g=GUID,b=opaque ByteString); example node maps for OpenPLC, Siemens S7-1500 TIA Portal, Beckhoff TwinCAT 3, Allen-Bradley via Kepware and KUKA KR C5 -
NodeMapdriven by YAML configuration - same binary serves any OPC-UA compliant server by changing the node map file - Deterministic entity ordering in
IntrospectionResultoutput (entries sorted by id) - Threshold-based PLC alarm detection routed to SOVD faults via
ros2_medkit_msgsservicesReportFault/ClearFault - Optional bridging of numeric PLC values to ROS 2
std_msgs/Float32topics fromset_context() - Type-aware writes with per-node range validation
- Robust connection-loss detection: all three OPC-UA client paths
(
read_value,read_values,write_value) mark the connection as dropped on terminal status codes soOpcuaPollerreconnect kicks in without stalling - Polling mode (default) and OPC-UA subscription mode, backed by
open62541ppv0.16.0 - Integration test suite against an OpenPLC IEC 61131-3 tank demo container
- Contributors: \@mfaferek93