Skip to content

ros2_medkit_opcua: add browse-based auto node_map generation #368

@mfaferek93

Description

@mfaferek93

Context

ros2_medkit_opcua (added in #365) requires a hand-written YAML node_map that maps OPC-UA node IDs to SOVD entities. For the tank demo this is fine (5 nodes, 1 PLC), but for a real industrial deployment a PLC can expose hundreds or thousands of tags across multiple namespaces, and writing the node map by hand is error-prone and time-consuming.

This issue tracks adding an opt-in browse mode that walks the OPC-UA address space and generates a starter node_map.yaml automatically. The integrator then edits the generated map to rename entities, group nodes under applications, set units, and mark writable nodes - far faster than writing from scratch.

Scope

Code changes

  1. Add OpcuaClient::browse_address_space(BrowseConfig) -> BrowseResult that walks the OPC-UA address space using open62541pp browse services. BrowseConfig controls:

    • Starting node (default: ObjectsFolder)
    • Depth limit
    • Namespace filter (include / exclude list)
    • Browse direction (forward / inverse / both)
    • Node class filter (Variable, Object, Method, etc.)
  2. Add NodeMapGenerator that takes a BrowseResult and produces a NodeMap YAML document:

    • Infers data_type from DataTypeId on each Variable node
    • Groups related nodes into apps by browse hierarchy (one app per Object node containing Variable children)
    • Derives data_name from BrowseName, sanitized to valid ROS 2 topic segments
    • Sets writable: true when the Variable has UserAccessLevel write bit
    • Emits a # TODO: comment per entry for fields the generator cannot determine (unit, min_value, max_value, alarm)
  3. Expose the generator in one of two ways (discussion needed):

    • Option A: new standalone CLI tool ros2_medkit_opcua_browse (argparse-style, ros2 run ros2_medkit_opcua browse --endpoint opc.tcp://... --output tank_nodes.yaml)
    • Option B: plugin parameter plugins.opcua.auto_browse: true - when set, the plugin browses on startup and writes the generated map next to the loaded config

Option A is cleaner for a one-shot developer workflow. Option B is less typing but commingles "generate config" with "run server" which is weird. Recommend Option A.

Configuration + plugin integration

  • Browse mode does not require the plugin to be running in gateway mode. It is a pure client-side OPC-UA browse operation.
  • The generator should produce YAML that is a valid input for the plugin's normal NodeMap::load so round-tripping works.

Tests

  1. Unit test for NodeMapGenerator with a mocked BrowseResult fixture.
  2. Integration test: browse OpenPLC tank demo and verify the generated YAML contains all 5 expected nodes with correct data types.
  3. Integration test: compare generated YAML against the handwritten tank_demo_nodes.yaml - most fields should match (with the expected TODO gaps for unit / alarm).

Documentation

  1. README.md - add Quickstart: auto-generate node map section showing the CLI usage.
  2. design/index.rst - document the browse strategy, filtering options, and the hierarchy-to-entity mapping heuristic.

Acceptance criteria

  • OpcuaClient::browse_address_space walks the address space with depth + namespace filters
  • NodeMapGenerator produces YAML that round-trips through NodeMap::load without errors
  • CLI tool available via ros2 run ros2_medkit_opcua browse ...
  • OpenPLC integration test generates a map matching the handwritten demo map (modulo documented gaps)
  • README and design doc updated

Out of scope for this issue

  • Full interactive "browse wizard" with prompts
  • Detection of OPC-UA Alarms & Conditions event types (use YAML alarm: block manually)
  • Automatic detection of unit, min_value, max_value from EngineeringUnits / EURange (nice-to-have, list as follow-up)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions