f8f8eb9fb8
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
130 lines
8.4 KiB
XML
130 lines
8.4 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
MTConnect integration-test device model — the canned Devices.xml the fixture Agent
|
|
(mtconnect/agent) serves from /probe.
|
|
|
|
This model is authored to exercise every branch of MTConnectDataTypeInference.Infer,
|
|
and to cover the two things the driver's canned unit-test fixtures structurally CANNOT:
|
|
|
|
1. UPPER_SNAKE type spellings. A probe document writes DataItem@type in UPPER_SNAKE
|
|
(PART_COUNT, ROTARY_VELOCITY); a streams document names the same concept in
|
|
PascalCase (PartCount, RotaryVelocity). The inference is separator-insensitive to
|
|
bridge the two, and a real Agent is the only thing that proves it — an earlier
|
|
revision typed PART_COUNT as String and passed every unit test.
|
|
|
|
2. name != id on every named DataItem. The hand-authored unit fixtures use ids like
|
|
"dev1_pos" that double as browse names; on a real machine tool the two always
|
|
differ, and DriverAttributeInfo.FullName must be the *id* (the observation
|
|
correlation key) while the browse name is the *name*. Every DataItem below that
|
|
carries a name deliberately gives it a value unequal to its id.
|
|
|
|
Inference coverage map (category / type / representation -> expected DriverDataType):
|
|
|
|
SAMPLE, units -> Float64 fixture_x_pos, fixture_x_load, fixture_c_speed
|
|
SAMPLE, TIME_SERIES, sampleCount -> Float64[] array fixture_c_temp_series
|
|
EVENT, PART_COUNT -> Int64 fixture_partcount (the regression case)
|
|
EVENT, LINE_NUMBER -> Int64 fixture_linenumber
|
|
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 (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
|
|
UNAVAILABLE forever — the live half of the UNAVAILABLE -> BadNoCommunication mapping.
|
|
|
|
fixture_writeback is likewise never fed, and exists for a different reason: it is the
|
|
target the write legs POST to (agent.cfg has AllowPut = true). It is a SEPARATE item from
|
|
fixture_block on purpose — the first successful write would take fixture_block off
|
|
UNAVAILABLE for the life of the container and quietly rob the mapping test above of its
|
|
subject. Because no adapter ever writes fixture_writeback, the value a write leg reads back
|
|
out of /current can only have come from that write.
|
|
-->
|
|
<MTConnectDevices
|
|
xmlns="urn:mtconnect.org:MTConnectDevices:2.0"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="urn:mtconnect.org:MTConnectDevices:2.0 http://schemas.mtconnect.org/schemas/MTConnectDevices_2.0.xsd">
|
|
<Header creationTime="2026-07-24T00:00:00Z" sender="otopcua-fixture" instanceId="0" version="2.0" assetBufferSize="128" assetCount="0" bufferSize="16384"/>
|
|
<Devices>
|
|
<!--
|
|
The Adapters block in agent.cfg is keyed by this Device's NAME (OtFixtureCnc). Rename
|
|
one and you must rename the other, or the Agent starts with no adapter attached and
|
|
every observation stays UNAVAILABLE.
|
|
-->
|
|
<Device id="otfixture" name="OtFixtureCnc" uuid="otopcua-mtconnect-fixture">
|
|
<Description manufacturer="OtOpcUa" model="IntegrationFixture" serialNumber="fixture-001">MTConnect integration-test fixture</Description>
|
|
<DataItems>
|
|
<!-- Device-level data items, declared OUTSIDE any component: the walker must not drop these. -->
|
|
<DataItem category="EVENT" id="fixture_avail" name="Favail" type="AVAILABILITY"/>
|
|
<DataItem category="EVENT" id="fixture_asset_changed" type="ASSET_CHANGED"/>
|
|
<DataItem category="EVENT" id="fixture_asset_removed" type="ASSET_REMOVED"/>
|
|
</DataItems>
|
|
<Components>
|
|
<Axes id="fixture_axes" name="Axes">
|
|
<Components>
|
|
<Linear id="fixture_x" name="X">
|
|
<DataItems>
|
|
<!-- SAMPLE + units -> Float64. Driven as a sinusoid, so its value CHANGES between reads. -->
|
|
<DataItem category="SAMPLE" id="fixture_x_pos" name="Xact" type="POSITION" subType="ACTUAL" units="MILLIMETER" nativeUnits="MILLIMETER"/>
|
|
<DataItem category="SAMPLE" id="fixture_x_load" name="Xload" type="LOAD" units="PERCENT" nativeUnits="PERCENT"/>
|
|
<!-- CONDITION -> String + IsAlarm, whatever its type says. -->
|
|
<DataItem category="CONDITION" id="fixture_x_travel" name="Xtravel" type="POSITION"/>
|
|
</DataItems>
|
|
</Linear>
|
|
<Rotary id="fixture_c" name="C">
|
|
<DataItems>
|
|
<DataItem category="SAMPLE" id="fixture_c_speed" name="Cspeed" type="ROTARY_VELOCITY" subType="ACTUAL" units="REVOLUTION/MINUTE" nativeUnits="REVOLUTION/MINUTE"/>
|
|
<!--
|
|
TIME_SERIES -> Float64 ARRAY, with ArrayDim NULL (variable length).
|
|
|
|
There is deliberately no sampleCount attribute here, and there cannot be:
|
|
`sampleCount` is an attribute of a TIME_SERIES *observation*, not of a
|
|
DataItem declaration. A real Agent rejects it outright: it logs
|
|
"The following keys were present and not expected: sampleCount" followed by
|
|
"DataItems: Invalid element 'DataItem'" and DROPS THE ENTIRE DATA ITEM from
|
|
the device model, so the tag simply vanishes from /probe (verified live
|
|
against mtconnect/agent 2.7.0.12). MTConnectDataTypeInference therefore only
|
|
ever sees sampleCount = null from a real Agent, and stamps ArrayDim = null.
|
|
`sampleRate` IS a valid DataItem attribute and is kept.
|
|
-->
|
|
<DataItem category="SAMPLE" id="fixture_c_temp_series" name="Ctemps" type="TEMPERATURE" units="CELSIUS" nativeUnits="CELSIUS" representation="TIME_SERIES" sampleRate="100"/>
|
|
</DataItems>
|
|
</Rotary>
|
|
</Components>
|
|
</Axes>
|
|
<Controller id="fixture_controller" name="Controller">
|
|
<DataItems>
|
|
<!-- Controlled vocabulary EVENT -> String. -->
|
|
<DataItem category="EVENT" id="fixture_mode" name="Cmode" type="CONTROLLER_MODE"/>
|
|
</DataItems>
|
|
<Components>
|
|
<Path id="fixture_path" name="Path">
|
|
<DataItems>
|
|
<DataItem category="EVENT" id="fixture_execution" name="Pexec" type="EXECUTION"/>
|
|
<!-- THE regression case: UPPER_SNAKE PART_COUNT must infer Int64, not String. -->
|
|
<DataItem category="EVENT" id="fixture_partcount" name="Pcount" type="PART_COUNT"/>
|
|
<DataItem category="EVENT" id="fixture_linenumber" name="Pline" type="LINE_NUMBER"/>
|
|
<DataItem category="EVENT" id="fixture_program" name="Pprogram" type="PROGRAM"/>
|
|
<!-- Free-text EVENT the adapter NEVER feeds -> stays UNAVAILABLE. -->
|
|
<DataItem category="EVENT" id="fixture_block" name="Pblock" type="BLOCK"/>
|
|
<!--
|
|
The write-back target (MTCONNECT_WRITE_TARGET overrides the id when the suite is
|
|
pointed at a real Agent). Free-text EVENT, so any string a write leg sends is a
|
|
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 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>
|
|
</Components>
|
|
</Controller>
|
|
</Components>
|
|
</Device>
|
|
</Devices>
|
|
</MTConnectDevices>
|