# ModbusPal simulator profiles Two hand-authored `.xmpp` profiles you load into ModbusPal to drive the integration-test suite without a real PLC: | File | What it simulates | Test category | |---|---|---| | [`Standard.xmpp`](Standard.xmpp) | Generic Modbus TCP server — HR[0..31] = address-as-value, alternating coils, one auto-incrementing register at HR[100] for subscribe tests, scratch ranges for write-roundtrip tests. | `Trait=Standard` | | [`DL205.xmpp`](DL205.xmpp) | AutomationDirect DirectLOGIC DL205 / DL260 quirks per [`docs/v2/dl205.md`](../../../docs/v2/dl205.md): low-byte-first string packing, CDAB Float32, BCD numerics, V-memory address markers, Y/C coil mappings. | `Trait=DL205` | Both listen on TCP **port 502** (the standard Modbus port — change in the ModbusPal GUI if a port conflict). Run **only one at a time** since they share the port. ## Getting started 1. Download ModbusPal 1.6b from [SourceForge](https://sourceforge.net/projects/modbuspal/) — `modbuspal.jar`. Requires Java 8+ (Java 17/21 work but emit Swing deprecation warnings). 2. `java -jar modbuspal.jar` to launch the GUI. 3. **File > Load** → pick `Standard.xmpp` (or `DL205.xmpp`). 4. Click the **Run** button (top-right of the toolbar) to start serving on TCP 502. 5. `dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests` — tests auto-skip with a clear `SkipReason` if the TCP probe at the configured endpoint fails within 2 seconds (`ModbusSimulatorFixture`). ## Switching between Standard and DL205 Stop the running simulator (toolbar's **Stop** button), **File > Load** the other profile, **Run**. ## Environment variables - `MODBUS_SIM_ENDPOINT` — override the simulator endpoint (`host:port`). Defaults to `localhost:502`. Useful when pointing the suite at a real PLC on the bench, or running ModbusPal on a non-default port. ## What's encoded in each profile ### Standard - HR[0..31]: each register's value equals its address. - HR[100]: bound to a `LinearGenerator` (0..65535 over 60s, looping) — drives subscribe-and-receive tests. - HR[200..209]: scratch range for write-roundtrip tests. - Coils[0..31]: alternating on/off (even=on). - Coils[100..109]: scratch range. ### DL205 (per `docs/v2/dl205.md`) | HR address | Quirk demonstrated | Raw value | Decoded value | |---|---|---|---| | `0` | Register zero is valid (rejects-register-0 rumour disproved) | `-13570` (0xCAFE) | marker | | `1024` (= V2000 octal) | V-memory octal-to-decimal mapping | `8192` (0x2000) | marker | | `8448` (= V40400 octal) | V40400 → PDU 0x2100 (NOT register 0) | `16448` (0x4040) | marker | | `1040..1042` | String "Hello" packed first-char-low-byte | `25928, 27756, 111` | `"Hello"` | | `1056..1057` | Float32 1.5f in CDAB word order | `0, 16320` | `1.5f` | | `1072` | Decimal 1234 in BCD encoding | `4660` (0x1234) | `1234` | | `1280..1407` | 128-register block (FC03 cap = 128 above spec's 125) | address − 1280 | for FC03 cap test | | Coil address | Quirk demonstrated | |---|---| | `2048` | Y0 maps to coil 2048 (DL260 layout) | | `3072` | C0 maps to coil 3072 (DL260 layout) | | `4000..4007` | Scratch C-relay range for write-roundtrip tests | ## Limitations of ModbusPal 1.6b - **Only `holding_registers` + `coils`** sections in the official build — no `input_registers` (FC04) and no `discrete_inputs` (FC02). DL205's X-input markers can't be encoded faithfully here. Tests for FC02 / FC04 wait for a fork (e.g. `SCADA-LTS/ModbusPal`) or a pymodbus rewrite. - **No semantic bindings** for strings / BCD / arbitrary byte layouts. The DL205 profile encodes everything as pre-computed raw 16-bit integers with the math worked out in inline comments. Anything fancier becomes unreadable above ~50 quirky registers — switch to pymodbus when that threshold approaches. - **Project is abandoned** since 1.6b on the official SourceForge listing. Active forks: `SCADA-LTS/ModbusPal`, `ControlThings-io/modbuspal`, `mrhenrike/ModbusPalEnhanced`. - **No headless mode** in the official 1.6b JAR (`-loadFile` / `-hide` flags exist only in source-built forks). For CI use, plan to switch to pymodbus's `ModbusSimulatorServer` (JSON config, scriptable callbacks, first-class headless). - **CVE-2018-10832** XXE in `.xmpp` import. Don't import `.xmpp` files from untrusted sources. Profiles in this repo are author-controlled; safe. ## Alternatives if ModbusPal stops working | Tool | Pros | Cons | |---|---|---| | **pymodbus `ModbusSimulatorServer`** | Headless-first, JSON config, per-register seeding, custom callbacks for byte-level layouts. Best CI fit. | Python dependency. | | **diagslave** | Simple, headless, fast. | Flat register banks; no per-address seeding from config; no scripting. | | **ModbusMechanic** | Headless config-file mode. | Lightly documented. | | **ModRSsim2** | Windows GUI, CSV import, scripting. | GUI-centric. | ## File format reference ModbusPal `.xmpp` is XML with a DTD reference (`modbuspal.dtd`). Root element `` with three children: - `` — internal id counter (start at 100+) - `` — `` for TCP listen, plus a `` placeholder - One or more `` containing `` (``), `` (``), `` Per-register `` ties a register to a `LinearGenerator` / `RandomGenerator` / `SineGenerator` automation declared at the project level. `order="0"` = LSW, `order="1"` = MSW for 32-bit types. There is **no string binding** and **no byte-swap-within-word** binding.