diff --git a/s7/_s7commplus_client.py b/s7/_s7commplus_client.py index c36527bf..ba4bd33f 100644 --- a/s7/_s7commplus_client.py +++ b/s7/_s7commplus_client.py @@ -488,6 +488,7 @@ def _build_read_payload(items: list[tuple[int, int, int]]) -> bytes: for addr in addresses: payload += addr payload += encode_object_qualifier() + payload += encode_uint32_vlq(1) payload += struct.pack(">I", 0) return bytes(payload) @@ -580,6 +581,7 @@ def _build_write_payload(items: list[tuple[int, int, bytes]]) -> bytes: payload += encode_pvalue_blob(data) payload += bytes([0x00]) payload += encode_object_qualifier() + payload += encode_uint32_vlq(1) payload += struct.pack(">I", 0) return bytes(payload) @@ -632,6 +634,7 @@ def _build_area_read_payload(area_rid: int, start: int, size: int) -> bytes: payload += encode_uint32_vlq(field_count) payload += addr_bytes payload += encode_object_qualifier() + payload += encode_uint32_vlq(1) payload += struct.pack(">I", 0) return bytes(payload) @@ -653,6 +656,7 @@ def _build_area_write_payload(area_rid: int, start: int, data: bytes) -> bytes: payload += encode_pvalue_blob(data) payload += bytes([0x00]) payload += encode_object_qualifier() + payload += encode_uint32_vlq(1) payload += struct.pack(">I", 0) return bytes(payload) @@ -685,6 +689,7 @@ def _build_symbolic_read_payload(access_area: int, lids: list[int], symbol_crc: payload += encode_uint32_vlq(field_count) payload += addr_bytes payload += encode_object_qualifier() + payload += encode_uint32_vlq(1) payload += struct.pack(">I", 0) return bytes(payload) @@ -712,6 +717,7 @@ def _build_symbolic_write_payload(access_area: int, lids: list[int], data: bytes payload += encode_pvalue_blob(data) payload += bytes([0x00]) payload += encode_object_qualifier() + payload += encode_uint32_vlq(1) payload += struct.pack(">I", 0) return bytes(payload) diff --git a/tests/test_s7_unit.py b/tests/test_s7_unit.py index 12191eb3..cefb2e8f 100644 --- a/tests/test_s7_unit.py +++ b/tests/test_s7_unit.py @@ -9,13 +9,29 @@ _parse_read_response, _build_write_payload, _parse_write_response, +<<<<<<< HEAD _build_explore_request, _parse_explore_datablocks, +||||||| parent of 22a5c08 (fix: add SequenceNumber to Get/SetMultiVariables payloads (#738)) +======= + _build_area_read_payload, + _build_area_write_payload, + _build_symbolic_read_payload, + _build_symbolic_write_payload, +>>>>>>> 22a5c08 (fix: add SequenceNumber to Get/SetMultiVariables payloads (#738)) ) +<<<<<<< HEAD from s7.connection import S7CommPlusConnection from s7.codec import encode_pvalue_blob from s7.codec import _pvalue_element_size as _element_size from s7.codec import skip_typed_value, parse_server_session_version +||||||| parent of 22a5c08 (fix: add SequenceNumber to Get/SetMultiVariables payloads (#738)) +from s7.codec import encode_pvalue_blob +from s7.connection import S7CommPlusConnection, _element_size +======= +from s7.codec import encode_object_qualifier, encode_pvalue_blob +from s7.connection import S7CommPlusConnection, _element_size +>>>>>>> 22a5c08 (fix: add SequenceNumber to Get/SetMultiVariables payloads (#738)) from s7.protocol import DataType, ElementID, ObjectId from s7.vlq import ( encode_uint32_vlq, @@ -194,6 +210,42 @@ def test_write_read_consistency(self) -> None: assert isinstance(write_payload, bytes) +class TestSequenceNumber: + """Verify all payload builders include a SequenceNumber after ObjectQualifier.""" + + @staticmethod + def _has_sequence_number(payload: bytes) -> bool: + oq = encode_object_qualifier() + idx = bytes(payload).find(oq) + assert idx >= 0, "ObjectQualifier not found in payload" + seq_offset = idx + len(oq) + return payload[seq_offset : seq_offset + 1] == encode_uint32_vlq(1) + + def test_read_payload_has_sequence_number(self) -> None: + payload = _build_read_payload([(1, 0, 4)]) + assert self._has_sequence_number(payload) + + def test_write_payload_has_sequence_number(self) -> None: + payload = _build_write_payload([(1, 0, bytes([1, 2, 3, 4]))]) + assert self._has_sequence_number(payload) + + def test_area_read_payload_has_sequence_number(self) -> None: + payload = _build_area_read_payload(82, 0, 4) + assert self._has_sequence_number(payload) + + def test_area_write_payload_has_sequence_number(self) -> None: + payload = _build_area_write_payload(82, 0, b"\x00\x00\x00\x00") + assert self._has_sequence_number(payload) + + def test_symbolic_read_payload_has_sequence_number(self) -> None: + payload = _build_symbolic_read_payload(0x8A0E0001, [1, 4]) + assert self._has_sequence_number(payload) + + def test_symbolic_write_payload_has_sequence_number(self) -> None: + payload = _build_symbolic_write_payload(0x8A0E0001, [1, 4], b"\x01") + assert self._has_sequence_number(payload) + + # -- Connection unit tests --