diff --git a/pymodbus/pdu/bit_message.py b/pymodbus/pdu/bit_message.py index 6579fbe23..a82f3d1e3 100644 --- a/pymodbus/pdu/bit_message.py +++ b/pymodbus/pdu/bit_message.py @@ -23,7 +23,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a request pdu.""" - self.address, self.count = struct.unpack(">HH", data) + self.address, self.count = struct.unpack(">HH", data[:4]) def get_response_pdu_size(self) -> int: """Get response pdu size. @@ -84,7 +84,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a write coil request.""" - self.address, value = struct.unpack(">HH", data) + self.address, value = struct.unpack(">HH", data[:4]) self.bits = [bool(value)] @@ -157,4 +157,4 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a write coils response.""" - self.address, self.count = struct.unpack(">HH", data) + self.address, self.count = struct.unpack(">HH", data[:4]) diff --git a/pymodbus/pdu/diag_message.py b/pymodbus/pdu/diag_message.py index bed347853..3dd603f8f 100644 --- a/pymodbus/pdu/diag_message.py +++ b/pymodbus/pdu/diag_message.py @@ -54,9 +54,9 @@ def decode(self, data: bytes) -> None: data_len += 1 data += b"0" if (word_len := data_len // 2) == 1: - (self.message,) = struct.unpack(">H", data) + (self.message,) = struct.unpack(">H", data[:2]) else: - self.message = struct.unpack(">" + "H" * word_len, data) + self.message = struct.unpack(">" + "H" * word_len, data[:2 * word_len]) def get_response_pdu_size(self) -> int: """Get response pdu size. diff --git a/pymodbus/pdu/file_message.py b/pymodbus/pdu/file_message.py index 459e8baa8..27ea7bee4 100644 --- a/pymodbus/pdu/file_message.py +++ b/pymodbus/pdu/file_message.py @@ -235,7 +235,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode the incoming request.""" - self.address = struct.unpack(">H", data)[0] + self.address = struct.unpack(">H", data[:2])[0] async def update_datastore(self, _context: ModbusDeviceContext) -> ModbusPDU: """Run a read exception status request against the store.""" diff --git a/pymodbus/pdu/mei_message.py b/pymodbus/pdu/mei_message.py index 6c8b6a49e..455aaba26 100644 --- a/pymodbus/pdu/mei_message.py +++ b/pymodbus/pdu/mei_message.py @@ -49,7 +49,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode data part of the message.""" - self.sub_function_code, self.read_code, self.object_id = struct.unpack(">BBB", data) + self.sub_function_code, self.read_code, self.object_id = struct.unpack(">BBB", data[:3]) async def update_datastore(self, _context: ModbusDeviceContext) -> ModbusPDU: """Run a read exception status request against the store.""" diff --git a/pymodbus/pdu/other_message.py b/pymodbus/pdu/other_message.py index ae973c9f6..cd58cb1f4 100644 --- a/pymodbus/pdu/other_message.py +++ b/pymodbus/pdu/other_message.py @@ -81,7 +81,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a the response.""" - ready, self.count = struct.unpack(">HH", data) + ready, self.count = struct.unpack(">HH", data[:4]) self.status = ready == ModbusStatus.READY diff --git a/pymodbus/pdu/register_message.py b/pymodbus/pdu/register_message.py index aa199acca..1bb3da059 100644 --- a/pymodbus/pdu/register_message.py +++ b/pymodbus/pdu/register_message.py @@ -1,4 +1,5 @@ """Register Reading Request/Response.""" + from __future__ import annotations import struct @@ -24,7 +25,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a register request packet.""" - self.address, self.count = struct.unpack(">HH", data) + self.address, self.count = struct.unpack(">HH", data[:4]) def get_response_pdu_size(self) -> int: """Get response pdu size. @@ -38,8 +39,16 @@ async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: values = await context.async_getValues( self.function_code, self.address, self.count ) - response_class = (ReadHoldingRegistersResponse if self.function_code == 3 else ReadInputRegistersResponse) - return response_class(registers=cast(list[int], values), dev_id=self.dev_id, transaction_id=self.transaction_id) + response_class = ( + ReadHoldingRegistersResponse + if self.function_code == 3 + else ReadInputRegistersResponse + ) + return response_class( + registers=cast(list[int], values), + dev_id=self.dev_id, + transaction_id=self.transaction_id, + ) class ReadHoldingRegistersResponse(ModbusPDU): @@ -59,7 +68,9 @@ def decode(self, data: bytes) -> None: """Decode a register response packet.""" self.registers = [] if (data_len := int(data[0])) >= len(data): - raise ModbusIOException(f"byte_count {data_len} > length of packet {len(data)}") + raise ModbusIOException( + f"byte_count {data_len} > length of packet {len(data)}" + ) for i in range(1, data_len, 2): self.registers.append(struct.unpack(">H", data[i : i + 2])[0]) @@ -82,13 +93,15 @@ class ReadWriteMultipleRegistersRequest(ModbusPDU): function_code = 23 rtu_byte_count_pos = 10 - def __init__(self, - read_address: int = 0x00, - read_count: int = 0, - write_address: int = 0x00, - write_registers: list[int] | None = None, - dev_id: int = 1, - transaction_id: int = 0) -> None: + def __init__( + self, + read_address: int = 0x00, + read_count: int = 0, + write_address: int = 0x00, + write_registers: list[int] | None = None, + dev_id: int = 1, + transaction_id: int = 0, + ) -> None: """Initialize a new request message.""" if not write_registers: write_registers = [] @@ -135,9 +148,13 @@ def decode(self, data: bytes) -> None: async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: """Run a write single register request against a datastore.""" if not (1 <= self.read_count <= 0x07D): - return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE) + return ExceptionResponse( + self.function_code, ExceptionResponse.ILLEGAL_VALUE + ) if not 1 <= self.write_count <= 0x079: - return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE) + return ExceptionResponse( + self.function_code, ExceptionResponse.ILLEGAL_VALUE + ) rc = await context.async_setValues( self.function_code, self.write_address, self.write_registers ) @@ -146,7 +163,11 @@ async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: registers = await context.async_getValues( self.function_code, self.read_address, self.read_count ) - return ReadWriteMultipleRegistersResponse(registers=cast(list[int], registers), dev_id=self.dev_id, transaction_id=self.transaction_id) + return ReadWriteMultipleRegistersResponse( + registers=cast(list[int], registers), + dev_id=self.dev_id, + transaction_id=self.transaction_id, + ) def get_response_pdu_size(self) -> int: """Get response pdu size. @@ -174,7 +195,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a write single register packet packet request.""" - self.address, register = struct.unpack(">HH", data) + self.address, register = struct.unpack(">HH", data[:4]) self.registers = [register] @@ -184,14 +205,18 @@ class WriteSingleRegisterRequest(WriteSingleRegisterResponse): async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: """Run a write single register request against a datastore.""" if not 0 <= self.registers[0] <= 0xFFFF: - return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE) + return ExceptionResponse( + self.function_code, ExceptionResponse.ILLEGAL_VALUE + ) rc = await context.async_setValues( self.function_code, self.address, self.registers ) if rc: return ExceptionResponse(self.function_code, rc) values = await context.async_getValues(self.function_code, self.address, 1) - return WriteSingleRegisterResponse(address=self.address, registers=cast(list[int], values)) + return WriteSingleRegisterResponse( + address=self.address, registers=cast(list[int], values) + ) def get_response_pdu_size(self) -> int: """Get response pdu size. @@ -225,13 +250,20 @@ def decode(self, data: bytes) -> None: async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: """Run a write single register request against a datastore.""" if not 1 <= self.count <= 0x07B: - return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE) + return ExceptionResponse( + self.function_code, ExceptionResponse.ILLEGAL_VALUE + ) rc = await context.async_setValues( self.function_code, self.address, self.registers - ) + ) if rc: return ExceptionResponse(self.function_code, rc) - return WriteMultipleRegistersResponse(address=self.address, count=self.count, dev_id=self.dev_id, transaction_id=self.transaction_id) + return WriteMultipleRegistersResponse( + address=self.address, + count=self.count, + dev_id=self.dev_id, + transaction_id=self.transaction_id, + ) def get_response_pdu_size(self) -> int: """Get response pdu size. @@ -253,7 +285,7 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a write single register packet packet request.""" - self.address, self.count = struct.unpack(">HH", data) + self.address, self.count = struct.unpack(">HH", data[:4]) class MaskWriteRegisterRequest(ModbusPDU): @@ -262,7 +294,14 @@ class MaskWriteRegisterRequest(ModbusPDU): function_code = 0x16 rtu_frame_size = 10 - def __init__(self, address=0x0000, and_mask=0xFFFF, or_mask=0x0000, dev_id=1, transaction_id=0) -> None: + def __init__( + self, + address=0x0000, + and_mask=0xFFFF, + or_mask=0x0000, + dev_id=1, + transaction_id=0, + ) -> None: """Initialize a new instance.""" super().__init__(transaction_id=transaction_id, dev_id=dev_id, address=address) self.and_mask = and_mask @@ -274,22 +313,34 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode the incoming request.""" - self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data) + self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data[:6]) async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: """Run a mask write register request against the store.""" if not 0x0000 <= self.and_mask <= 0xFFFF: - return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE) + return ExceptionResponse( + self.function_code, ExceptionResponse.ILLEGAL_VALUE + ) if not 0x0000 <= self.or_mask <= 0xFFFF: - return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE) + return ExceptionResponse( + self.function_code, ExceptionResponse.ILLEGAL_VALUE + ) values = await context.async_getValues(self.function_code, self.address, 1) - values = (cast(Sequence[int | bool], values)[0] & self.and_mask) | (self.or_mask & ~self.and_mask) + values = (cast(Sequence[int | bool], values)[0] & self.and_mask) | ( + self.or_mask & ~self.and_mask + ) rc = await context.async_setValues( self.function_code, self.address, cast(list[int], [values]) ) if rc: return ExceptionResponse(self.function_code, rc) - return MaskWriteRegisterResponse(address=self.address, and_mask=self.and_mask, or_mask=self.or_mask, dev_id=self.dev_id, transaction_id=self.transaction_id) + return MaskWriteRegisterResponse( + address=self.address, + and_mask=self.and_mask, + or_mask=self.or_mask, + dev_id=self.dev_id, + transaction_id=self.transaction_id, + ) class MaskWriteRegisterResponse(ModbusPDU): @@ -298,7 +349,14 @@ class MaskWriteRegisterResponse(ModbusPDU): function_code = 0x16 rtu_frame_size = 10 - def __init__(self, address=0x0000, and_mask=0xFFFF, or_mask=0x0000, dev_id=1, transaction_id=0) -> None: + def __init__( + self, + address=0x0000, + and_mask=0xFFFF, + or_mask=0x0000, + dev_id=1, + transaction_id=0, + ) -> None: """Initialize new instance.""" super().__init__(transaction_id=transaction_id, dev_id=dev_id, address=address) self.and_mask = and_mask @@ -311,4 +369,4 @@ def encode(self) -> bytes: def decode(self, data: bytes) -> None: """Decode a the response.""" - self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data) + self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data[:6]) diff --git a/test/framer/test_framer.py b/test/framer/test_framer.py index b2bf29d78..93f90184b 100644 --- a/test/framer/test_framer.py +++ b/test/framer/test_framer.py @@ -69,6 +69,14 @@ def test_roundtrip_CRC(self): assert FramerRTU.compute_CRC(data) == 0xE2DB assert FramerRTU.check_CRC(data, 0xE2DB) + async def test_handleFrame2(self): + """Test handleFrame.""" + test_framer = FramerRTU(DecodePDU(True)) + msg = b"\xfe\x04\x00\x03\x00\x01\xd5\xc5\x00" + used_len, pdu = test_framer.handleFrame(msg, 0, 0) + assert used_len == len(msg) + assert pdu + class TestFramerType: """Test classes."""