Phase 3 PR 43 — Swap ModbusPal to pymodbus for the integration-test simulator #42

Merged
dohertj2 merged 1 commits from phase-3-pr43-pymodbus-swap into v2 2026-04-18 20:52:48 -04:00
Owner

Replaces the .xmpp profiles from PR 42 with pymodbus 3.13.0 ModbusSimulatorServer JSON configs. Per the README's prior 'when this becomes unwieldy, switch to pymodbus' note — happening now while the catalog is still small.

Why swap

ModbusPal 1.6b pymodbus 3.13.0
Maintenance Abandoned (last release ~2019) Current stable, active dev
Headless No (Java GUI only in official JAR) Yes, pure CLI
Tables exposed HR + coils only All four (HR, IR, coils, DI)
Per-register seeding Yes (per-element) Yes (per-addr JSON entry)
Dynamic registers Generators with bindings Built-in increment/random/timestamp/uptime actions + custom Python actions
Web UI / REST No Optional aiohttp UI on --http_port
Install on Windows Java + jar download pip install 'pymodbus[simulator]==3.13.0'
CI-friendly No Yes

Files

  • Pymodbus/standard.json — generic Modbus TCP server. HR[0..31] address-as-value, HR[100] declarative increment action, HR[200..209] scratch, alternating coils.
  • Pymodbus/dl205.json — DL205/DL260 quirks per docs/v2/dl205.md. Each quirky register has an inline _quirk JSON-comment naming the behavior. shared blocks: true matches DL series memory model.
  • Pymodbus/serve.ps1-Profile {standard|dl205} wrapper that validates pymodbus.simulator is on PATH, picks the right JSON, and execs the simulator in foreground.
  • Pymodbus/README.md — install + run + per-profile reference tables + alternatives + file-format reference.

Code changes

  • ModbusSimulatorFixture default port 5025020 (pymodbus convention; sidesteps Windows admin requirement). Skip-message points at Pymodbus\serve.ps1 instead of 'start ModbusPal'. Override via MODBUS_SIM_ENDPOINT.
  • csproj <None Update> rule swapped from ModbusPal/** to Pymodbus/** — JSON + PS1 + README copy to test-output.
  • DL205Profile.cs + DL205SmokeTests.cs xml-doc references updated.

Doc changes

  • docs/v2/modbus-test-plan.md harness section rewritten with the swap rationale; PR-history list marks PR 42 SUPERSEDED. Test-conventions bullet generalized from 'don't depend on ModbusPal state' to 'don't depend on simulator state'.

Run

pip install "pymodbus[simulator]==3.13.0"
cd tests\ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests\Pymodbus
.\serve.ps1 -Profile standard      # or -Profile dl205
# In another shell:
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests

Test posture

  • Build clean — 0 errors.
  • Both JSON files structurally valid (PowerShell ConvertFrom-Json clean).
  • Functional validation deferred — Python not installed on this dev box (winget search Python.Python.3 --exact returned no matches). JSON should parse on a pymodbus install; if not, Pymodbus/README.md's schema-reference link points at the canonical doc.

What's next

The DL205 quirks are now both documented (docs/v2/dl205.md) and simulator-encoded (Pymodbus/dl205.json). The next bounded PR per the roadmap in modbus-test-plan.md is the first quirk-implementation PR (PR 44+) — string-byte-order option in the Modbus driver, followed by the DL205_String_low_byte_first_within_register integration test that asserts it works against dl205.json.

Replaces the `.xmpp` profiles from PR 42 with pymodbus 3.13.0 `ModbusSimulatorServer` JSON configs. Per the README's prior 'when this becomes unwieldy, switch to pymodbus' note — happening now while the catalog is still small. ## Why swap | | ModbusPal 1.6b | pymodbus 3.13.0 | | --- | --- | --- | | Maintenance | **Abandoned** (last release ~2019) | Current stable, active dev | | Headless | No (Java GUI only in official JAR) | **Yes**, pure CLI | | Tables exposed | HR + coils only | **All four** (HR, IR, coils, DI) | | Per-register seeding | Yes (per-element) | Yes (per-`addr` JSON entry) | | Dynamic registers | Generators with bindings | Built-in `increment`/`random`/`timestamp`/`uptime` actions + custom Python actions | | Web UI / REST | No | Optional aiohttp UI on `--http_port` | | Install on Windows | Java + jar download | `pip install 'pymodbus[simulator]==3.13.0'` | | CI-friendly | No | Yes | ## Files - `Pymodbus/standard.json` — generic Modbus TCP server. HR[0..31] address-as-value, HR[100] declarative `increment` action, HR[200..209] scratch, alternating coils. - `Pymodbus/dl205.json` — DL205/DL260 quirks per `docs/v2/dl205.md`. Each quirky register has an inline `_quirk` JSON-comment naming the behavior. `shared blocks: true` matches DL series memory model. - `Pymodbus/serve.ps1` — `-Profile {standard|dl205}` wrapper that validates pymodbus.simulator is on PATH, picks the right JSON, and execs the simulator in foreground. - `Pymodbus/README.md` — install + run + per-profile reference tables + alternatives + file-format reference. ## Code changes - `ModbusSimulatorFixture` default port `502` → `5020` (pymodbus convention; sidesteps Windows admin requirement). Skip-message points at `Pymodbus\serve.ps1` instead of 'start ModbusPal'. Override via `MODBUS_SIM_ENDPOINT`. - `csproj` `<None Update>` rule swapped from `ModbusPal/**` to `Pymodbus/**` — JSON + PS1 + README copy to test-output. - `DL205Profile.cs` + `DL205SmokeTests.cs` xml-doc references updated. ## Doc changes - `docs/v2/modbus-test-plan.md` harness section rewritten with the swap rationale; PR-history list marks PR 42 SUPERSEDED. Test-conventions bullet generalized from 'don't depend on ModbusPal state' to 'don't depend on simulator state'. ## Run ```powershell pip install "pymodbus[simulator]==3.13.0" cd tests\ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests\Pymodbus .\serve.ps1 -Profile standard # or -Profile dl205 # In another shell: dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests ``` ## Test posture - Build clean — 0 errors. - Both JSON files structurally valid (PowerShell `ConvertFrom-Json` clean). - **Functional validation deferred** — Python not installed on this dev box (`winget search Python.Python.3 --exact` returned no matches). JSON should parse on a pymodbus install; if not, `Pymodbus/README.md`'s schema-reference link points at the canonical doc. ## What's next The DL205 quirks are now both **documented** (`docs/v2/dl205.md`) and **simulator-encoded** (`Pymodbus/dl205.json`). The next bounded PR per the roadmap in `modbus-test-plan.md` is the first quirk-implementation PR (PR 44+) — string-byte-order option in the Modbus driver, followed by the `DL205_String_low_byte_first_within_register` integration test that asserts it works against `dl205.json`.
dohertj2 added 1 commit 2026-04-18 20:52:45 -04:00
ModbusSimulatorFixture default port bumped from 502 to 5020 to match the pymodbus convention. Override via MODBUS_SIM_ENDPOINT for a real PLC on its native 502. Skip-message updated to point at the new Pymodbus\serve.ps1 wrapper instead of 'start ModbusPal'. csproj <None Update> rule swapped from ModbusPal/** to Pymodbus/** so the new JSON profiles + serve.ps1 + README copy to test-output as PreserveNewest.
standard.json — generic Modbus TCP server, slave id 1, port 5020, shared blocks=false (independent coils + HR address spaces, more textbook-PLC-like). HR[0..31] seeded with address-as-value via per-register uint16 entries, HR[100] auto-increments via the built-in increment action with parameters minval=0/maxval=65535 (drives subscribe-and-receive integration tests so they have a register that ticks without a write — pymodbus's increment ticks per-access not wall-clock, which is good enough for a 250ms-poll test), HR[200..209] scratch range left at 0 for write tests, coils 0..31 alternating, coils 100..109 scratch. write list covers 0..1023 so any test address is mutable.
dl205.json — AutomationDirect DirectLOGIC DL205/DL260 quirk simulator, slave id 1, port 5020, shared blocks=true (matches DL series memory model where coils/DI/HR overlay the same word address space). Each quirky register seeded with the pre-computed raw uint16 value documented in docs/v2/dl205.md, with an inline _quirk JSON-comment naming the behavior so future-me reading the file knows why HR[1040]=25928 means 'H' lo / 'e' hi (the user's headline string-byte-order finding). Encoded quirks: V0 marker at HR[0]=0xCAFE; V2000 at HR[1024]=0x2000; V40400 at HR[8448]=0x4040; 'Hello' string at HR[1040..1042] first-char-low-byte; Float32 1.5f at HR[1056..1057] in CDAB word order (low word first); BCD register at HR[1072]=0x1234; FC03-128-cap block at HR[1280..1407]; Y0/C0 coil markers at 2048/3072; scratch C-relays at 4000..4007.
serve.ps1 wrapper — pwsh script with a -Profile {standard|dl205} parameter switch. Validates pymodbus.simulator is on PATH (clearer message than the raw CommandNotFoundException), validates the profile JSON exists, builds the right --modbus_server/--modbus_device/--json_file/--http_port arg list, and execs pymodbus.simulator in the foreground. -HttpPort 0 disables the web UI. Foreground exec lets the operator Ctrl+C to stop without an extra control script.
README.md fully rewritten for pymodbus: install command (pip install 'pymodbus[simulator]==3.13.0' — pinned for reproducibility, [simulator] extra pulls aiohttp), per-profile reference tables, the same DL205 quirk → register table from PR 42 but adjusted for pymodbus paths, what's-NEW-vs-ModbusPal section (all four tables, raw uint16 seeding, declarative actions, custom Python action modules, headless, web UI, maintained), trade-offs section (float32-as-two-uint16s for explicit CDAB control, increment ticks per-access not wall-clock, shared-blocks mode for DL205 vs separate for Standard), file-format quick reference for hand-authoring more profiles. References pinned to the pymodbus readthedocs simulator/config + REST API pages.
docs/v2/modbus-test-plan.md harness section rewritten with the swap rationale; PR-history list updated to mark PR 42 SUPERSEDED by PR 43 and call out PR 44+ as the per-quirk implementation track. Test-conventions bullet about 'don't depend on ModbusPal state between tests' generalized to 'don't depend on simulator state' and a note added that pymodbus's REST API can reset state between facts if a test ever needs it.
DL205Profile.cs and DL205SmokeTests.cs xml-doc updated to reference pymodbus / dl205.json instead of ModbusPal / DL205.xmpp.
Functional validation deferred — Python isn't installed on this dev box (winget search returned no matches for Python.Python.3 exact). JSON parses structurally (PowerShell ConvertFrom-Json clean on both files), build clean, .json + serve.ps1 + README all copy to test-output as expected. User installs pymodbus when they want to actually run the simulator end-to-end; if pymodbus rejects the config the README's reference link to pymodbus's simulator/config schema doc is the right next stop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit faeab34541 into v2 2026-04-18 20:52:48 -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#42