feat(opcua): populate order_code identity field from the device nameplate#499
feat(opcua): populate order_code identity field from the device nameplate#499mfaferek93 wants to merge 2 commits into
Conversation
Add a typed order_code field (AAS ManufacturerOrderCode) to AssetIdentity, distinct from model, and read it from the DI device OrderNumber node by BrowseName across namespaces. Verified on a Siemens CPU 1505SP F.
a4622ae to
276b66b
Compare
There was a problem hiding this comment.
Pull request overview
Adds an order_code field to the gateway’s asset identity model and wires it end-to-end (OPC UA device-info read → identity mapping/merge → REST JSON), including manifest/CSV import and expanded test coverage to validate cross-namespace OrderNumber reads and per-field provenance behavior.
Changes:
- Extend
AssetIdentitywith typedorder_code(AASManufacturerOrderCode) and include it in JSON serialization, merge, and inventory/manifest parsing. - Enhance the OPC UA plugin/client to read device nameplate (BuildInfo + DI nameplate + vendor
OrderNumber), cache it per session, and refresh after reconnect with trust-gated source precedence. - Add/expand unit + integration tests and docs to cover the new identity behavior and stability in CI.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tsan_suppressions.txt | Suppresses a benign TSan race triggered by open62541/glibc timezone lazy init. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/yaml_utils.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/vector_utils.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/typesupport.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/types.h | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/string_utils.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/msg_parser.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/message_reading.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_serialization/include/ros2_medkit_serialization/vendored/dynmsg/config.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/test/test_opcua_identity.cpp | New end-to-end identity test using the alarm-server fixture to validate device-info reads, trimming, and reconnect refresh. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/test/test_device_identity.cpp | New unit tests for DeviceInfo→AssetIdentity mapping and trust-gated identity precedence. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/test/integration/test_opcua_secured.test.py | Relaxes a wait deadline to reduce flakes on slow CI runners. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/test/fixtures/test_alarm_server/test_alarm_server.cpp | Extends the fixture with pinned BuildInfo, DI nameplate, vendor OrderNumber, and browse paging controls. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/src/opcua_plugin.cpp | Populates component identity from OPC UA device-info with per-session caching and trust-gated source. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/src/opcua_client.cpp | Adds per-session generation counter and implements BuildInfo + DI + vendor OrderNumber device-info reads (with BrowseNext continuation handling). |
| src/ros2_medkit_plugins/ros2_medkit_opcua/src/device_identity.cpp | New mapping/trust-gate implementation for OPC UA device identity. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/README.md | Documents identity population and trust-gated precedence for the OPC UA plugin. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/opcua_poller.hpp | Disables move operations (likely to avoid accidental moves of threaded state). |
| src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/opcua_plugin.hpp | Stores cached device identity + generation; disables copy/move. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/opcua_client.hpp | Adds connection_generation() API and DeviceInfo + read_device_info() interface; disables move. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/device_identity.hpp | New public header for DeviceInfo→AssetIdentity mapping and trust gating. |
| src/ros2_medkit_plugins/ros2_medkit_opcua/CMakeLists.txt | Adds new sources/tests, and marks open62541 headers as SYSTEM for warning hygiene; adjusts test timeouts. |
| src/ros2_medkit_gateway/test/test_manifest_parser.cpp | Adds coverage for manifest identity: parsing and provenance stamping. |
| src/ros2_medkit_gateway/test/test_discovery_handlers.cpp | Adds coverage for emitting x-medkit.identity and provenance in list/detail component endpoints. |
| src/ros2_medkit_gateway/test/test_asset_inventory.cpp | Adds CSV header aliasing tests for order_code/order_number mapping and provenance. |
| src/ros2_medkit_gateway/test/test_asset_identity.cpp | Adds serialization/round-trip and merge tests for the new order_code field and trust-gated plugin precedence. |
| src/ros2_medkit_gateway/src/vendored/tl_expected/include/tl/expected.hpp | Marks vendored header region with NOLINT to avoid lint noise. |
| src/ros2_medkit_gateway/src/http/handlers/discovery_handlers.cpp | Emits x-medkit.identity in component list/detail responses when present. |
| src/ros2_medkit_gateway/src/discovery/manifest/manifest_parser.cpp | Parses optional manifest identity: into AssetIdentity and stamps provenance. |
| src/ros2_medkit_gateway/src/core/discovery/manifest/asset_inventory.cpp | Maps order_code/order_number into structured identity + provenance via inventory parsing. |
| src/ros2_medkit_gateway/include/ros2_medkit_gateway/dto/x_medkit.hpp | Adds optional identity field to x-medkit DTO output. |
| src/ros2_medkit_gateway/include/ros2_medkit_gateway/core/discovery/models/asset_identity.hpp | Adds typed order_code field and includes it in JSON/empty/equality logic. |
| src/ros2_medkit_gateway/include/ros2_medkit_gateway/core/discovery/manifest/manifest_parser.hpp | Declares manifest identity parsing API. |
| src/ros2_medkit_gateway/include/ros2_medkit_gateway/core/discovery/manifest/asset_inventory.hpp | Extends inventory model/schema with order_code and alias documentation. |
| src/ros2_medkit_gateway/include/ros2_medkit_gateway/core/discovery/identity_merge.hpp | Updates merge precedence and adds order_code to merge field set. |
| docs/config/manifest-schema.rst | Documents manifest identity: block (needs update for order_code). |
| docs/api/rest.rst | Documents x-medkit.identity REST shape and provenance behavior. |
Comments suppressed due to low confidence (1)
src/ros2_medkit_plugins/ros2_medkit_opcua/src/opcua_client.cpp:792
- In create_subscription(), the newly created Subscription is copied into subscriptions, and the local
subis then destroyed when the function returns. Elsewhere in this file you rely on the open62541pp Subscription destructor / deleteSubscription semantics (see remove_subscriptions()), so copying here risks the stored subscription being torn down immediately (or double-deleted later). This should be moved into the container as before.
auto sub = impl_->client.createSubscription(params);
uint32_t sub_id = sub.subscriptionId();
std::lock_guard<std::mutex> sub_lock(impl_->sub_mutex);
impl_->subscriptions.push_back({sub, std::move(callback)});
There was a problem hiding this comment.
Docs not updated for the new order_code field. It is a distinct typed field now, so several places that enumerate the identity or column set are stale, and two still label model as the order code, which contradicts the code:
src/ros2_medkit_gateway/README.md, Asset Identity nameplate table: no row fororder_code/orderCode/ ManufacturerOrderCode, and themodelrow is still marked "(order code)".docs/config/manifest-schema.rst: theidentity:block, the identity-keys list, theassets:block, the alias list, and the CSV "canonical columns" list all omitorder_code/order_number; themodellines still say "model / order code".asset_inventory.hpp: theparse_asset_csvcanonical-names docstring and theasset_entry_to_componentidentity-map docstring both omitorder_code, but the code maps it.src/ros2_medkit_gateway/src/gateway_node.cpp: thediscovery.inventory.csv_pathparameter comment lists the columns withoutorder_code.
The E2E order-code fixture only padded the trailing edge with no internal space, so a whitespace-collapsing trim would not have failed any test. Use the real Siemens MLFB with leading + trailing pad and an internal space, and assert the read order_code keeps that space.
Adds a typed
order_codeidentity field and populates it from the OPC UA device nameplate.AssetIdentity: neworder_codefield (AASManufacturerOrderCode, distinct frommodel=ManufacturerProductDesignation), wired throughto_json(orderCode),from_json,empty(), equality, and the merge field set so it merges with per-field provenance.OrderNumbernameplate node by BrowseName across namespaces (vendors expose it outside the standard DI namespace), trimming fixed-width padding on that vendor field. Standard DI fields are still read raw (no regression).order_code/order_numberalias maps to the typed field.test_alarm_serverfixture now exposes a vendor-namespace, space-paddedOrderNumber, so the cross-namespace read and trim are covered by the identity tests.Closes #498.