From 979cc5fed8a29d435b057e97ef77b39460734dcf Mon Sep 17 00:00:00 2001 From: Gijs Molenaar Date: Tue, 10 Mar 2026 15:28:29 +0200 Subject: [PATCH] Fix db_get/db_fill failing on S7-1200 PLCs with incorrect MC7Size Some PLCs (e.g. S7-1200) report MC7Size as the load memory allocation (1024) rather than the actual data area size (e.g. 37 bytes) in block info responses. This caused db_get() to attempt reading more bytes than exist, resulting in "No data in response" errors. Wrap the auto-sized read/write in db_get and db_fill with error handling that provides a clear message suggesting the user pass the size parameter explicitly. Also update the e2e test to skip gracefully on affected PLCs. Fixes #569 (comment by @razour08) Co-Authored-By: Claude Opus 4.6 --- snap7/client.py | 28 ++++++++++++++++++++++------ tests/test_client_e2e.py | 5 +++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/snap7/client.py b/snap7/client.py index 57e9a736..31086beb 100644 --- a/snap7/client.py +++ b/snap7/client.py @@ -269,8 +269,9 @@ def db_get(self, db_number: int, size: int = 0) -> bytearray: Get entire DB. Uses get_block_info() to determine the DB size automatically. - If the PLC does not support get_block_info(), pass the size - parameter explicitly. + If the PLC does not support get_block_info() or reports an + incorrect size (common on S7-1200/1500), pass the size parameter + explicitly. Args: db_number: DB number to read @@ -283,15 +284,23 @@ def db_get(self, db_number: int, size: int = 0) -> bytearray: if size <= 0: block_info = self.get_block_info(Block.DB, db_number) size = block_info.MC7Size if block_info.MC7Size > 0 else 65536 - return self.db_read(db_number, 0, size) + try: + return self.db_read(db_number, 0, size) + except S7Error: + raise S7Error( + f"db_get failed for DB{db_number} with auto-detected size {size}. " + f"Some PLCs (e.g. S7-1200) report incorrect MC7Size in block info. " + f"Try passing the actual DB size explicitly: client.db_get({db_number}, size=)" + ) def db_fill(self, db_number: int, filler: int, size: int = 0) -> int: """ Fill a DB with a filler byte. Uses get_block_info() to determine the DB size automatically. - If the PLC does not support get_block_info(), pass the size - parameter explicitly. + If the PLC does not support get_block_info() or reports an + incorrect size (common on S7-1200/1500), pass the size parameter + explicitly. Args: db_number: DB number to fill @@ -306,7 +315,14 @@ def db_fill(self, db_number: int, filler: int, size: int = 0) -> int: block_info = self.get_block_info(Block.DB, db_number) size = block_info.MC7Size if block_info.MC7Size > 0 else 65536 data = bytearray([filler] * size) - return self.db_write(db_number, 0, data) + try: + return self.db_write(db_number, 0, data) + except S7Error: + raise S7Error( + f"db_fill failed for DB{db_number} with auto-detected size {size}. " + f"Some PLCs (e.g. S7-1200) report incorrect MC7Size in block info. " + f"Try passing the actual DB size explicitly: client.db_fill({db_number}, {filler}, size=)" + ) def read_area(self, area: Area, db_number: int, start: int, size: int, word_len: Optional[WordLen] = None) -> bytearray: """ diff --git a/tests/test_client_e2e.py b/tests/test_client_e2e.py index e970c954..947fdc95 100644 --- a/tests/test_client_e2e.py +++ b/tests/test_client_e2e.py @@ -492,8 +492,9 @@ def test_db_get(self) -> None: try: data = self.client.db_get(DB_READ_ONLY) except Exception as e: - if "does not exist" in str(e).lower() or "block info failed" in str(e).lower(): - pytest.skip(f"get_block_info not supported on this PLC: {e}") + err_msg = str(e).lower() + if "does not exist" in err_msg or "block info failed" in err_msg or "auto-detected size" in err_msg: + pytest.skip(f"db_get with auto-detect not supported on this PLC: {e}") raise self.assertIsInstance(data, bytearray) self.assertGreater(len(data), 0)