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.
This commit is contained in:
@@ -1,84 +1,65 @@
|
||||
# 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.
|
||||
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.
|
||||
|
||||
## mx_data_type to OPC UA Type Mapping
|
||||
## DriverDataType → OPC UA built-in type
|
||||
|
||||
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:
|
||||
`DriverNodeManager.MapDataType` (`src/ZB.MOM.WW.OtOpcUa.Server/OpcUa/DriverNodeManager.cs`) is the single translation table for every driver:
|
||||
|
||||
| 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` |
|
||||
| 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` |
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Why ElapsedTime maps to Double
|
||||
## Per-driver mappers
|
||||
|
||||
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.
|
||||
Each driver owns its native → `DriverDataType` translation:
|
||||
|
||||
## Array Handling
|
||||
- **Galaxy Proxy** — `GalaxyProxyDriver.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 — `ElapsedTime` → `Float64`, `InternationalizedString` → `String`, `Reference` → `Reference`, enumerations → `Int32`.
|
||||
- **AB CIP** — `src/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDataType.cs` maps CIP tag type codes.
|
||||
- **Modbus** — `src/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.
|
||||
|
||||
Galaxy attributes with `is_array = 1` in the repository are exposed as one-dimensional OPC UA array variables.
|
||||
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.
|
||||
|
||||
### ValueRank
|
||||
## Array handling
|
||||
|
||||
The `ValueRank` property on the OPC UA variable node indicates the array dimensionality:
|
||||
`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`.
|
||||
|
||||
| `is_array` | ValueRank | Constant |
|
||||
|:---:|:---------:|----------|
|
||||
| 0 | -1 | `ValueRanks.Scalar` |
|
||||
| 1 | 1 | `ValueRanks.OneDimension` |
|
||||
## SecurityClassification — metadata, not ACL
|
||||
|
||||
### ArrayDimensions
|
||||
`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`.
|
||||
|
||||
When `ValueRank = 1`, the `ArrayDimensions` property is set to a single-element `ReadOnlyList<uint>` containing the declared array length from `array_dimension`:
|
||||
The classification values mirror the v1 Galaxy model so existing Galaxy galaxies keep their published semantics:
|
||||
|
||||
```csharp
|
||||
if (attr.IsArray && attr.ArrayDimension.HasValue)
|
||||
{
|
||||
variable.ArrayDimensions = new ReadOnlyList<uint>(
|
||||
new List<uint> { (uint)attr.ArrayDimension.Value });
|
||||
}
|
||||
```
|
||||
| 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 |
|
||||
|
||||
The `array_dimension` value is extracted from the `mx_value` binary column in the Galaxy database (bytes 13-16, little-endian int32).
|
||||
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.
|
||||
|
||||
### NodeId for array variables
|
||||
## Historization
|
||||
|
||||
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.
|
||||
`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.Host/Domain/MxDataTypeMapper.cs` -- Type and CLR mapping
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/SecurityClassificationMapper.cs` -- Write access mapping
|
||||
- `gr/data_type_mapping.md` -- Reference documentation for the full mapping table
|
||||
- `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.cs` — `MapDataType` translation
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Server/Security/WriteAuthzPolicy.cs` — classification-to-role policy
|
||||
- Per-driver mappers in each `Driver.*` project
|
||||
|
||||
Reference in New Issue
Block a user