Renames all 11 projects (5 src + 6 tests), the .slnx solution file, all source-file namespaces, all axaml namespace references, and all v1 documentation references in CLAUDE.md and docs/*.md (excluding docs/v2/ which is already in OtOpcUa form). Also updates the TopShelf service registration name from "LmxOpcUa" to "OtOpcUa" per Phase 0 Task 0.6.
Preserves runtime identifiers per Phase 0 Out-of-Scope rules to avoid breaking v1/v2 client trust during coexistence: OPC UA `ApplicationUri` defaults (`urn:{GalaxyName}:LmxOpcUa`), server `EndpointPath` (`/LmxOpcUa`), `ServerName` default (feeds cert subject CN), `MxAccessConfiguration.ClientName` default (defensive — stays "LmxOpcUa" for MxAccess audit-trail consistency), client OPC UA identifiers (`ApplicationName = "LmxOpcUaClient"`, `ApplicationUri = "urn:localhost:LmxOpcUaClient"`, cert directory `%LocalAppData%\LmxOpcUaClient\pki\`), and the `LmxOpcUaServer` class name (class rename out of Phase 0 scope per Task 0.5 sed pattern; happens in Phase 1 alongside `LmxNodeManager → GenericDriverNodeManager` Core extraction). 23 LmxOpcUa references retained, all enumerated and justified in `docs/v2/implementation/exit-gate-phase-0.md`.
Build clean: 0 errors, 30 warnings (lower than baseline 167). Tests at strict improvement over baseline: 821 passing / 1 failing vs baseline 820 / 2 (one flaky pre-existing failure passed this run; the other still fails — both pre-existing and unrelated to the rename). `Client.UI.Tests`, `Historian.Aveva.Tests`, `Client.Shared.Tests`, `IntegrationTests` all match baseline exactly. Exit gate compliance results recorded in `docs/v2/implementation/exit-gate-phase-0.md` with all 7 checks PASS or DEFERRED-to-PR-review (#7 service install verification needs Windows service permissions on the reviewer's box).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.5 KiB
Data Type Mapping
MxDataTypeMapper and SecurityClassificationMapper translate Galaxy attribute metadata into OPC UA variable node properties. These mappings determine how Galaxy runtime values are represented to OPC UA clients and whether clients can write to them.
mx_data_type to OPC UA Type Mapping
Each Galaxy attribute carries an mx_data_type integer that identifies its data type. MxDataTypeMapper.MapToOpcUaDataType maps these to OPC UA built-in type NodeIds:
| mx_data_type | Galaxy type | OPC UA type | NodeId | CLR type |
|---|---|---|---|---|
| 1 | Boolean | Boolean | i=1 | bool |
| 2 | Integer | Int32 | i=6 | int |
| 3 | Float | Float | i=10 | float |
| 4 | Double | Double | i=11 | double |
| 5 | String | String | i=12 | string |
| 6 | Time | DateTime | i=13 | DateTime |
| 7 | ElapsedTime | Double | i=11 | double |
| 8 | Reference | String | i=12 | string |
| 13 | Enumeration | Int32 | i=6 | int |
| 14 | Custom | String | i=12 | string |
| 15 | InternationalizedString | LocalizedText | i=21 | string |
| 16 | Custom | String | i=12 | string |
| other | Unknown | String | i=12 | string |
Unknown types default to String. This is a safe fallback because MXAccess delivers values as COM VARIANT objects, and string serialization preserves any value that does not have a direct OPC UA counterpart.
Why ElapsedTime maps to Double
Galaxy ElapsedTime (mx_data_type 7) represents a duration/timespan. OPC UA has no native TimeSpan type. The OPC UA specification defines a Duration type alias (NodeId i=290) that is semantically a Double representing milliseconds, but the simpler approach is to map directly to Double (i=11) representing seconds. This avoids ambiguity about whether the value is in seconds or milliseconds and matches how the Galaxy runtime exposes elapsed time values through MXAccess.
Array Handling
Galaxy attributes with is_array = 1 in the repository are exposed as one-dimensional OPC UA array variables.
ValueRank
The ValueRank property on the OPC UA variable node indicates the array dimensionality:
is_array |
ValueRank | Constant |
|---|---|---|
| 0 | -1 | ValueRanks.Scalar |
| 1 | 1 | ValueRanks.OneDimension |
ArrayDimensions
When ValueRank = 1, the ArrayDimensions property is set to a single-element ReadOnlyList<uint> containing the declared array length from array_dimension:
if (attr.IsArray && attr.ArrayDimension.HasValue)
{
variable.ArrayDimensions = new ReadOnlyList<uint>(
new List<uint> { (uint)attr.ArrayDimension.Value });
}
The array_dimension value is extracted from the mx_value binary column in the Galaxy database (bytes 13-16, little-endian int32).
NodeId for array variables
Array variables use a NodeId without the [] suffix. The full_tag_reference stored internally for MXAccess addressing retains the [] (e.g., MESReceiver_001.MoveInPartNumbers[]), but the OPC UA NodeId strips it to ns=1;s=MESReceiver_001.MoveInPartNumbers.
Security Classification to AccessLevel Mapping
Galaxy attributes carry a security_classification value that controls write permissions. SecurityClassificationMapper.IsWritable determines the OPC UA AccessLevel:
| security_classification | Galaxy level | OPC UA AccessLevel | Writable |
|---|---|---|---|
| 0 | FreeAccess | CurrentReadOrWrite | Yes |
| 1 | Operate | CurrentReadOrWrite | Yes |
| 2 | SecuredWrite | CurrentRead | No |
| 3 | VerifiedWrite | CurrentRead | No |
| 4 | Tune | CurrentReadOrWrite | Yes |
| 5 | Configure | CurrentReadOrWrite | Yes |
| 6 | ViewOnly | CurrentRead | No |
Most attributes default to Operate (1). The mapper treats SecuredWrite, VerifiedWrite, and ViewOnly as read-only because the OPC UA server does not implement the Galaxy's multi-level authentication model. Allowing writes to SecuredWrite or VerifiedWrite attributes without proper verification would bypass Galaxy security.
For historized attributes, AccessLevels.HistoryRead is added to the access level via bitwise OR, enabling OPC UA history read requests when an IHistorianDataSource is configured via the runtime-loaded historian plugin.
Key source files
src/ZB.MOM.WW.OtOpcUa.Host/Domain/MxDataTypeMapper.cs-- Type and CLR mappingsrc/ZB.MOM.WW.OtOpcUa.Host/Domain/SecurityClassificationMapper.cs-- Write access mappinggr/data_type_mapping.md-- Reference documentation for the full mapping table