Skip to content

Read the real CPU state from SZL 0x0424 - #988

Merged
gijzelaerr merged 1 commit into
masterfrom
fix/real-cpu-state
Nov 11, 2026
Merged

gijzelaerr merged 1 commit into
masterfrom
fix/real-cpu-state

Conversation

@gijzelaerr

Copy link
Copy Markdown
Owner

get_cpu_state() sent a read request with no address specification and always returned S7CpuStatusRun. It passed only because the server answered such a request with default address values.

Both clients now read SZL 0x0424, the server answers it from its real CPU state, and the server refuses a read request with no address. The heartbeat probe treats a refusal as proof the connection is alive. The old builder and extractor stay as deprecated public names.

Commits

  • Read the real CPU state from SZL 0x0424

Testing

  • Test suite and mypy pass locally.
  • pre-commit passes except end-of-file-fixer on .dockerignore, which is a symlink artefact on Windows.
  • Not tested on hardware.

🤖 Generated with Claude Code

get_cpu_state() sent a read request whose parameter section was one byte
long and returned "S7CpuStatusRun" without looking at the reply. It only
appeared to work because the server answered a read request that has no
address with the first four bytes of DB1.

Both clients now read SZL 0x0424 index 0 through read_szl and decode it
with snap7.szl.parse_cpu_state_szl. The payload is the partial-list
header (LENTHDR = 0x0014, N_DR = 1) followed by one 20-byte record:

    ereig(2) ae(1) bzu-id(1) res(4) anlinfo1..4(4) time(8)

bzu-id holds the requested mode in bits 0-3 and the previous mode in
bits 4-7: 1-4 are STOP variants, 5 and 7 startup, 8 RUN, 0xA HOLD,
0xD DEFECT.

Sources for the layout:
- Wireshark, epan/dissectors/packet-s7comm_szl_ids.c,
  s7comm_decode_szl_id_0424_idx_0000 and szl_0424_0000_bzu_id_names.
- Snap7 (github.com/SCADACS/snap7), src/core/s7_micro_client.cpp:
  opGetPlcStatus reads opData[7], the same byte after the 4-byte
  partial-list header. In src/core/s7_server.cpp, SZL_ID424 writes the
  CPU status at offset 15 of the reply data (8 bytes of data and SZL
  header, 4 bytes of partial-list header, record offset 3).
The Siemens reference manual "System Software for S7-300/400 System and
Standard Functions" (05/2010, A5E02789976-01) has no section for
SSL-ID W#16#xy24, so it could not be used.

The two sources read the byte differently. Snap7 compares the whole byte
with 0x00, 0x04 and 0x08 and reports anything else as STOP. This change
reports a zero byte as Unknown, a requested-mode nibble of 8 as Run and
everything else as Stop. That agrees with Snap7 for 0x00, 0x03, 0x04 and
0x08, and differs only when RUN comes with a non-zero previous mode,
which Snap7 would report as Stop.

Not tested on hardware. The record layout and the mapping come from the
sources above and from the server in this repository only.

Also:
- The server answers SZL 0x0424 from its CPU state and lists the ID in
  SZL 0x0000.
- The server refuses a read request without an address specification
  instead of falling back to DB1, offset 0, 4 bytes.
- The heartbeat of the synchronous client treats an answered refusal as
  a live connection. A CPU that rejects SZL 0x0424 would otherwise be
  reconnected on every interval. Transport failures and lost or stale
  replies still mark the connection dead.
- S7Protocol.build_cpu_state_request and extract_cpu_state are kept and
  documented as deprecated.
@gijzelaerr
gijzelaerr enabled auto-merge (squash) November 11, 2026 13:26
@gijzelaerr
gijzelaerr merged commit 429b398 into master Nov 11, 2026
20 checks passed
@gijzelaerr
gijzelaerr deleted the fix/real-cpu-state branch November 11, 2026 13:34
@gijzelaerr gijzelaerr mentioned this pull request Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant