Files
lmxopcua/docs/DataTypeMapping.md
Joseph Doherty 985b7aba26 Doc refresh (task #202) — core architecture docs for multi-driver OtOpcUa
Rewrite seven core-architecture docs to match the shipped multi-driver platform.
The v1 single-driver LmxNodeManager framing is replaced with the Core +
capability-interface model — Galaxy is now one driver of seven, and each doc
points at the current class names + source paths.

What changed per file:
- OpcUaServer.md — OtOpcUaServer as StandardServer host; per-driver
  DriverNodeManager + CapabilityInvoker wiring; Config-DB-driven configuration
  (sp_PublishGeneration, DraftRevisionToken, Admin UI); Phase 6.2
  AuthorizationGate integration.
- AddressSpace.md — GenericDriverNodeManager.BuildAddressSpaceAsync walks
  ITagDiscovery.DiscoverAsync and streams DriverAttributeInfo through
  IAddressSpaceBuilder; CapturingBuilder registers alarm-condition sinks;
  per-driver NodeId schemes replace the fixed ns=1;s=ZB root.
- ReadWriteOperations.md — OnReadValue / OnWriteValue dispatch to
  IReadable.ReadAsync / IWritable.WriteAsync through CapabilityInvoker,
  honoring WriteIdempotentAttribute (#143); two-layer authorization
  (WriteAuthzPolicy + Phase 6.2 AuthorizationGate).
- Subscriptions.md — ISubscribable.SubscribeAsync/UnsubscribeAsync is the
  capability surface; STA-thread story is now Galaxy-specific (StaPump inside
  Driver.Galaxy.Host), other drivers are free-threaded.
- AlarmTracking.md — IAlarmSource is optional; AlarmSurfaceInvoker wraps
  Subscribe/Ack/Unsubscribe with fan-out by IPerCallHostResolver and the
  no-retry AlarmAcknowledge pipeline (#143); CapturingBuilder registers sinks
  at build time.
- DataTypeMapping.md — DriverDataType + SecurityClassification are the
  driver-agnostic enums; per-driver mappers (GalaxyProxyDriver inline,
  AbCipDataType, ModbusDriver, etc.); SecurityClassification is metadata only,
  ACL enforcement is at the server layer.
- IncrementalSync.md — IRediscoverable covers backend-change signals;
  sp_ComputeGenerationDiff + DiffViewer drive generation-level change
  detection; IDriver.ReinitializeAsync is the in-process recovery path.
2026-04-20 01:33:28 -04:00

5.1 KiB

Data Type Mapping

Data-type mapping is driver-defined. Each driver translates its native attribute metadata into two driver-agnostic enums from Core.AbstractionsDriverDataType (src/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverDataType.cs) and SecurityClassification (src/ZB.MOM.WW.OtOpcUa.Core.Abstractions/SecurityClassification.cs) — and populates the DriverAttributeInfo record it hands to IAddressSpaceBuilder.Variable(...). Core doesn't interpret the native types; it trusts the driver's translation.

DriverDataType → OPC UA built-in type

DriverNodeManager.MapDataType (src/ZB.MOM.WW.OtOpcUa.Server/OpcUa/DriverNodeManager.cs) is the single translation table for every driver:

DriverDataType OPC UA NodeId
Boolean DataTypeIds.Boolean (i=1)
Int32 DataTypeIds.Int32 (i=6)
Float32 DataTypeIds.Float (i=10)
Float64 DataTypeIds.Double (i=11)
String DataTypeIds.String (i=12)
DateTime DataTypeIds.DateTime (i=13)
anything else DataTypeIds.BaseDataType

The enum also carries Int16 / Int64 / UInt16 / UInt32 / UInt64 / Reference members for drivers that need them; the mapping table is extended as those types surface in actual drivers. Reference is the Galaxy-style attribute reference — it's encoded as an OPC UA String on the wire.

Per-driver mappers

Each driver owns its native → DriverDataType translation:

  • Galaxy ProxyGalaxyProxyDriver.MapDataType(int mxDataType) and MapSecurity(int mxSec) (inline in src/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Proxy/GalaxyProxyDriver.cs). The Galaxy mx_data_type integer is sent across the Host↔Proxy pipe and mapped on the Proxy side. Galaxy's full classic 16-entry table (Boolean / Integer / Float / Double / String / Time / ElapsedTime / Reference / Enumeration / Custom / InternationalizedString) is preserved but compressed into the seven-entry DriverDataType enum — ElapsedTimeFloat64, InternationalizedStringString, ReferenceReference, enumerations → Int32.
  • AB CIPsrc/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDataType.cs maps CIP tag type codes.
  • Modbussrc/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs maps register shapes (16-bit signed, 16-bit unsigned, 32-bit float, etc.) including the DirectLogic quirk table in DirectLogicAddress.cs.
  • S7 / AB Legacy / TwinCAT / FOCAS / OPC UA Client — each has its own inline mapper or *DataType.cs file per the same pattern.

The driver's mapping is authoritative — when a field type is ambiguous (a LREAL that could be bit-reinterpreted, a BCD counter, a string of a particular encoding), the driver decides the exposed OPC UA shape.

Array handling

DriverAttributeInfo.IsArray = true flips ValueRank = OneDimension on the generated BaseDataVariableState; scalars stay at ValueRank.Scalar. DriverAttributeInfo.ArrayDim carries the declared length. Writing element-by-element (OPC UA IndexRange) is a driver-level decision — see docs/ReadWriteOperations.md.

SecurityClassification — metadata, not ACL

SecurityClassification is driver-reported metadata only. Drivers never enforce write permissions themselves — the classification flows into the Server project where WriteAuthzPolicy.IsAllowed(classification, userRoles) (src/ZB.MOM.WW.OtOpcUa.Server/Security/WriteAuthzPolicy.cs) gates the write against the session's LDAP-derived roles, and (Phase 6.2) the AuthorizationGate + permission trie apply on top. This is the "ACL at server layer" invariant recorded in feedback_acl_at_server_layer.md.

The classification values mirror the v1 Galaxy model so existing Galaxy galaxies keep their published semantics:

SecurityClassification Required role Write-from-OPC-UA
FreeAccess yes (even anonymous)
Operate WriteOperate yes
Tune WriteTune yes
Configure WriteConfigure yes
SecuredWrite WriteOperate yes
VerifiedWrite WriteConfigure yes
ViewOnly no

Drivers whose backend has no notion of classification (Modbus, most PLCs) default every tag to FreeAccess or Operate; drivers whose backend does carry the notion (Galaxy, OPC UA Client relaying UserAccessLevel) translate it directly.

Historization

DriverAttributeInfo.IsHistorized = true flips AccessLevel.HistoryRead and Historizing = true on the variable. The driver must then implement IHistoryProvider for HistoryRead service calls to succeed; otherwise the node manager surfaces BadHistoryOperationUnsupported per request.

Key source files

  • src/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverDataType.cs — driver-agnostic type enum
  • src/ZB.MOM.WW.OtOpcUa.Core.Abstractions/SecurityClassification.cs — write-authz tier metadata
  • src/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverAttributeInfo.cs — per-attribute descriptor
  • src/ZB.MOM.WW.OtOpcUa.Server/OpcUa/DriverNodeManager.csMapDataType translation
  • src/ZB.MOM.WW.OtOpcUa.Server/Security/WriteAuthzPolicy.cs — classification-to-role policy
  • Per-driver mappers in each Driver.* project