Description
After upgrading from python-snap7==2.1 to the pure-Python implementation 3.1.0, the method get_order_code() fails to correctly parse the SZL 0x0011 response bytes on an S7-1500 CPU (tested on CPU 1516F-3 PN/DP and CPU 1511F-1 PN).
While version 2.1 (using the native C library wrapper) parses the catalog code and firmware version flawlessly, version 3.1.0 outputs an empty bytes string for the order code and corrupted numerical representations for the hardware/firmware versions.
Environment
OS: Windows 10/11
Python version: 3.14.6
python-snap7 version: 3.1.0
PLC Hardware: Siemens S7-1516F-3 PN/DP (Firmware V3.3.0)
Code Snippet Used
import snap7
plc = snap7.client.Client()
plc.connect("192.168.0.100", rack=0, slot=1)
if plc.get_connected():
info = plc.get_cpu_info()
print("Module:", info.ModuleTypeName)
order_code = plc.get_order_code()
print("Catalog Code (MLFB):", order_code.OrderCode)
print(f"Version: {order_code.V1}.{order_code.V2}.{order_code.V3}")
plc.disconnect()
Step to Reproduce & Outputs Comparison
Expected Output (with python-snap7 2.1):
Module: b'CPU 1516F-3 PN/DP'
Catalog Code (MLFB): b'6ES7 516-3FN02-0AB0'
Version: 3.3.0
Actual Output (with python-snap7 3.1.0):
Module: b'CPU 1516F-3 PN/DP'
Catalog Code (MLFB): b''
Version: 45.48.65
Root Cause Analysis (Found in szl.py)
The issue is located inside the parse_order_code_szl function within szl.py:
def parse_order_code_szl(szl: S7SZL) -> S7OrderCode:
"""Decode SZL 0x0011 (module order code + firmware version) into :class:`S7OrderCode`."""
order_code = S7OrderCode()
data = _szl_data(szl)
if len(data) >= 20:
order_code.OrderCode = data[0:20].rstrip(b"\x00")
if len(data) >= 21:
order_code.V1 = data[20]
if len(data) >= 22:
order_code.V2 = data[21]
if len(data) >= 23:
order_code.V3 = data[22]
return order_code
The function relies on a hardcoded static slice (data[0:20]), assuming the payload structure is identical for every S7 CPU family.
However, the SZL response consists of dynamic records. Older S7-300 controllers place the MLFB record almost immediately. In contrast, modern S7-1500 firmware includes additional diagnostic records before the MLFB, dynamically shifting the actual order code string forward in the raw network payload.
Because of this rigid mapping, on an S7-1500:
data[0:20] reads empty padding bytes, which results in an empty string b'' after evaluating .rstrip(b"\x00").
- The firmware fields
V1, V2, and V3 read indices 20, 21, and 22, which now map into the actual text string of the shifted MLFB payload instead of the intended version integers.
- The corrupted output
45.48.65 translates perfectly from decimal integers to ASCII characters as -, 0, A (from ASCII table: 45='-', 48='0', 65='A'), which are part of the CPU's real MLFB catalog suffix (-0AB0).
Suggested Fix
Instead of fixed index slicing, parse_order_code_szl should iterate dynamically through the SZL record headers to locate the specific ID block containing the MLFB data, mirroring the robust architecture implemented in the original native C backend of Snap7.
Description
After upgrading from
python-snap7==2.1to the pure-Python implementation 3.1.0, the methodget_order_code()fails to correctly parse the SZL 0x0011 response bytes on an S7-1500 CPU (tested on CPU 1516F-3 PN/DP and CPU 1511F-1 PN).While version 2.1 (using the native C library wrapper) parses the catalog code and firmware version flawlessly, version 3.1.0 outputs an empty bytes string for the order code and corrupted numerical representations for the hardware/firmware versions.
Environment
OS: Windows 10/11
Python version: 3.14.6
python-snap7 version: 3.1.0
PLC Hardware: Siemens S7-1516F-3 PN/DP (Firmware V3.3.0)
Code Snippet Used
Step to Reproduce & Outputs Comparison
Expected Output (with python-snap7 2.1):
Actual Output (with python-snap7 3.1.0):
Root Cause Analysis (Found in szl.py)
The issue is located inside the
parse_order_code_szlfunction withinszl.py:The function relies on a hardcoded static slice (
data[0:20]), assuming the payload structure is identical for every S7 CPU family.However, the SZL response consists of dynamic records. Older S7-300 controllers place the MLFB record almost immediately. In contrast, modern S7-1500 firmware includes additional diagnostic records before the MLFB, dynamically shifting the actual order code string forward in the raw network payload.
Because of this rigid mapping, on an S7-1500:
data[0:20]reads empty padding bytes, which results in an empty stringb''after evaluating.rstrip(b"\x00").V1,V2, andV3read indices 20, 21, and 22, which now map into the actual text string of the shifted MLFB payload instead of the intended version integers.45.48.65translates perfectly from decimal integers to ASCII characters as-,0,A(from ASCII table: 45='-', 48='0', 65='A'), which are part of the CPU's real MLFB catalog suffix (-0AB0).Suggested Fix
Instead of fixed index slicing,
parse_order_code_szlshould iterate dynamically through the SZL record headers to locate the specific ID block containing the MLFB data, mirroring the robust architecture implemented in the original native C backend of Snap7.