# 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`:

```csharp
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 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
