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.
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.Abstractions — DriverDataType (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 Proxy —
GalaxyProxyDriver.MapDataType(int mxDataType)andMapSecurity(int mxSec)(inline insrc/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Proxy/GalaxyProxyDriver.cs). The Galaxymx_data_typeinteger 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-entryDriverDataTypeenum —ElapsedTime→Float64,InternationalizedString→String,Reference→Reference, enumerations →Int32. - AB CIP —
src/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDataType.csmaps CIP tag type codes. - Modbus —
src/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.csmaps register shapes (16-bit signed, 16-bit unsigned, 32-bit float, etc.) including the DirectLogic quirk table inDirectLogicAddress.cs. - S7 / AB Legacy / TwinCAT / FOCAS / OPC UA Client — each has its own inline mapper or
*DataType.csfile 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 enumsrc/ZB.MOM.WW.OtOpcUa.Core.Abstractions/SecurityClassification.cs— write-authz tier metadatasrc/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverAttributeInfo.cs— per-attribute descriptorsrc/ZB.MOM.WW.OtOpcUa.Server/OpcUa/DriverNodeManager.cs—MapDataTypetranslationsrc/ZB.MOM.WW.OtOpcUa.Server/Security/WriteAuthzPolicy.cs— classification-to-role policy- Per-driver mappers in each
Driver.*project