Web verification (2026-04-25) against current vendor docs surfaced concrete
grammar conflicts in the v1 suffix grammar shipped in #137. Hard cutover
before the Admin UI rolls out widely so users don't paste `:I` from a
Wonderware spreadsheet and silently get wrong-typed reads.
Sources:
- Wonderware DASMBTCP user guide
https://cdn.logic-control.com/media/DASMBTCP.pdf
- Ignition Modbus addressing (8.1)
https://www.docs.inductiveautomation.com/docs/8.1/ignition-modules/opc-ua/opc-ua-drivers/modbus/modbus-addressing
Type-code changes:
| Code | Pre-#146 | Post-#146 | Vendor reference |
|--------|----------|------------|------------------------------|
| `:S` | (n/a) | Int16 | Wonderware DASMBTCP `S` |
| `:US` | (n/a) | UInt16 | Ignition `HRUS` |
| `:I` | Int16 | **Int32** | Wonderware `I` + Ignition `HRI` |
| `:UI` | UInt16 | **UInt32** | Ignition `HRUI` |
| `:I_64` | (n/a) | Int64 | Ignition `HRI_64` |
| `:UI_64` | (n/a) | UInt64 | Ignition `HRUI_64` |
| `:BCD_32`| (n/a) | BCD32 | Ignition `HRBCD_32` |
Codes REMOVED (no clear vendor precedent + conflict with the new mapping):
`:DI`, `:L`, `:UDI`, `:UL`, `:LI`, `:ULI`, `:LBCD`. Pre-#146 configs that
use them get an "Unknown type code" diagnostic at parse time so users get
a fast surface-level error rather than silent wrong-typed reads.
Codes UNCHANGED (already vendor-aligned): `:BOOL`, `:F`, `:D`, `:BCD`,
`:STR<n>`. Modicon 5/6-digit + mnemonic regions (HR/IR/C/DI) + bit suffix
`.N` are also unchanged.
Defaults:
- Coils / DiscreteInputs → `BOOL` (unchanged)
- HoldingRegisters / InputRegisters with no explicit type → Int16 (matches
Ignition's bare `HR` default)
Byte-order mnemonics (`:ABCD` / `:CDAB` / `:BADC` / `:DCBA`) are kept but
documented as OtOpcUa-specific — they aren't in any major vendor's per-tag
address string. Ignition uses a `-R` suffix per prefix; Wonderware
configures word-order at the topic level.
Tests:
- 12 Type_Codes_Parse rows updated to assert the new mappings.
- New Removed_Aliases_Are_Rejected (×7) confirms each pre-#146 alias now
fails fast with "Unknown type code".
- Worked_Example_Int16_Array uses the new `:S` code.
- New Worked_Example_Int32_Array_Via_I_Code documents the `:I = Int32`
vendor-alignment intent so a future "fix" doesn't accidentally regress.
- Unknown_Type_Code_Rejected_With_Catalog updated to match the new error
message ("Valid: BOOL, S, US, I, ...").
Docs:
- docs/v2/modbus-addressing.md — table replaced with the post-#146 codes,
each row cites its Wonderware / Ignition reference. New "Codes removed
in #146" subsection documents the cutover.
- docs/Driver.Modbus.Cli.md — example grammar list updated; explicit
type-code reminder appended.
114 addressing tests + 231 driver tests still green. Solution build clean.
Closes the docs/e2e end of the Modbus addressing line shipped across
#136-#145.
Docs:
- docs/v2/modbus-addressing.md (new) — full grammar reference.
Region+offset (Modicon 5-digit / 6-digit / mnemonic), bit suffix,
type codes (BOOL / I / UI / DI / UDI / LI / ULI / F / D / BCD / LBCD /
STR<n>), all four byte-order mnemonics (ABCD / CDAB / BADC / DCBA),
array-count semantics, family-native syntax (DL205 V/Y/C/X/SP and
MELSEC D/M/X/Y with hex-vs-octal sub-family selection), driver-instance
options (KeepAlive / Reconnect / IdleDisconnect, MaxCoilsPerRead and
FC15/16 forcing, Deadband + WriteOnChangeOnly, MaxReadGap +
CoalesceProhibited, multi-unit IPerCallHostResolver). Includes a worked
JSON DTO example mixing AddressString + structured tag forms.
- docs/Driver.Modbus.Cli.md — appended a "v2 addressing grammar" section
pointing users at the full reference, with quick-reference examples.
- Vendor-compatibility caveat documented: type codes and byte-order
mnemonics were synthesised from training-era vendor docs (Wonderware
DASMBTCP, Kepware KEPServerEX, Ignition, Matrikon, OAS) and should be
verified against current vendor manuals before locking for production.
E2E tests (4 new AddressingGrammarTests in IntegrationTests):
- Modicon 5-digit and 6-digit forms map to identical wire offsets.
- Float32 + WordSwap (CDAB) round-trips end-to-end through the
pymodbus simulator.
- Int16[5] array round-trips as a typed short[] surface.
- Block-read coalescing produces a wire-acceptable PDU when MaxReadGap=5
bridges three nearby tags.
All tests skip gracefully when the pymodbus simulator at localhost:5020
is unreachable (matches the existing ModbusSimulatorFixture pattern).
Final test count across the Modbus addressing surface:
- 107 ModbusAddressing.Tests (parser + family + Modicon)
- 231 Driver.Modbus.Tests (driver, byte order, array, multi-unit, coalescing,
protocol, subscribe, connection options)
- 110 Admin.Tests (incl. ModbusOptionsViewModel defaults pinning)
- 4 new AddressingGrammar integration tests (skip when sim down)