Files
lmxopcua/docs/reqs/OpcUaServerReqs.md
Joseph Doherty 3c326e2d45 Replace full address space rebuild with incremental subtree sync
On Galaxy deploy changes, only the affected gobject subtrees are torn down
and rebuilt instead of destroying the entire address space. Unchanged nodes,
subscriptions, and alarm tracking continue uninterrupted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:23:11 -04:00

11 KiB

OPC UA Server — Component Requirements

Parent: HLR-001, HLR-002, HLR-004

OPC-001: Server Endpoint

The OPC UA server shall listen on a configurable TCP port (default 4840) using the OPC Foundation .NET Standard stack.

Acceptance Criteria

  • Server starts and accepts TCP connections on the configured port.
  • Port is read from appsettings.json under OpcUa:Port; defaults to 4840 if absent.
  • Endpoint URL format: opc.tcp://<hostname>:<port>/LmxOpcUa.
  • If the port is in use at startup, log an Error and fail to start (do not silently pick another port).
  • Security policy: None (no certificate validation). This is an internal plant-floor service.

Details

  • Configurable items: port (default 4840), endpoint path (default /LmxOpcUa), server application name (default LmxOpcUa).
  • Server shall use the OPCFoundation.NetStandard.Opc.Ua.Server NuGet package.
  • On startup, log the endpoint URL at Information level.

OPC-002: Address Space Structure

The server shall create folder nodes for areas and object nodes for automation objects, organized in the same parent-child hierarchy as the Galaxy.

Acceptance Criteria

  • The root folder node has BrowseName ZB (hardcoded Galaxy name).
  • Objects where is_area = 1 are created as FolderType nodes (organizational).
  • Objects where is_area = 0 are created as BaseObjectType nodes.
  • Parent-child relationships use Organizes references (for areas) and HasComponent references (for contained objects).
  • A client browsing Root → Objects → ZB → DEV → TestArea → TestMachine_001 → DelmiaReceiver sees the same structure as gr/layout.md.

Details

  • NodeIds use a string-based identifier scheme: ns=1;s=<tag_name> for object nodes, ns=1;s=<tag_name>.<attribute_name> for variable nodes.
  • Infrastructure objects (AppEngines, Platforms) are included in the tree but may have no variable children.
  • When contained_name is null or empty, fall back to tag_name as the BrowseName.

OPC-003: Variable Nodes for Attributes

Each user-defined attribute on a deployed object shall be represented as an OPC UA variable node under its parent object node.

Acceptance Criteria

  • Each row from attributes.sql creates one variable node under the matching object node (matched by gobject_id).
  • Variable node BrowseName and DisplayName are set to attribute_name.
  • Variable node stores full_tag_reference as its runtime MXAccess address.
  • Variable node AccessLevel is set based on the attribute's security_classification per the mapping in gr/data_type_mapping.md.
  • FreeAccess (0), Operate (1), Tune (4), Configure (5) → AccessLevel = CurrentRead | CurrentWrite (3).
  • SecuredWrite (2), VerifiedWrite (3), ViewOnly (6) → AccessLevel = CurrentRead (1).
  • Objects with no user-defined attributes still appear as object nodes with zero children.

Details

  • Security classification determines the OPC UA AccessLevel and UserAccessLevel attributes on each variable node. The OPC UA stack enforces read-only access for nodes with CurrentRead-only access level.
  • Attributes whose names start with _ are already filtered by the SQL query.

OPC-004: Browse Name Translation

Browse names shall use contained names (human-readable, scoped to parent). The server shall internally translate browse paths to tag_name references for MXAccess operations.

Acceptance Criteria

  • A variable node browsed as ZB/DEV/TestArea/TestMachine_001/DelmiaReceiver/DownloadPath correctly translates to MXAccess reference DelmiaReceiver_001.DownloadPath.
  • Translation uses the tag_name stored on the parent object node, not the browse path.
  • No runtime path parsing — the mapping is baked into each node at build time.

Details

  • Each variable node stores its full_tag_reference (e.g., DelmiaReceiver_001.DownloadPath) at address-space build time. Read/write operations use this stored reference directly.

OPC-005: Data Type Mapping

Variable nodes shall use OPC UA data types mapped from Galaxy mx_data_type values per the mapping in gr/data_type_mapping.md.

Acceptance Criteria

  • Every mx_data_type value in the mapping table produces the correct OPC UA DataType NodeId on the variable node.
  • Unknown/unmapped mx_data_type values default to String (i=12).
  • ElapsedTime (type 7) maps to Double representing seconds.

Details

  • Full mapping table in gr/data_type_mapping.md.
  • DateTime conversion: Galaxy may store local time; convert to UTC for OPC UA.
  • LocalizedText (type 15): use empty locale string with the text value.

OPC-006: Array Support

Attributes marked as arrays shall have ValueRank=1 and ArrayDimensions set to the attribute's array_dimension value.

Acceptance Criteria

  • is_array = 1 produces ValueRank = 1 (OneDimension) and ArrayDimensions = [array_dimension].
  • is_array = 0 produces ValueRank = -1 (Scalar) and no ArrayDimensions.
  • MXAccess reference for array attributes uses tag_name.attribute[] (whole array) format.

Details

  • Individual array element access (tag_name.attribute[n]) is not required for initial implementation. Whole-array read/write only.
  • If array_dimension is null or 0 when is_array = 1, log a Warning and default to ArrayDimensions = [0] (variable-length).

OPC-007: Read Operations

The server shall fulfill OPC UA Read requests by reading the corresponding tag value from MXAccess using the tag_name.AttributeName reference.

Acceptance Criteria

  • OPC UA Read request for a variable node results in a read via MXAccess using the node's stored full_tag_reference.
  • Returned value is converted from the COM variant to the OPC UA data type specified on the node.
  • OPC UA StatusCode reflects MXAccess quality: Good maps to Good, Bad/Uncertain map appropriately.
  • If MXAccess is not connected, return StatusCode = Bad_NotConnected.
  • Read timeout: configurable, default 5 seconds. On timeout, return Bad_Timeout.

Details

  • Prefer cached subscription-delivered values over on-demand reads to reduce COM round-trips.
  • If no subscription is active for the tag, perform an on-demand read (AddItem, AdviseSupervisory, wait for first OnDataChange, then UnAdvise/RemoveItem).
  • Concurrency: semaphore-limited to configurable max (default 10) concurrent MXAccess operations.

OPC-008: Write Operations

The server shall fulfill OPC UA Write requests by writing to the corresponding tag via MXAccess.

Acceptance Criteria

  • OPC UA Write request results in an MXAccess Write() call with completion confirmed via OnWriteComplete() callback.
  • Write timeout: configurable, default 5 seconds. On timeout, log Warning and return Bad_Timeout.
  • MXSTATUS_PROXY with success = 0 causes the OPC UA write to return Bad_InternalError with the detail message.
  • MXAccess errors 1008 (no permission), 1012 (secured write), 1013 (verified write) return Bad_UserAccessDenied.
  • Write to a non-existent tag returns Bad_NodeIdUnknown.
  • The server shall attempt to convert the written value to the expected Galaxy data type before passing to Write().

Details

  • Write uses security classification -1 (no security). Galaxy runtime handles security enforcement.
  • Write sequence: uses existing subscription handle if available, otherwise AddItem + AdviseSupervisory + Write + await OnWriteComplete + cleanup.
  • Concurrent write limit: same semaphore as reads (configurable, default 10).

OPC-009: Subscriptions

The server shall support OPC UA subscriptions by mapping them to MXAccess advisory subscriptions and forwarding data change notifications.

Acceptance Criteria

  • OPC UA CreateMonitoredItems results in MXAccess AdviseSupervisory() subscriptions for the requested tags.
  • Data changes from OnDataChange callback are forwarded as OPC UA notifications to all subscribed clients.
  • Shared subscriptions: if two OPC UA clients subscribe to the same tag, only one MXAccess subscription exists (ref-counted).
  • Last subscriber unsubscribing triggers UnAdvise/RemoveItem on the MXAccess side.
  • After MXAccess reconnect, all active MXAccess subscriptions are re-established automatically.

Details

  • Publishing interval from the OPC UA subscription request is honored on the OPC UA side; MXAccess delivers changes as fast as it receives them.
  • OPC UA quality mapping from MXAccess quality integers: 192+ = Good, 64-191 = Uncertain, 0-63 = Bad.
  • OnDataChange with MXSTATUS_PROXY failure: deliver notification with Bad quality to subscribed clients.

OPC-010: Address Space Rebuild

When a Galaxy deployment change is detected, the server shall rebuild the address space without dropping existing OPC UA client connections where possible.

Acceptance Criteria

  • When Galaxy Repository detects a deployment change, the OPC UA address space is updated.
  • Only changed gobject subtrees are torn down and rebuilt; unchanged nodes, subscriptions, and alarm tracking remain intact.
  • Existing OPC UA client sessions are preserved — clients stay connected.
  • Subscriptions for tags on unchanged objects continue to work without interruption.
  • Subscriptions for tags that no longer exist receive a Bad_NodeIdUnknown status notification.
  • Sync is logged at Information level with the number of changed gobjects.

Details

  • Uses incremental subtree sync: compares previous hierarchy+attributes with new, identifies changed gobject IDs, expands to include child subtrees, tears down only affected subtrees, and rebuilds them.
  • First build (no cached state) performs a full build.
  • If no changes are detected, the sync is a no-op (logged and skipped).
  • Alarm tracking and MXAccess subscriptions for unchanged objects are not disrupted.
  • Falls back to full rebuild behavior if the entire hierarchy changes.

OPC-011: Server Diagnostics Node

The server shall expose a ServerStatus node under the standard OPC UA Server object with ServerState, CurrentTime, and StartTime. This is required by the OPC UA specification for compliant servers.

Acceptance Criteria

  • ServerState reports Running during normal operation.
  • CurrentTime returns the server's current UTC time.
  • StartTime returns the UTC time when the service started.

OPC-012: Namespace Configuration

The server shall register a namespace URI at namespace index 1. All application-specific NodeIds shall use this namespace.

Acceptance Criteria

  • Namespace URI: urn:ZB:LmxOpcUa (Galaxy name is configurable).
  • All object and variable NodeIds created from Galaxy data use namespace index 1.
  • Standard OPC UA nodes remain in namespace 0.

OPC-013: Session Management

The server shall support multiple concurrent OPC UA client sessions.

Acceptance Criteria

  • Maximum concurrent sessions: configurable, default 100.
  • Session timeout: configurable, default 30 minutes of inactivity.
  • Expired sessions are cleaned up and their subscriptions removed.
  • Session count is reported to the status dashboard.