Wire Galaxy security_classification to OPC UA AccessLevel (ReadOnly for SecuredWrite/VerifiedWrite/ViewOnly). Use deployed package chain for attribute queries to exclude undeployed attributes. Group primitive attributes under their parent variable node (merged Variable+Object). Add is_historized and is_alarm detection via HistoryExtension/AlarmExtension primitives. Implement OPC UA HistoryRead backed by Wonderware Historian Runtime database. Implement AlarmConditionState nodes driven by InAlarm with condition refresh support. Add historyread and alarms CLI commands for testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.jsonunderOpcUa: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 (defaultLmxOpcUa). - Server shall use the
OPCFoundation.NetStandard.Opc.Ua.ServerNuGet 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 = 1are created as FolderType nodes (organizational). - Objects where
is_area = 0are 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_nameis null or empty, fall back totag_nameas 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.sqlcreates one variable node under the matching object node (matched bygobject_id). - Variable node BrowseName and DisplayName are set to
attribute_name. - Variable node stores
full_tag_referenceas its runtime MXAccess address. - Variable node AccessLevel is set based on the attribute's
security_classificationper the mapping ingr/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/DownloadPathcorrectly translates to MXAccess referenceDelmiaReceiver_001.DownloadPath. - Translation uses the
tag_namestored 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_typevalue in the mapping table produces the correct OPC UA DataType NodeId on the variable node. - Unknown/unmapped
mx_data_typevalues 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 = 1produces ValueRank = 1 (OneDimension) and ArrayDimensions =[array_dimension].is_array = 0produces 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_dimensionis null or 0 whenis_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 viaOnWriteComplete()callback. - Write timeout: configurable, default 5 seconds. On timeout, log Warning and return Bad_Timeout.
- MXSTATUS_PROXY with
success = 0causes 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
OnDataChangecallback 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 rebuilt.
- Existing OPC UA client sessions are preserved — clients stay connected.
- Subscriptions for tags that still exist after rebuild continue to work.
- Subscriptions for tags that no longer exist receive a Bad_NodeIdUnknown status notification.
- Rebuild is logged at Information level with timing (duration).
Details
- Rebuild is a full replace, not an incremental diff. Re-query hierarchy and attributes, build new tree, swap atomically.
- During rebuild, reads/writes against the old address space may fail briefly. This is acceptable.
- New MXAccess subscriptions for new tags are established; removed tags are unsubscribed.
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.