Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ manual workflow and no releases (`own/go-modbus/README.md`).

---

## v0.4.1

**Date:** 2026-11-09

**Previous release:** v0.4.0

## Summary

j2mod declares `serverFileRecord` false, and the self-tests read two FC20 sub-requests at once.

## Changes

### Fixed

- **`j2mod` declared `serverFileRecord` although j2mod 3.4.0 answers the second of two FC20 sub-requests with the wrong records** (FIXTURES.md rule 10; Modbus Application Protocol V1.1b3, 6.14): `140e0600010000000206000200000002` is answered `140c0506000b0506000100020000`, not `140c0506000b01a4050600010002`. The go-modbus interop suite found it. The server still answers FC20 and FC21 (`serverFunctions`), but the pairings that need `serverFileRecord` now skip the j2mod server.

### Added

- **Self-test** `values.fc20.two-sub-requests`: an FC20 with two sub-requests, sent with `raw`, must return the records of both from the fixture.

---

## v0.4.0

**Date:** 2026-11-08
Expand Down
1 change: 1 addition & 0 deletions adapters/j2mod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ both are `connection-lost`.
| `diagnostics` with other than two data bytes | `ReadSerialDiagnosticsRequest` has exactly one data word. Usage error, nothing sent. |
| file numbers 10000 and above | `SimpleProcessImage.getFileByNumber` refuses them (exception 2). |
| `serverIllegalFunction` | Over TCP an unknown function code closes the connection instead of exception 1: `IllegalFunctionRequest.readData` skips the MBAP length rather than the data length and reads past the frame (finding 1). Over UDP the library answers `c101` correctly, but the feature is declared for the adapter. |
| `serverFileRecord` | FC20 with two sub-requests answers the second one with the wrong records: `ReadFileRecordResponse.getMessage` advances the offset wrongly (finding 5; FIXTURES.md rule 10, spec 6.14). FC20 and FC21 are still answered by the library, with the adapter's checks in front. The self-tests send two sub-requests (`values.fc20.two-sub-requests`). |

## Findings about the library (3.4.0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ private static JsonObject capabilities() {
// FC08 and FC17 request classes answer exception 1 on the server side.
features.addProperty("serverDiagnostics", false);
features.addProperty("serverReportServerId", false);
features.addProperty("serverFileRecord", true);
// FC20 with two sub-requests answers the second with the wrong
// records (README, finding 5; FIXTURES.md rule 10). FC20 and FC21
// are still answered by the library (serverFunctions).
features.addProperty("serverFileRecord", false);
features.addProperty("serverFifoQueue", true);
features.addProperty("clientDiagnostics", true);
features.addProperty("clientReportServerId", true);
Expand Down
8 changes: 5 additions & 3 deletions docs/COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ also build and pass on the release before (v3.1.12 and 3.15.0).
| `clientUnitIdCheck` | **no** | yes | **no** | yes | yes | **no** | **no** | **no** | yes |
| `serverDiagnostics` | **no** | yes | **no** | yes ² | **no** | **no** | **no** | **no** | **no** |
| `serverReportServerId` | **no** ³ | yes | **no** | **no** | **no** ⁴ | **no** | **no** | **no** | yes |
| `serverFileRecord` | **no** | yes | **no** | **no** | **no** | yes | **no** | **no** | **no** |
| `serverFileRecord` | **no** | yes | **no** | **no** | **no** | **no** | **no** | **no** | **no** |
| `serverFifoQueue` | **no** | yes | **no** | **no** | **no** | yes | **no** | **no** | **no** |
| `clientDiagnostics` | **no** | yes | **no** | **no** | **no** | yes | **no** | **no** | **no** |
| `clientReportServerId` | yes | yes | **no** | **no** | yes | yes | **no** | **no** | yes |
Expand Down Expand Up @@ -266,8 +266,10 @@ every response. Details and hex in `adapters/j2mod/README.md`.
- FC20 with two sub-requests (spec 6.14): `140e0600010000000206000200000002`
is answered `140c0506000b0506000100020000`, expected
`140c0506000b01a4050600010002` (`ReadFileRecordResponse.getMessage`
advances the offset wrongly). Not in the self-tests, which read one
sub-request.
advances the offset wrongly). Found by the go-modbus interop suite after
v0.4.0, which declared `serverFileRecord` true; it is false since v0.4.1,
and the self-tests now send two sub-requests
(`values.fc20.two-sub-requests`).
- Over UDP, FC24 and FC20 responses are framed by a data length that is never
updated: FC24 `1804de` is answered one byte short, the empty queue carries
3 stale bytes, FC20 is answered with MBAP length 0 and no PDU.
Expand Down
11 changes: 11 additions & 0 deletions scripts/interop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,17 @@ for server in "${ADAPTERS[@]}"; do
2 0 4 1
FILES
else skip "${pair}: FC20 values"; fi
# Two sub-requests in one FC20 (FIXTURES.md rule 10): records 0..1 of
# file 1 and of file 2. Only raw sends more than one.
at values.fc20.two-sub-requests
if supported "${client}" "${server}" "server:serverFileRecord,client:clientRaw,client:clientRawAnyResponse"; then
got=$(run_client "${client}" raw --function 20 --data 0e0600010000000206000200000002 --host "${name}" | jq -r '.response.data')
# Byte count 0x0c, then per sub-response: length 5, reference type 6, two registers.
want="0c$(jq -r '[.fileRecords[0].records[0:2], .fileRecords[1].records[0:2]][] | map(tostring) | join(" ")' "${FIXTURE}" \
| while read -r r0 r1; do printf '0506%04x%04x' "${r0}" "${r1}"; done)"
if [[ "${got}" == "${want}" ]]; then ok "${pair}: FC20 with two sub-requests returns the records of both"
else fail "${pair}: FC20 with two sub-requests returns ${got}, want ${want}"; fi
else skip "${pair}: FC20 with two sub-requests (needs server:serverFileRecord,client:clientRaw,client:clientRawAnyResponse)"; fi
at values.fc24
if supported "${client}" "${server}" "server:serverFifoQueue,client:clientFifoQueue"; then
for index in 0 1 2; do
Expand Down
2 changes: 1 addition & 1 deletion versions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# The Dockerfiles are the source of truth for a build:
# scripts/check-versions.sh fails when this file disagrees with them.

interop_version: "0.4.0"
interop_version: "0.4.1"
contract_version: "1.2" # docs/CONTAINER_CONTRACT.md, schemaVersion of every document
fixture_schema: "1.1" # fixtures/schema/modbus-fixture.schema.json

Expand Down
Loading