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
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Unreleased
* Compress now uses the PI service `_GARB` and copy RAM to ROM uses file system `EP` with `_MODU`, matching real
engineering tools and native Snap7, instead of the invented `_MSZL`. The bundled server recognises the new
requests (#941).
* Read-SZL requests now carry the `0xFF`/octet-string data header and continuation requests the 12-byte parameter
block (method `0x12`) that real engineering tools send; both previously used the header of a request without data
and an 8-byte parameter block (#942).

3.2.1
-----
Expand Down
17 changes: 11 additions & 6 deletions snap7/s7protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -1059,8 +1059,8 @@ def build_read_szl_request(self, szl_id: int, szl_index: int) -> bytes:
# Data section: SZL ID and Index
data_section = struct.pack(
">BBHHH",
0x0A, # Return value (request)
0x00, # Transport size
0xFF, # Return value (0xFF, as engineering tools and native Snap7 send for requests carrying data)
0x09, # Transport size (octet string)
0x0004, # Length (4 bytes for ID + Index)
szl_id, # SZL ID
szl_index, # SZL Index
Expand Down Expand Up @@ -1091,18 +1091,23 @@ def build_userdata_followup_request(self, group: int, subfunction: int, sequence
Returns:
Complete S7 PDU for follow-up request
"""
# Parameter section: same as initial but with DataRef = sequence_number
# Parameter section as sent by engineering tools for continuation requests: 12 bytes, method 0x12,
# DataRef = sequence_number, followed by last-data-unit and error code fields set to zero
type_group = 0x40 | (group & 0x0F) # Type 4 (request) | group
param_data = struct.pack(
">BBBBBBBB",
">BBBBBBBBBBBB",
0x00, # Reserved
0x01, # Parameter count
0x12, # Type/length header
0x04, # Length of following data
0x11, # Method (0x11 = request)
0x08, # Length of following data
0x12, # Method (0x12)
type_group, # Type | Group
subfunction, # Subfunction
sequence_number, # DataRef from previous response
0x00, # Last data unit
0x00, # Reserved
0x00, # Error code high
0x00, # Error code low
)

# Minimal data section for follow-up
Expand Down
7 changes: 4 additions & 3 deletions tests/test_multipacket.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,20 @@ def test_szl_followup(self) -> None:
# Extract param_len and data_len from header
param_len = struct.unpack(">H", pdu[6:8])[0]
data_len = struct.unpack(">H", pdu[8:10])[0]
assert param_len == 8
assert param_len == 12
assert data_len == 4

# Parameter section starts at offset 10
params = pdu[10 : 10 + param_len]
assert params[0] == 0x00 # Reserved
assert params[1] == 0x01 # Param count
assert params[2] == 0x12 # Type header
assert params[3] == 0x04 # Length
assert params[4] == 0x11 # Method (request)
assert params[3] == 0x08 # Length
assert params[4] == 0x12 # Method, as engineering tools send for continuation requests
assert params[5] == 0x44 # Type(4) | Group(4=SZL)
assert params[6] == 0x01 # Subfunction
assert params[7] == 0x02 # DataRef = sequence_number
assert params[8:12] == bytes(4)

# Data section
data = pdu[10 + param_len :]
Expand Down
6 changes: 6 additions & 0 deletions tests/test_s7protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ def test_copy_ram_to_rom_request_matches_real_job(self) -> None:
request = self.proto.build_copy_ram_to_rom_request()
assert request[10:] == bytes.fromhex("28000000000000fd00024550055f4d4f4455")

def test_read_szl_request_matches_real_tool(self) -> None:
# Captured from a real S7-300 session: SZL id 0x0132 index 4. Bytes 4-5 are the PDU reference.
real = bytes.fromhex("320700000300000800080001120411440100ff09000401320004")
request = self.proto.build_read_szl_request(0x0132, 4)
assert request[:4] + request[6:] == real[:4] + real[6:]

def test_request_db_download(self) -> None:
assert self.proto.build_download_request(0x41, 1, bytes(64)).hex() == (
"320100000001002000001a00010000000000095f30413030303031500d31303030303634303030303238"
Expand Down
Loading