Phase 3 PR 42 — ModbusPal simulator profiles for Standard + DL205/DL260 #41

Merged
dohertj2 merged 1 commits from phase-3-pr42-modbuspal-profiles into v2 2026-04-18 20:12:41 -04:00
Owner

Two hand-authored .xmpp profiles drop into ModbusPal's GUI to drive the integration suite without a real PLC. Both well-formed XML; both copy to test-output as PreserveNewest content per the existing csproj rule.

Standard.xmpp

Generic Modbus TCP server, slave id 1, port 502.

  • HR[0..31] = address-as-value (HR[5]=5 — easy mental map for diagnostics).
  • HR[100] auto-increments via a 1Hz LinearGenerator binding — drives subscribe-and-receive tests so they have a register that changes without a write.
  • HR[200..209] scratch range for write-roundtrip tests.
  • Coils 0..31 alternate on/off; coils 100..109 scratch.

DL205.xmpp

AutomationDirect DirectLOGIC DL205/DL260 quirk simulator, modeling each behavior in docs/v2/dl205.md as concrete register values:

HR address Quirk Raw value Decoded
0 (V0) Register 0 valid 0xCAFE marker
1024 (V2000 octal) V-memory octal→decimal 0x2000 marker
8448 (V40400 octal) V40400 → PDU 0x2100 0x4040 marker
1040..1042 String 'Hello' first-char-low-byte 25928, 27756, 111 "Hello"
1056..1057 Float32 1.5f in CDAB 0, 16320 1.5f
1072 Decimal 1234 in BCD 0x1234 (4660) 1234
1280..1407 128-register block (FC03 cap = 128) address − 1280 for cap test

Coils: Y0 marker at coil 2048, C0 marker at coil 3072 (DL260 layout), scratch C-coils at 4000..4007.

The headline string-byte-order quirk you flagged: HR[0x410] holds 0x6548 ('H' in low byte, 'e' in high byte). A textbook decoder reads "eHll \0o"; an AutomationDirect-aware decoder reads "Hello".

ModbusPal 1.6b limitations called out

  • Only holding_registers + coils in the official build. No input_registers, no discrete_inputs. DL260 X-input markers can't be encoded faithfully here — FC02/FC04 tests wait for a fork or pymodbus.
  • No semantic bindings for strings / BCD / arbitrary byte layouts — only SINT16/SINT32/FLOAT32 with word-order. Every DL205 quirk encoded as a pre-computed raw 16-bit integer with the math worked out in inline comments. README flags pymodbus as the recommended pivot when this becomes unwieldy (~50+ quirky registers).
  • Project is abandoned (last 1.6b on SourceForge); active forks: SCADA-LTS/ModbusPal, ControlThings-io/modbuspal, mrhenrike/ModbusPalEnhanced.
  • No headless mode in the official JAR — fine for dev-loop GUI use, plan to switch to pymodbus.server.ModbusSimulatorServer when CI needs simulator coverage.
  • CVE-2018-10832 XXE on .xmpp import — only matters if you import untrusted files. In-repo profiles are author-controlled.

README

Fully rewritten with per-profile reference tables, getting-started, MODBUS_SIM_ENDPOINT doc, alternatives comparison, and a quick-reference XML format table for hand-authoring more profiles.

What's NOT in this PR

The integration tests that consume these profiles (the actual DL205_<behavior> facts). One PR per quirk lands in PR 43+ as you validate each on the bench. Pure documentation + test-asset PR; no code changes.

Two hand-authored `.xmpp` profiles drop into ModbusPal's GUI to drive the integration suite without a real PLC. Both well-formed XML; both copy to test-output as `PreserveNewest` content per the existing csproj rule. ## `Standard.xmpp` Generic Modbus TCP server, slave id 1, port 502. - HR[0..31] = address-as-value (HR[5]=5 — easy mental map for diagnostics). - HR[100] auto-increments via a 1Hz `LinearGenerator` binding — drives subscribe-and-receive tests so they have a register that changes without a write. - HR[200..209] scratch range for write-roundtrip tests. - Coils 0..31 alternate on/off; coils 100..109 scratch. ## `DL205.xmpp` AutomationDirect DirectLOGIC DL205/DL260 quirk simulator, modeling each behavior in `docs/v2/dl205.md` as concrete register values: | HR address | Quirk | Raw value | Decoded | | --- | --- | --- | --- | | `0` (V0) | Register 0 valid | `0xCAFE` | marker | | `1024` (V2000 octal) | V-memory octal→decimal | `0x2000` | marker | | `8448` (V40400 octal) | V40400 → PDU 0x2100 | `0x4040` | marker | | `1040..1042` | **String 'Hello' first-char-low-byte** | `25928, 27756, 111` | `"Hello"` | | `1056..1057` | Float32 1.5f in CDAB | `0, 16320` | `1.5f` | | `1072` | Decimal 1234 in BCD | `0x1234` (4660) | `1234` | | `1280..1407` | 128-register block (FC03 cap = 128) | address − 1280 | for cap test | Coils: Y0 marker at coil 2048, C0 marker at coil 3072 (DL260 layout), scratch C-coils at 4000..4007. The headline string-byte-order quirk you flagged: HR[0x410] holds `0x6548` ('H' in low byte, 'e' in high byte). A textbook decoder reads `"eHll \0o"`; an AutomationDirect-aware decoder reads `"Hello"`. ## ModbusPal 1.6b limitations called out - **Only `holding_registers` + `coils`** in the official build. No `input_registers`, no `discrete_inputs`. DL260 X-input markers can't be encoded faithfully here — FC02/FC04 tests wait for a fork or pymodbus. - **No semantic bindings** for strings / BCD / arbitrary byte layouts — only SINT16/SINT32/FLOAT32 with word-order. Every DL205 quirk encoded as a pre-computed raw 16-bit integer with the math worked out in inline comments. README flags pymodbus as the recommended pivot when this becomes unwieldy (~50+ quirky registers). - Project is **abandoned** (last 1.6b on SourceForge); active forks: `SCADA-LTS/ModbusPal`, `ControlThings-io/modbuspal`, `mrhenrike/ModbusPalEnhanced`. - **No headless mode** in the official JAR — fine for dev-loop GUI use, plan to switch to `pymodbus.server.ModbusSimulatorServer` when CI needs simulator coverage. - **CVE-2018-10832** XXE on `.xmpp` import — only matters if you import untrusted files. In-repo profiles are author-controlled. ## README Fully rewritten with per-profile reference tables, getting-started, `MODBUS_SIM_ENDPOINT` doc, alternatives comparison, and a quick-reference XML format table for hand-authoring more profiles. ## What's NOT in this PR The integration tests that consume these profiles (the actual `DL205_<behavior>` facts). One PR per quirk lands in PR 43+ as you validate each on the bench. Pure documentation + test-asset PR; no code changes.
dohertj2 added 1 commit 2026-04-18 20:12:38 -04:00
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>
dohertj2 merged commit c59ac9e52d into v2 2026-04-18 20:12:41 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#41