feat(mtconnect): serve DATA_SET/TABLE as compact JSON strings; park EVENT enumerations (#650)
Parser captures Entry/Cell content; the observation index merges /sample deltas into the /current baseline (resetTriggered replaces, removed deletes, UNAVAILABLE drops). Fixture gains a TABLE item and periodic DATA_SET/TABLE churn. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+5
-2
@@ -27,7 +27,8 @@
|
||||
EVENT, controlled vocabulary -> String fixture_execution, fixture_avail, fixture_mode
|
||||
EVENT, free text -> String fixture_program, fixture_block (never fed),
|
||||
fixture_writeback (never fed; write target)
|
||||
EVENT, DATA_SET representation -> String fixture_varset
|
||||
EVENT, DATA_SET representation -> String fixture_varset (JSON object, #650)
|
||||
EVENT, TABLE representation -> String fixture_worktable (JSON object of objects, #650)
|
||||
CONDITION (any type) -> String + IsAlarm fixture_x_travel, fixture_logic
|
||||
|
||||
fixture_block is deliberately NEVER fed by the adapter, so the Agent reports it
|
||||
@@ -113,8 +114,10 @@
|
||||
legal value, and never fed by the adapter, so nothing races the assertion.
|
||||
-->
|
||||
<DataItem category="EVENT" id="fixture_writeback" name="Pwriteback" type="PROGRAM_COMMENT"/>
|
||||
<!-- DATA_SET representation demotes to String even though the Agent sends key=value entries. -->
|
||||
<!-- DATA_SET representation is served as a String carrying JSON (#650); the Agent sends key=value entries. -->
|
||||
<DataItem category="EVENT" id="fixture_varset" name="Pvars" type="VARIABLE" representation="DATA_SET"/>
|
||||
<!-- TABLE representation (#650): rows of cells, served as nested JSON. -->
|
||||
<DataItem category="EVENT" id="fixture_worktable" name="Ptab" type="WORK_OFFSET" representation="TABLE"/>
|
||||
<DataItem category="CONDITION" id="fixture_logic" name="Plogic" type="LOGIC_PROGRAM"/>
|
||||
</DataItems>
|
||||
</Path>
|
||||
|
||||
@@ -83,7 +83,8 @@ observation correlation key is visible.
|
||||
| `fixture_linenumber` | `Pline` | EVENT | LINE_NUMBER | | Int64 |
|
||||
| `fixture_program` | `Pprogram` | EVENT | PROGRAM | | String |
|
||||
| `fixture_block` | `Pblock` | EVENT | BLOCK | | String — **never fed ⇒ UNAVAILABLE** |
|
||||
| `fixture_varset` | `Pvars` | EVENT | VARIABLE | DATA_SET | String (structured ⇒ BadNotSupported) |
|
||||
| `fixture_varset` | `Pvars` | EVENT | VARIABLE | DATA_SET | String — JSON object (#650); `toolNumber`/`offsetX`/`offsetZ` stable, `cycle` moves every ~5 s |
|
||||
| `fixture_worktable` | `Ptab` | EVENT | WORK_OFFSET | TABLE | String — JSON object of objects (#650); `G54`/`G55` stable, `G56` row moves every ~5 s |
|
||||
| `fixture_logic` | `Plogic` | CONDITION | LOGIC_PROGRAM | | String + IsAlarm |
|
||||
| `fixture_asset_changed` / `fixture_asset_removed` | — | EVENT | ASSET_CHANGED / ASSET_REMOVED | | String |
|
||||
|
||||
|
||||
+17
-3
@@ -89,9 +89,12 @@ class Feed:
|
||||
[
|
||||
("Favail", "AVAILABLE"),
|
||||
("Pprogram", "OTOPCUA-FIXTURE.NC"),
|
||||
# DATA_SET representation: the Agent renders these as <Entry key=...> children,
|
||||
# which is precisely the wire shape the driver demotes to String / BadNotSupported.
|
||||
("Pvars", "toolNumber=7 offsetX=1.25 offsetZ=-0.5"),
|
||||
# DATA_SET / TABLE representations: the Agent renders these as <Entry key=...>
|
||||
# children, which the driver serves as a JSON String (#650). The three Pvars keys
|
||||
# are STABLE for the life of the fixture; the periodic feed only touches `cycle`
|
||||
# (Pvars) and the G56 row (Ptab).
|
||||
("Pvars", "toolNumber=7 offsetX=1.25 offsetZ=-0.5 cycle=0"),
|
||||
("Ptab", "G54={X=1.0 Y=2.0} G55={X=3.0} G56={X=0 Z=0.5}"),
|
||||
]
|
||||
)
|
||||
self.send_conditions()
|
||||
@@ -142,6 +145,17 @@ class Feed:
|
||||
[("Cmode", CONTROLLER_MODES[(self._ticks // 44) % len(CONTROLLER_MODES)])]
|
||||
)
|
||||
|
||||
# DATA_SET / TABLE churn (#650): every ~5 s change one key / one row so a live gate can
|
||||
# observe a /sample delta merge. The other keys / rows never change.
|
||||
if self._ticks % 20 == 0:
|
||||
n = self._ticks // 20
|
||||
self.send_observations(
|
||||
[
|
||||
("Pvars", f"cycle={n}"),
|
||||
("Ptab", f"G56={{X={n} Z=0.5}}"),
|
||||
]
|
||||
)
|
||||
|
||||
# Re-assert conditions occasionally so a late-attaching driver still sees them.
|
||||
if self._ticks % 60 == 0:
|
||||
self.send_conditions()
|
||||
|
||||
+34
@@ -448,6 +448,40 @@ public sealed class MTConnectAgentIntegrationTests(MTConnectAgentFixture agent)
|
||||
results.ShouldAllBe(r => r.Value == null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #650: the fixture's DATA_SET (<c>fixture_varset</c>) and TABLE (<c>fixture_worktable</c>) read as Good
|
||||
/// Strings carrying JSON. Only the keys the adapter never mutates are asserted exactly; the periodic
|
||||
/// <c>cycle</c> key / <c>G56</c> row are asserted present, because they move every ~5 s.
|
||||
/// </summary>
|
||||
[Fact(Timeout = TestTimeoutMs)]
|
||||
public async Task Read_serves_data_set_and_table_as_json_strings()
|
||||
{
|
||||
SkipIfDown();
|
||||
|
||||
await using var live = await LiveDriverAsync(
|
||||
[Tag("fixture_varset", DriverDataType.String), Tag("fixture_worktable", DriverDataType.String)]);
|
||||
|
||||
var results = await live.Driver.ReadAsync(["fixture_varset", "fixture_worktable"], Ct);
|
||||
|
||||
if (results.Any(r => r.StatusCode == BadNoCommunication))
|
||||
{
|
||||
Assert.Skip("The fixture's adapter is not feeding Pvars/Ptab (UNAVAILABLE). Is the adapter service running?");
|
||||
}
|
||||
|
||||
results.ShouldAllBe(r => r.StatusCode == Good);
|
||||
|
||||
using var set = JsonDocument.Parse(results[0].Value.ShouldBeOfType<string>());
|
||||
set.RootElement.GetProperty("toolNumber").GetInt32().ShouldBe(7);
|
||||
set.RootElement.GetProperty("offsetX").GetDouble().ShouldBe(1.25);
|
||||
set.RootElement.GetProperty("offsetZ").GetDouble().ShouldBe(-0.5);
|
||||
set.RootElement.TryGetProperty("cycle", out _).ShouldBeTrue();
|
||||
|
||||
using var table = JsonDocument.Parse(results[1].Value.ShouldBeOfType<string>());
|
||||
table.RootElement.GetProperty("G54").GetProperty("Y").GetDouble().ShouldBe(2.0);
|
||||
table.RootElement.GetProperty("G55").GetProperty("X").GetDouble().ShouldBe(3.0);
|
||||
table.RootElement.GetProperty("G56").GetProperty("Z").GetDouble().ShouldBe(0.5);
|
||||
}
|
||||
|
||||
// ---- subscriptions: the /sample long poll ----
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user