docs(dcl): update protocol and type mapping docs to reflect v2 TypedValue and SDK integration
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# LmxProxy Protocol Specification
|
||||
|
||||
> **Note:** This specification reflects the v2 protocol with native `TypedValue` support. The original v1 string-based protocol (string values, string quality) has been replaced.
|
||||
|
||||
The LmxProxy protocol is a gRPC-based SCADA read/write interface for bridging ScadaLink's Data Connection Layer to devices via an intermediary proxy server (LmxProxy). The proxy translates LmxProxy protocol operations into backend device calls (e.g., OPC UA). All communication uses HTTP/2 gRPC with Protocol Buffers.
|
||||
|
||||
## Service Definition
|
||||
@@ -105,37 +107,61 @@ The core data structure for all read and subscription results:
|
||||
|
||||
```
|
||||
VtqMessage {
|
||||
tag: string // Tag address
|
||||
value: string // Value encoded as string (see Value Encoding)
|
||||
timestamp_utc_ticks: int64 // UTC DateTime.Ticks (100ns intervals since 0001-01-01)
|
||||
quality: string // "Good", "Uncertain", or "Bad"
|
||||
tag: string // Tag address
|
||||
value: TypedValue // Native typed value (protobuf oneof)
|
||||
timestamp_utc_ticks: int64 // UTC DateTime.Ticks (100ns intervals since 0001-01-01)
|
||||
quality: QualityCode // OPC UA status code + symbolic name
|
||||
}
|
||||
```
|
||||
|
||||
### Value Encoding
|
||||
### TypedValue
|
||||
|
||||
All values are transmitted as strings on the wire. Both client and server use the same parsing order:
|
||||
Values are transmitted as native types via a protobuf `oneof`:
|
||||
|
||||
| Wire String | Parsed Type | Example |
|
||||
|-------------|------------|---------|
|
||||
| Numeric (double-parseable) | `double` | `"42.5"` → `42.5` |
|
||||
| `"true"` / `"false"` (case-insensitive) | `bool` | `"True"` → `true` |
|
||||
| Everything else | `string` | `"Running"` → `"Running"` |
|
||||
| Empty string | `null` | `""` → `null` |
|
||||
| Oneof Variant | Proto Type | .NET Type |
|
||||
|---|---|---|
|
||||
| `bool_value` | `bool` | `bool` |
|
||||
| `int32_value` | `int32` | `int` |
|
||||
| `int64_value` | `int64` | `long` |
|
||||
| `float_value` | `float` | `float` |
|
||||
| `double_value` | `double` | `double` |
|
||||
| `string_value` | `string` | `string` |
|
||||
| `bytes_value` | `bytes` | `byte[]` |
|
||||
| `datetime_value` | `int64` | `DateTime` (UTC ticks) |
|
||||
| `array_value` | `ArrayValue` | See below |
|
||||
|
||||
For write operations, values are converted to strings via `.ToString()` before transmission.
|
||||
### ArrayValue
|
||||
|
||||
Arrays and lists are JSON-serialized (e.g., `[1,2,3]`).
|
||||
`ArrayValue` contains typed sub-arrays via a protobuf `oneof`:
|
||||
|
||||
| Sub-array | Element Type |
|
||||
|---|---|
|
||||
| `BoolArray` | `repeated bool` |
|
||||
| `Int32Array` | `repeated int32` |
|
||||
| `Int64Array` | `repeated int64` |
|
||||
| `FloatArray` | `repeated float` |
|
||||
| `DoubleArray` | `repeated double` |
|
||||
| `StringArray` | `repeated string` |
|
||||
|
||||
> **Note:** `DateTime` arrays are not natively supported in the proto — they are serialized as `Int64Array` (UTC ticks) by the Host.
|
||||
|
||||
The ScadaLink adapter normalizes `ArrayValue` objects to comma-separated display strings at the adapter boundary (see [Component-DataConnectionLayer.md](Component-DataConnectionLayer.md#value-serialization)).
|
||||
|
||||
### Value Encoding (v1 — deprecated)
|
||||
|
||||
The v1 protocol transmitted all values as strings with client-side parsing (`double.TryParse`, `bool.TryParse`). This has been replaced by native `TypedValue`. The v1 heuristics are no longer used.
|
||||
|
||||
### Quality Codes
|
||||
|
||||
Quality is transmitted as a case-insensitive string:
|
||||
Quality is transmitted as a `QualityCode` enum with OPC UA status code semantics:
|
||||
|
||||
| Wire Value | Meaning | OPC UA Status Code |
|
||||
|-----------|---------|-------------------|
|
||||
| `"Good"` | Value is reliable | `0x00000000` (StatusCode == 0) |
|
||||
| `"Uncertain"` | Value may not be current | Non-zero, high bit clear |
|
||||
| `"Bad"` | Value is unreliable or unavailable | High bit set (`0x80000000`) |
|
||||
| QualityCode | Meaning | OPC UA Mapping |
|
||||
|---|---|---|
|
||||
| Good | Value is reliable | StatusCode high bits clear |
|
||||
| Uncertain | Value may not be current | Non-zero, high bit clear |
|
||||
| Bad | Value is unreliable or unavailable | High bit set (`0x80000000`) |
|
||||
|
||||
The SDK provides `IsGood()`, `IsUncertain()`, and `IsBad()` extension methods on the `Quality` enum. The adapter maps these to ScadaLink's `QualityCode`.
|
||||
|
||||
A null or missing VTQ message is treated as Bad quality with null value and current UTC timestamp.
|
||||
|
||||
@@ -187,7 +213,7 @@ Batch reads are **partially successful** — individual tags may have Bad qualit
|
||||
WriteRequest {
|
||||
session_id: string
|
||||
tag: string
|
||||
value: string // Value as string (parsed server-side)
|
||||
value: TypedValue // Native typed value (see TypedValue)
|
||||
}
|
||||
|
||||
WriteResponse {
|
||||
@@ -196,12 +222,14 @@ WriteResponse {
|
||||
}
|
||||
```
|
||||
|
||||
The client adapter's `ToTypedValue` method converts `object?` values to the appropriate `TypedValue` variant before transmission. See [Component-DataConnectionLayer.md](Component-DataConnectionLayer.md#value-serialization) for the mapping table.
|
||||
|
||||
### WriteBatch (Multiple Tags)
|
||||
|
||||
```
|
||||
WriteItem {
|
||||
tag: string
|
||||
value: string
|
||||
value: TypedValue
|
||||
}
|
||||
|
||||
WriteResult {
|
||||
@@ -231,9 +259,9 @@ A compound operation: write values, then poll a flag tag until it matches an exp
|
||||
```
|
||||
WriteBatchAndWaitRequest {
|
||||
session_id: string
|
||||
items: repeated WriteItem // Values to write
|
||||
items: repeated WriteItem // Values to write (TypedValue)
|
||||
flag_tag: string // Tag to poll after writes
|
||||
flag_value: string // Expected value (string comparison)
|
||||
flag_value: TypedValue // Expected value (typed comparison)
|
||||
timeout_ms: int32 // Timeout in ms (default 5000 if ≤ 0)
|
||||
poll_interval_ms: int32 // Poll interval in ms (default 100 if ≤ 0)
|
||||
}
|
||||
@@ -250,7 +278,7 @@ WriteBatchAndWaitResponse {
|
||||
**Behavior:**
|
||||
1. All writes execute first. If any write fails, the operation returns immediately with `success=false`.
|
||||
2. If writes succeed, polls `flag_tag` at `poll_interval_ms` intervals.
|
||||
3. Compares `readResult.Value?.ToString() == flag_value` (case-sensitive string comparison).
|
||||
3. Compares the read result's `TypedValue` against `flag_value`.
|
||||
4. If flag matches before timeout: `success=true`, `flag_reached=true`.
|
||||
5. If timeout expires: `success=true`, `flag_reached=false` (timeout is not an error).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user