Standard.xmpp — generic Modbus TCP server on port 502, slave id 1. HR[0..31] seeded with address-as-value (HR[5]=5 — easy mental map for diagnostics), HR[100] auto-incrementing via a 1Hz LinearGenerator binding (drives subscribe-and-receive integration tests so they have a register that actually changes without a write), HR[200..209] scratch range for write-roundtrip tests, coils 0..31 alternating on/off, coils 100..109 scratch. The Tick automation runs 0..65535 over 60s looping; bound to HR[100] via Binding_SINT16 — slow enough that a 250ms-poll integration test sees discrete jumps, fast enough that a 5s subscribe test sees several change notifications. DL205.xmpp — AutomationDirect DirectLOGIC DL205/DL260 quirk simulator on port 502, slave id 1, modeling the behaviors documented in docs/v2/dl205.md as concrete register values so DL205 integration tests can assert each quirk WITHOUT a live PLC. Per-quirk encoding: V0 marker at HR[0]=0xCAFE proves register 0 is valid (rejects-register-0 rumour disproved); V2000 marker at HR[1024]=0x2000 proves V-memory octal-to-decimal mapping; V40400 marker at HR[8448]=0x4040 proves V40400→PDU 0x2100 (NOT register 0, contrary to the widespread shorthand); 'Hello' string at HR[1040..1042] packed first-char-low-byte (HR[1040]=0x6548 = 'H' lo + 'e' hi, HR[1041]=0x6C6C, HR[1042]=0x006F) — the headline string-byte-order quirk the user flagged; Float32 1.5f at HR[1056..1057] in CDAB word order (low word first: 0, then 0x3FC0); BCD register at HR[1072]=0x1234 representing decimal 1234 in BCD nibbles (NOT binary 0x04D2); 128-register block at HR[1280..1407] for FC03-128-cap testing; Y0 marker at coil 2048, C0 marker at coil 3072, scratch C-coils at 4000..4007 for write tests. Critical limitation flagged inline + in README: ModbusPal 1.6b CANNOT represent the DL205 quirks semantically — it has no string binding, no BCD binding, no arbitrary-byte-layout binding (only SINT16/SINT32/FLOAT32 with word-order). So every DL205 quirk is encoded as a pre-computed raw 16-bit integer with the math worked out in inline comments above each register. Becomes unreadable past ~50 quirky registers; the README's 'alternatives' section recommends switching to pymodbus when that threshold approaches (pymodbus's ModbusSimulatorServer has first-class headless + scriptable callbacks for byte-level layouts). Other ModbusPal 1.6b limitations called out in README: only holding_registers + coils sections in the official build (no input_registers / discrete_inputs — DL260 X-input markers can't be encoded faithfully here, FC02/FC04 tests wait for a fork or pymodbus); abandoned project (last release 1.6b, active forks at SCADA-LTS/ModbusPal, ControlThings-io/modbuspal, mrhenrike/ModbusPalEnhanced); no headless mode in the official JAR (-loadFile / -hide flags only in source-built forks); CVE-2018-10832 XXE on .xmpp import (don't import untrusted profiles — the in-repo ones are author-controlled). README.md updated with: per-profile description tables, getting-started (download jar + java -jar + GUI File>Load>Run), MODBUS_SIM_ENDPOINT env-var override doc, two reference tables documenting which HR / coil address encodes which DL205 quirk + which test name asserts it (the same DL205_<behavior> naming convention from docs/v2/modbus-test-plan.md), 4-row alternatives comparison (pymodbus / diagslave / ModbusMechanic / ModRSsim2) for when ModbusPal can no longer carry the load, and a quick-reference XML format table at the bottom for future-me hand-authoring more profiles. Pure documentation + test-asset PR — no code changes. The integration tests that consume these profiles (the actual DL205_<behavior> facts) land one at a time in PR 43+ as user validates each quirk via ModbusPal on the bench. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
193 lines
9.9 KiB
XML
193 lines
9.9 KiB
XML
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
|
<!DOCTYPE modbuspal_project SYSTEM "modbuspal.dtd">
|
|
|
|
<!--
|
|
DL205.xmpp — AutomationDirect DirectLOGIC DL205 / DL260 quirk simulator.
|
|
|
|
Slave id 1 on TCP 502. Models the real-PLC behaviors documented in
|
|
docs/v2/dl205.md as concrete register values, so integration tests can
|
|
assert each quirk WITHOUT a live PLC. The driver is correct when reads
|
|
against this profile produce the same logical values that an
|
|
AutomationDirect-aware client would see.
|
|
|
|
BIG WARNING: every "interesting" register here is encoded as a raw 16-bit
|
|
integer. ModbusPal 1.6b serves whatever you put in `value="..."` straight
|
|
onto the wire as a 16-bit big-endian register; it has no String / BCD /
|
|
Float / WordSwap binding (only SINT16 / SINT32 / FLOAT32 + word-order, none
|
|
of which capture the byte-level packing the DL series uses). So strings,
|
|
BCD, and CDAB floats live here as opaque integers with the math worked out
|
|
in the comment above each register. That math is reproduced in
|
|
docs/v2/dl205.md so the two stay in sync.
|
|
|
|
If this profile grows beyond ~50 quirky registers, switch to pymodbus
|
|
(see ModbusPal/README.md §"alternatives") — the magic-number table will
|
|
become unreadable. For the planned 12 DL205_<behavior> tests, raw values
|
|
are fine.
|
|
|
|
Loaded via the ModbusPal GUI: File > Load > pick this file > Run.
|
|
Run only ONE simulator at a time (they share TCP 502); to switch between
|
|
Standard and DL205, stop one before loading the other.
|
|
-->
|
|
|
|
<modbuspal_project>
|
|
|
|
<idgen value="200"/>
|
|
|
|
<links selected="TCP/IP">
|
|
<tcpip port="502"/>
|
|
<serial com="COM 1" baudrate="9600" parity="even" stops="1">
|
|
<flowcontrol xonxoff="false" rtscts="false"/>
|
|
</serial>
|
|
</links>
|
|
|
|
<slave id="1" enabled="true" name="DL205Sim" implementation="modbus">
|
|
|
|
<holding_registers>
|
|
|
|
<!-- ============================================================
|
|
V-MEMORY ADDRESSING MARKERS
|
|
============================================================
|
|
DirectLOGIC V-memory is octal natively; the CPU translates
|
|
V<oct> -> Modbus PDU <decimal>. Tests verify our address
|
|
helper produces the right PDU offset for known V-addresses.
|
|
Marker values are arbitrary but distinctive so a test that
|
|
reads the wrong PDU sees Goodread+wrong-value, not zero.
|
|
-->
|
|
|
|
<!-- V0 (octal) = PDU 0x0000. Decisively proves register 0 is valid
|
|
on DL205/DL260 with H2-ECOM100 in absolute mode (the default).
|
|
The "rejects register 0" rumour was a DL05/DL06 relative-mode
|
|
artefact — see dl205.md §Register Zero. -->
|
|
<!-- 0xCAFE = 51966 (signed 16-bit: -13570) -->
|
|
<register address="0" value="-13570" name="V0_marker_0xCAFE"/>
|
|
|
|
<!-- V2000 octal = decimal 1024 = PDU 0x0400. -->
|
|
<!-- 0x2000 = 8192 -->
|
|
<register address="1024" value="8192" name="V2000_marker_0x2000"/>
|
|
|
|
<!-- V40400 octal = decimal 8448 = PDU 0x2100. Proves the
|
|
"V40400 = register 0" myth wrong on absolute-mode firmware. -->
|
|
<!-- 0x4040 = 16448 -->
|
|
<register address="8448" value="16448" name="V40400_marker_0x4040"/>
|
|
|
|
<!-- ============================================================
|
|
STRING PACKING (the user's headline quirk)
|
|
============================================================
|
|
Two ASCII chars per register, FIRST CHAR in the LOW byte.
|
|
"Hello" at HR[0x410..0x412]:
|
|
|
|
HR[0x410] = 'H' (0x48) lo, 'e' (0x65) hi -> 0x6548 = 25928
|
|
HR[0x411] = 'l' (0x6C) lo, 'l' (0x6C) hi -> 0x6C6C = 27756
|
|
HR[0x412] = 'o' (0x6F) lo, '\0' (0x00) hi -> 0x006F = 111
|
|
|
|
A textbook (high-byte-first) decoder reads "eH" "ll" "\0o"
|
|
and prints "eHll \0o" — that's exactly the failure mode the
|
|
DL205 string test asserts NOT happens once we add the
|
|
ModbusStringByteOrder=LowFirst option to the driver.
|
|
Test: DL205_String_low_byte_first_within_register. -->
|
|
<register address="1040" value="25928" name="HelloStr_lo='H'_hi='e'"/>
|
|
<register address="1041" value="27756" name="HelloStr_lo='l'_hi='l'"/>
|
|
<register address="1042" value="111" name="HelloStr_lo='o'_hi=null"/>
|
|
|
|
<!-- ============================================================
|
|
32-BIT FLOAT IN CDAB WORD ORDER
|
|
============================================================
|
|
IEEE 754 float 1.5f = 0x3FC00000.
|
|
Standard ABCD: HR[N]=0x3FC0, HR[N+1]=0x0000
|
|
DL205 CDAB: HR[N]=0x0000, HR[N+1]=0x3FC0 (LOW word first)
|
|
|
|
Test: DL205_Float32_word_order_is_CDAB.
|
|
Driver must use ModbusByteOrder=WordSwap to decode this as 1.5. -->
|
|
<register address="1056" value="0" name="FloatCDAB_lo_word"/>
|
|
<!-- 0x3FC0 = 16320 -->
|
|
<register address="1057" value="16320" name="FloatCDAB_hi_word"/>
|
|
|
|
<!-- ============================================================
|
|
BCD-ENCODED REGISTER (DirectLOGIC default numeric storage)
|
|
============================================================
|
|
Ladder value 1234 stored as 0x1234 = 4660 (BCD nibbles, NOT
|
|
binary 1234 = 0x04D2). A driver in binary-int mode reads 4660
|
|
and reports the wrong value; in BCD mode it nibble-decodes
|
|
0x1234 -> 1234. Test: DL205_BCD_register_decodes_as_decimal. -->
|
|
<!-- 0x1234 = 4660 -->
|
|
<register address="1072" value="4660" name="BCD_1234_as_0x1234"/>
|
|
|
|
<!-- ============================================================
|
|
LOAD-LIMIT BOUNDARY MARKERS
|
|
============================================================
|
|
The DL series caps FC03 at 128 registers (above spec's 125)
|
|
and FC16 at 100 (BELOW spec's 123). The cap-tests don't need
|
|
specific values — they assert exception 03 IllegalDataValue
|
|
on an over-sized request. We pre-seed a contiguous block at
|
|
0x500..0x57F (128 regs) so a 128-register read returns Good
|
|
and a 129-register read can be tried for the failure case.
|
|
Per-register values: address - 0x500 (so HR[0x500]=0,
|
|
HR[0x501]=1, ..., HR[0x57F]=127). Easy mental verification.
|
|
Test: DL205_FC03_128_registers_returns_Good. -->
|
|
<!-- (Generated programmatically below for brevity — first / last + spot-check) -->
|
|
<register address="1280" value="0" name="FC03Block_first"/>
|
|
<register address="1281" value="1"/>
|
|
<register address="1282" value="2"/>
|
|
<register address="1343" value="63" name="FC03Block_mid"/>
|
|
<register address="1407" value="127" name="FC03Block_last"/>
|
|
<!-- Note: ModbusPal serves unlisted addresses as 0 by default for
|
|
reads that fall within the configured slave's address space.
|
|
The block-test relies on that behavior; the hand-listed
|
|
entries above are sanity markers. If the driver later wants
|
|
byte-perfect comparison across the whole 128-register range,
|
|
expand this section to one element per address (or switch to
|
|
pymodbus). -->
|
|
</holding_registers>
|
|
|
|
<coils>
|
|
|
|
<!-- ============================================================
|
|
COIL / DISCRETE-INPUT MAPPING MARKERS (DL260 layout)
|
|
============================================================
|
|
Per dl205.md, on the DL260:
|
|
X inputs -> discrete inputs 0..511 (FC02)
|
|
Y outputs -> coils 2048..2559 (FC01/05)
|
|
C relays -> coils 3072..4095 (FC01/05)
|
|
|
|
ModbusPal 1.6b does NOT have a discrete-inputs section in
|
|
the official build, so the X-input markers can't be
|
|
encoded faithfully (the driver test for FC02 against this
|
|
profile will need a fork or pymodbus). The Y and C coil
|
|
markers ARE encodable here.
|
|
-->
|
|
|
|
<!-- Y0 marker — coil 2048 ON proves "Y0 maps to coil 2048" mapping.
|
|
Test: DL205_Y0_maps_to_coil_2048. -->
|
|
<coil address="2048" value="1" name="Y0_marker"/>
|
|
<coil address="2049" value="0"/>
|
|
<coil address="2050" value="1"/>
|
|
|
|
<!-- C0 marker — coil 3072 ON proves "C0 maps to coil 3072" mapping.
|
|
Test: DL205_C0_maps_to_coil_3072. -->
|
|
<coil address="3072" value="1" name="C0_marker"/>
|
|
<coil address="3073" value="0"/>
|
|
<coil address="3074" value="1"/>
|
|
|
|
<!-- Scratch coils 4000..4007 for write-roundtrip tests against
|
|
the C-relay range. C ranges are writable on the real DL260. -->
|
|
<coil address="4000" value="0" name="Cscratch_0"/>
|
|
<coil address="4001" value="0"/>
|
|
<coil address="4002" value="0"/>
|
|
<coil address="4003" value="0"/>
|
|
<coil address="4004" value="0"/>
|
|
<coil address="4005" value="0"/>
|
|
<coil address="4006" value="0"/>
|
|
<coil address="4007" value="0"/>
|
|
</coils>
|
|
|
|
<tuning>
|
|
<!-- Zero delay / zero error rate. The DL205 H2-ECOM has a typical
|
|
2-10ms scan-cycle delay; if a test wants to simulate that,
|
|
tune via the ModbusPal GUI (Tuning > Reply delay). -->
|
|
<reply_delay min="0" max="0"/>
|
|
<error_rates no_reply="0.0"/>
|
|
</tuning>
|
|
</slave>
|
|
|
|
</modbuspal_project>
|