Merge b626-mtconnect (batch #626+)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+53
-20
@@ -230,10 +230,37 @@ Other status codes an observation can surface:
|
||||
|---|---|
|
||||
| `BadTypeMismatch` (`0x80740000`) | The Agent's text isn't a value of the tag's coerced type at all |
|
||||
| `BadOutOfRange` (`0x803C0000`) | The Agent reported a number the coerced type can't represent |
|
||||
| `BadNotSupported` (`0x803D0000`) | A shape this build doesn't materialize — a `TIME_SERIES` vector, or a structured `DATA_SET`/`TABLE` observation (deferred to P1.5; see [Known limitations](#known-limitations)) |
|
||||
| `BadDecodingError` (`0x80070000`) | A `TIME_SERIES` vector whose element count disagrees with the observation's own `sampleCount` (or the tag's fixed `ArrayDim`) — see [TIME_SERIES](#time_series-arrays-627) |
|
||||
| `BadNotSupported` (`0x803D0000`) | A shape this build doesn't materialize — a structured `DATA_SET`/`TABLE` observation (decided: stays unsupported, #627), or an array tag declared as anything other than Float64 |
|
||||
| `BadWaitingForInitialData` | Tag is authored but the Agent hasn't reported it yet |
|
||||
| `BadNodeIdUnknown` | DataItem id is neither authored nor ever observed |
|
||||
|
||||
### TIME_SERIES arrays (#627)
|
||||
|
||||
A `SAMPLE` DataItem with `representation="TIME_SERIES"` is served as a
|
||||
**variable-length `double[]`** (`ValueRank = 1`, no fixed `ArrayDimensions` —
|
||||
a live Agent never declares `sampleCount` on the DataItem, see Known
|
||||
limitations 4; a probe that does declare one yields a fixed `ArrayDim`).
|
||||
Parsing: the observation text is split on any whitespace, each token parsed
|
||||
invariant-culture. The result is **all-or-nothing** — one non-numeric /
|
||||
non-finite token is `BadTypeMismatch`, a literal `UNAVAILABLE` token or an
|
||||
empty body is `BadNoCommunication`, and a count that disagrees with the
|
||||
observation's own `sampleCount` attribute (or a fixed `ArrayDim`) is
|
||||
`BadDecodingError`. A partial vector is never served as Good. Only a
|
||||
**Float64** array tag is served; any other declared element type stays
|
||||
`BadNotSupported`. An authored tag needs `"isArray": true` and
|
||||
`"dataType": "Float64"` in its TagConfig (the AdminUI editor shows an advisory
|
||||
for any other array type; a browse-commit does not stamp `isArray`, so set it
|
||||
in the Tag modal's array section).
|
||||
|
||||
**Sample rate is not exposed on the served node.** The observation's
|
||||
`sampleRate` and `sampleCount` attributes are parsed (`MTConnectObservation`)
|
||||
but the OPC UA node has no description/property slot through `DriverAttributeInfo`,
|
||||
and a sibling property node would need a new address-space mechanism. Decision:
|
||||
document it — the rate is a property of the Agent's DataItem (`sampleRate` in
|
||||
`/probe`), read it there. **`DATA_SET` / `TABLE` stay `BadNotSupported`** (decision, #627): they are
|
||||
key/value maps with no scalar or array OPC UA shape that preserves the keys.
|
||||
|
||||
### Subscribe — the `/sample` pump
|
||||
|
||||
One shared `/sample` long-poll stream per driver instance (the Agent streams
|
||||
@@ -383,14 +410,24 @@ for anything beyond values-and-conditions.
|
||||
`GetHostStatuses()` fresh, and the result rides `DriverHealthChanged` to
|
||||
the AdminUI `/hosts` page (no SQL; the `DriverHostStatus` table was
|
||||
dropped). See `docs/Telemetry.md`.
|
||||
2. **`RawTagEntry.DeviceName` is accepted on the wire shape but ignored for
|
||||
routing.** One driver instance owns exactly one Agent client scoped by
|
||||
`MTConnectDriverOptions.DeviceName` (the top-level config key); the
|
||||
per-tag `RawTagEntry.DeviceName` field the v3 raw-tag identity carries is
|
||||
never read by the driver. Two `/raw` Devices authored under one MTConnect
|
||||
driver instance both resolve to the same one Agent connection. Real
|
||||
per-device routing (a client per device) would be a design change, not a
|
||||
bug fix.
|
||||
2. **One MTConnect driver instance = one Agent client = at most one `/raw`
|
||||
Device (#628, decision (b)).** The driver never reads
|
||||
`RawTagEntry.DeviceName`; every raw tag binds through the single Agent client
|
||||
scoped by `MTConnectDriverOptions.DeviceName`. The v3 raw tree always has a
|
||||
Device level (a new driver auto-creates `Device1`), and that single-Device
|
||||
layout is the normal, valid shape — its `DeviceConfig` is merged up into the
|
||||
driver config by `DriverDeviceConfigMerger` (this is how the endpoint lands).
|
||||
With **two or more** Devices under one MTConnect driver there is no merge-up
|
||||
and no routing, so which Device supplies the endpoint is ambiguous. That is
|
||||
now a **deploy-time `DraftValidator` error** (`MTConnectMultipleDevices`),
|
||||
and the AdminUI refuses to *create* the second Device
|
||||
(`RawTreeService.CreateDeviceAsync`, same message): "MTConnect binds all tags
|
||||
through one agent client per driver instance; author one MTConnect driver per
|
||||
agent device." Option (a), a client per Device, was rejected as a design
|
||||
change with no demand. A multi-device Agent is served by several driver
|
||||
instances, each with its own `DeviceName` scope. The driver's init-time
|
||||
Warning for differing `RawTagEntry.DeviceName` values stays as defence in
|
||||
depth for configs that predate the rule.
|
||||
|
||||
**The write path is the one place a device name is resolved per DataItem**,
|
||||
and it does not use `RawTagEntry.DeviceName` either: it reads the owning
|
||||
@@ -399,18 +436,13 @@ for anything beyond values-and-conditions.
|
||||
So `MTConnectDriverOptions.DeviceName` does double duty — request scope for
|
||||
reads/browse, fallback write target for writes.
|
||||
|
||||
**It is no longer silent (#613).** When the instance's raw tags carry two or
|
||||
more distinct non-blank `RawTagEntry.DeviceName` values, driver init logs one
|
||||
Warning naming them and the driver-level scope they collapse into. A single
|
||||
value is not reported (it is just the `/raw` Device's own name). Real
|
||||
per-device routing remains deferred.
|
||||
3. **Authoring only *warns* when a declared coercion type cannot hold the
|
||||
inferred one (#613).** The AdminUI tag editor (`MTConnectTagConfigModel.Warnings()`)
|
||||
compares the declared type with `MTConnectDataTypeInference` over the
|
||||
`/probe` metadata a browse-commit stamped (`mtCategory`/`mtType`), and shows an
|
||||
advisory line — it never blocks a save (the deploy gate does not refuse it) and
|
||||
is a no-op for hand-authored blobs with no stamped metadata. `representation`
|
||||
is not on the model, so TIME_SERIES arrays are not covered. Beyond that,
|
||||
is not on the model; TIME_SERIES arrays get their own `isArray` advisory (declare Float64). Beyond that,
|
||||
nothing validates a raw tag's declared OPC UA `DataType` against the
|
||||
blob's coercion type. The `/probe`-sourced inference
|
||||
(`MTConnectDataTypeInference`) narrows how often an author picks a wrong
|
||||
@@ -427,9 +459,11 @@ for anything beyond values-and-conditions.
|
||||
declare a `sampleCount`, is unrealistic on this point and exists only to
|
||||
pin the parsing rule itself.
|
||||
5. **CONDITION is a plain `String`, not a native Part 9 alarm** (see above).
|
||||
6. **`TIME_SERIES` arrays and structured `DATA_SET`/`TABLE` observations
|
||||
surface as `BadNotSupported`**, not an approximation — see the Quality
|
||||
mapping table.
|
||||
6. **Structured `DATA_SET`/`TABLE` observations surface as
|
||||
`BadNotSupported`**, not an approximation — see the Quality mapping table.
|
||||
`TIME_SERIES` is served as `double[]` (#627, see
|
||||
[TIME_SERIES arrays](#time_series-arrays-627)); the sample rate is not
|
||||
surfaced on the node.
|
||||
|
||||
## Deferred (not in this build)
|
||||
|
||||
@@ -447,8 +481,7 @@ for anything beyond values-and-conditions.
|
||||
- **P1.5 fast-follow** (design
|
||||
[§9](../plans/2026-07-15-mtconnect-driver-design.md#9-phasing--effort)):
|
||||
CONDITION → native OPC UA Part 9 alarms via `IAlarmSource` (the Galaxy
|
||||
native-alarm pattern); `TIME_SERIES` SAMPLE arrays materialized as real
|
||||
OPC UA arrays; EVENT controlled-vocabulary values → OPC UA enumerations.
|
||||
native-alarm pattern); EVENT controlled-vocabulary values → OPC UA enumerations.
|
||||
- **P2 (on demand): SHDR adapter ingest.** A `SourceMode: "Agent" | "Shdr"`
|
||||
switch that opens the raw pipe-delimited SHDR TCP socket directly. Loses
|
||||
auto-discovery (no device model without an Agent in front, so
|
||||
|
||||
Reference in New Issue
Block a user