Files
lmxopcua/tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests
Joseph Doherty 10c724b5b6 Phase 3 PR 56 -- Siemens S7-1500 pymodbus profile + smoke integration test. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Pymodbus/s7_1500.json modelling the SIMATIC S7-1500 + MB_SERVER default deployment documented in docs/v2/s7.md: DB1.DBW0 = 0xABCD fingerprint marker (operators reserve this so clients can verify they're talking to the right DB), scratch HR range 200..209 for write-roundtrip tests mirroring dl205.json + standard.json, Float32 1.5f at HR[100..101] in ABCD word order (high word first -- OPPOSITE of DL260 CDAB), Int32 0x12345678 at HR[300..301] in ABCD. Also seeds a coil at bit-addr 400 (= cell 25 bit 0) and a discrete input at bit-addr 500 (= cell 31 bit 0) so future S7-specific tests for FC01/FC02 have stable markers. shared blocks=true to match the proven dl205.json pattern (pymodbus's bits/uint16 cells coexist cleanly when addresses don't collide). Write list references cells (0, 25, 100-101, 200-209, 300-301), not bit addresses -- pymodbus's write-range entries are cell-indexed, not bit-indexed. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/ directory with S7_1500Profile.cs (mirrors DL205Profile pattern: SmokeHoldingRegister=200, SmokeHoldingValue=4321, BuildOptions tags + probe-disabled + 2s timeout) and S7_1500SmokeTests.cs (single fact S7_1500_roundtrip_write_then_read_of_holding_register that writes SmokeHoldingValue then reads it back, asserting both write status 0 and read status 0 + value equality). Gates on MODBUS_SIM_PROFILE=s7_1500 so the test skips cleanly against other profiles. csproj updated to copy S7/** to test output as PreserveNewest (pattern matching DL205/**). Pymodbus/serve.ps1 ValidateSet extended from {standard,dl205} to {standard,dl205,s7_1500,mitsubishi} -- mitsubishi.json lands in PR 58 but the validator slot is claimed now so the serve.ps1 diff is one line in this PR and zero lines in future PRs. Verified end-to-end: smoke test 1/1 passes against the running pymodbus s7_1500 profile (localhost:5020 FC06 write of 4321 at HR[200] + FC03 read back). 143/143 Modbus.Tests pass, no regression in driver code because this PR is purely test-asset. Per-quirk S7 integration tests (ABCD word order default, FC23 IllegalFunction, MB_SERVER STATUS 0x8383 behaviour, port-per-connection semantics) land in PR 57+.
2026-04-18 22:57:03 -04:00
..
Phase 3 PR 52 -- Modbus exception-code -> OPC UA StatusCode translation. Before this PR every server-side Modbus exception AND every transport-layer failure collapsed to BadInternalError (0x80020000) in the driver's Read/Write results, making field diagnosis 'is this a tag misconfig or a driver bug?' impossible from the OPC UA client side. PR 52 adds a MapModbusExceptionToStatus helper that translates per spec: 01 Illegal Function -> BadNotSupported (0x803D0000); 02 Illegal Data Address -> BadOutOfRange (0x803C0000); 03 Illegal Data Value -> BadOutOfRange; 04 Server Failure -> BadDeviceFailure (0x80550000); 05/06 Acknowledge/Busy -> BadDeviceFailure; 0A/0B Gateway -> BadCommunicationError (0x80050000); unknown -> BadInternalError fallback. Non-Modbus failures (socket drop, timeout, malformed frame) in ReadAsync are now distinguished from tag-level faults: they map to BadCommunicationError so operators check network/PLC reachability rather than tag definitions. Why per-DL205: docs/v2/dl205.md documents DL205/DL260 returning only codes 01-04 with specific triggers -- exception 04 specifically means 'CPU in PROGRAM mode during a protected write', which is operator-recoverable by switching the CPU to RUN; surfacing it as BadDeviceFailure (not BadInternalError) makes the fix obvious. Changes in ModbusDriver: Read catch-chain now ModbusException first (-> mapper), generic Exception second (-> BadCommunicationError); Write catch-chain same pattern but generic Exception stays BadInternalError because write failures can legitimately come from EncodeRegister (out-of-range value) which is a driver-layer fault. Unit tests: MapModbusExceptionToStatus theory exercising every code in the table including the 0xFF fallback; Read_surface_exception_02_as_BadOutOfRange with an ExceptionRaisingTransport that forces code 02; Write_surface_exception_04_as_BadDeviceFailure for CPU-mode faults; Read_non_modbus_failure_maps_to_BadCommunicationError with a NonModbusFailureTransport that raises EndOfStreamException. 115/115 Modbus.Tests pass. Integration test: DL205ExceptionCodeTests.DL205_FC03_at_unmapped_register_returns_BadOutOfRange reads HR[16383] which is beyond the seeded uint16 cells on the dl205.json profile; pymodbus returns exception 02 and the driver surfaces BadOutOfRange. 11/11 DL205 integration tests pass with MODBUS_SIM_PROFILE=dl205.
2026-04-18 22:28:37 -04:00
Phase 3 PR 56 -- Siemens S7-1500 pymodbus profile + smoke integration test. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Pymodbus/s7_1500.json modelling the SIMATIC S7-1500 + MB_SERVER default deployment documented in docs/v2/s7.md: DB1.DBW0 = 0xABCD fingerprint marker (operators reserve this so clients can verify they're talking to the right DB), scratch HR range 200..209 for write-roundtrip tests mirroring dl205.json + standard.json, Float32 1.5f at HR[100..101] in ABCD word order (high word first -- OPPOSITE of DL260 CDAB), Int32 0x12345678 at HR[300..301] in ABCD. Also seeds a coil at bit-addr 400 (= cell 25 bit 0) and a discrete input at bit-addr 500 (= cell 31 bit 0) so future S7-specific tests for FC01/FC02 have stable markers. shared blocks=true to match the proven dl205.json pattern (pymodbus's bits/uint16 cells coexist cleanly when addresses don't collide). Write list references cells (0, 25, 100-101, 200-209, 300-301), not bit addresses -- pymodbus's write-range entries are cell-indexed, not bit-indexed. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/ directory with S7_1500Profile.cs (mirrors DL205Profile pattern: SmokeHoldingRegister=200, SmokeHoldingValue=4321, BuildOptions tags + probe-disabled + 2s timeout) and S7_1500SmokeTests.cs (single fact S7_1500_roundtrip_write_then_read_of_holding_register that writes SmokeHoldingValue then reads it back, asserting both write status 0 and read status 0 + value equality). Gates on MODBUS_SIM_PROFILE=s7_1500 so the test skips cleanly against other profiles. csproj updated to copy S7/** to test output as PreserveNewest (pattern matching DL205/**). Pymodbus/serve.ps1 ValidateSet extended from {standard,dl205} to {standard,dl205,s7_1500,mitsubishi} -- mitsubishi.json lands in PR 58 but the validator slot is claimed now so the serve.ps1 diff is one line in this PR and zero lines in future PRs. Verified end-to-end: smoke test 1/1 passes against the running pymodbus s7_1500 profile (localhost:5020 FC06 write of 4321 at HR[200] + FC03 read back). 143/143 Modbus.Tests pass, no regression in driver code because this PR is purely test-asset. Per-quirk S7 integration tests (ABCD word order default, FC23 IllegalFunction, MB_SERVER STATUS 0x8383 behaviour, port-per-connection semantics) land in PR 57+.
2026-04-18 22:57:03 -04:00
Phase 3 PR 56 -- Siemens S7-1500 pymodbus profile + smoke integration test. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Pymodbus/s7_1500.json modelling the SIMATIC S7-1500 + MB_SERVER default deployment documented in docs/v2/s7.md: DB1.DBW0 = 0xABCD fingerprint marker (operators reserve this so clients can verify they're talking to the right DB), scratch HR range 200..209 for write-roundtrip tests mirroring dl205.json + standard.json, Float32 1.5f at HR[100..101] in ABCD word order (high word first -- OPPOSITE of DL260 CDAB), Int32 0x12345678 at HR[300..301] in ABCD. Also seeds a coil at bit-addr 400 (= cell 25 bit 0) and a discrete input at bit-addr 500 (= cell 31 bit 0) so future S7-specific tests for FC01/FC02 have stable markers. shared blocks=true to match the proven dl205.json pattern (pymodbus's bits/uint16 cells coexist cleanly when addresses don't collide). Write list references cells (0, 25, 100-101, 200-209, 300-301), not bit addresses -- pymodbus's write-range entries are cell-indexed, not bit-indexed. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/ directory with S7_1500Profile.cs (mirrors DL205Profile pattern: SmokeHoldingRegister=200, SmokeHoldingValue=4321, BuildOptions tags + probe-disabled + 2s timeout) and S7_1500SmokeTests.cs (single fact S7_1500_roundtrip_write_then_read_of_holding_register that writes SmokeHoldingValue then reads it back, asserting both write status 0 and read status 0 + value equality). Gates on MODBUS_SIM_PROFILE=s7_1500 so the test skips cleanly against other profiles. csproj updated to copy S7/** to test output as PreserveNewest (pattern matching DL205/**). Pymodbus/serve.ps1 ValidateSet extended from {standard,dl205} to {standard,dl205,s7_1500,mitsubishi} -- mitsubishi.json lands in PR 58 but the validator slot is claimed now so the serve.ps1 diff is one line in this PR and zero lines in future PRs. Verified end-to-end: smoke test 1/1 passes against the running pymodbus s7_1500 profile (localhost:5020 FC06 write of 4321 at HR[200] + FC03 read back). 143/143 Modbus.Tests pass, no regression in driver code because this PR is purely test-asset. Per-quirk S7 integration tests (ABCD word order default, FC23 IllegalFunction, MB_SERVER STATUS 0x8383 behaviour, port-per-connection semantics) land in PR 57+.
2026-04-18 22:57:03 -04:00
Phase 3 PR 56 -- Siemens S7-1500 pymodbus profile + smoke integration test. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Pymodbus/s7_1500.json modelling the SIMATIC S7-1500 + MB_SERVER default deployment documented in docs/v2/s7.md: DB1.DBW0 = 0xABCD fingerprint marker (operators reserve this so clients can verify they're talking to the right DB), scratch HR range 200..209 for write-roundtrip tests mirroring dl205.json + standard.json, Float32 1.5f at HR[100..101] in ABCD word order (high word first -- OPPOSITE of DL260 CDAB), Int32 0x12345678 at HR[300..301] in ABCD. Also seeds a coil at bit-addr 400 (= cell 25 bit 0) and a discrete input at bit-addr 500 (= cell 31 bit 0) so future S7-specific tests for FC01/FC02 have stable markers. shared blocks=true to match the proven dl205.json pattern (pymodbus's bits/uint16 cells coexist cleanly when addresses don't collide). Write list references cells (0, 25, 100-101, 200-209, 300-301), not bit addresses -- pymodbus's write-range entries are cell-indexed, not bit-indexed. Adds tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/ directory with S7_1500Profile.cs (mirrors DL205Profile pattern: SmokeHoldingRegister=200, SmokeHoldingValue=4321, BuildOptions tags + probe-disabled + 2s timeout) and S7_1500SmokeTests.cs (single fact S7_1500_roundtrip_write_then_read_of_holding_register that writes SmokeHoldingValue then reads it back, asserting both write status 0 and read status 0 + value equality). Gates on MODBUS_SIM_PROFILE=s7_1500 so the test skips cleanly against other profiles. csproj updated to copy S7/** to test output as PreserveNewest (pattern matching DL205/**). Pymodbus/serve.ps1 ValidateSet extended from {standard,dl205} to {standard,dl205,s7_1500,mitsubishi} -- mitsubishi.json lands in PR 58 but the validator slot is claimed now so the serve.ps1 diff is one line in this PR and zero lines in future PRs. Verified end-to-end: smoke test 1/1 passes against the running pymodbus s7_1500 profile (localhost:5020 FC06 write of 4321 at HR[200] + FC03 read back). 143/143 Modbus.Tests pass, no regression in driver code because this PR is purely test-asset. Per-quirk S7 integration tests (ABCD word order default, FC23 IllegalFunction, MB_SERVER STATUS 0x8383 behaviour, port-per-connection semantics) land in PR 57+.
2026-04-18 22:57:03 -04:00