Files
lmxopcua/docs/DataTypeMapping.md
Joseph Doherty 21e0fdd4cd Docs audit — fill gaps so the top-level docs/ reference matches shipped code
Audit of docs/ against src/ surfaced shipped features without current-reference
coverage (FOCAS CLI, Core.Scripting+VirtualTags, Core.ScriptedAlarms,
Core.AlarmHistorian), an out-of-date driver count + capability matrix, ADR-002's
virtual-tag dispatch not reflected in data-path docs, broken cross-references,
and OpcUaServerReqs declaring OPC-020..022 that were never scoped. This commit
closes all of those so operators + integrators can stay inside docs/ without
falling back to v2/implementation/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:42:42 -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 documented in docs/security.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