diff --git a/examples/message_parser.py b/examples/message_parser.py index 659d2747a..f5970a126 100755 --- a/examples/message_parser.py +++ b/examples/message_parser.py @@ -80,7 +80,7 @@ def decode(self, message): print(f"{decoder.decoder.__class__.__name__}") print("-" * 80) try: - decoder.processIncomingPacket(message, self.report) + self.report(decoder.processIncomingFrame(message)) except Exception: # pylint: disable=broad-except self.check_errors(decoder, message) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 34ebd1953..1d701b084 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -101,7 +101,7 @@ async def async_execute(self, request) -> ModbusResponse: :meta private: """ request.transaction_id = self.ctx.transaction.getNextTID() - packet = self.ctx.framer.buildPacket(request) + packet = self.ctx.framer.buildFrame(request) count = 0 while count <= self.retries: diff --git a/pymodbus/client/modbusclientprotocol.py b/pymodbus/client/modbusclientprotocol.py index a06025f4d..79f64b114 100644 --- a/pymodbus/client/modbusclientprotocol.py +++ b/pymodbus/client/modbusclientprotocol.py @@ -68,7 +68,8 @@ def callback_data(self, data: bytes, addr: tuple | None = None) -> int: returns number of bytes consumed """ - self.framer.processIncomingPacket(data, self._handle_response) + if (pdu := self.framer.processIncomingFrame(data)): + self._handle_response(pdu) return len(data) def __str__(self): diff --git a/pymodbus/framer/ascii.py b/pymodbus/framer/ascii.py index b39726a7c..cc8a2f3a5 100644 --- a/pymodbus/framer/ascii.py +++ b/pymodbus/framer/ascii.py @@ -33,44 +33,45 @@ class FramerAscii(FramerBase): MIN_SIZE = 10 - def specific_decode(self, data: bytes, data_len: int) -> tuple[int, bytes]: + def decode(self, data: bytes) -> tuple[int, int, int, bytes]: """Decode ADU.""" used_len = 0 + data_len = len(data) while True: if data_len - used_len < self.MIN_SIZE: - return used_len, self.EMPTY + Log.debug("Short frame: {} wait for more data", data, ":hex") + return used_len, 0, 0, self.EMPTY buffer = data[used_len:] if buffer[0:1] != self.START: if (i := buffer.find(self.START)) == -1: Log.debug("No frame start in data: {}, wait for data", data, ":hex") - return data_len, self.EMPTY + return data_len, 0, 0, self.EMPTY used_len += i continue if (end := buffer.find(self.END)) == -1: Log.debug("Incomplete frame: {} wait for more data", data, ":hex") - return used_len, self.EMPTY - self.incoming_dev_id = int(buffer[1:3], 16) - self.incoming_tid = self.incoming_dev_id + return used_len, 0, 0, self.EMPTY + dev_id = int(buffer[1:3], 16) lrc = int(buffer[end - 2: end], 16) msg = a2b_hex(buffer[1 : end - 2]) used_len += end + 2 if not self.check_LRC(msg, lrc): Log.debug("LRC wrong in frame: {} skipping", data, ":hex") continue - return used_len, msg[1:] + return used_len, dev_id, 0, msg[1:] def encode(self, data: bytes, device_id: int, _tid: int) -> bytes: """Encode ADU.""" dev_id = device_id.to_bytes(1,'big') checksum = self.compute_LRC(dev_id + data) - packet = ( + frame = ( self.START + f"{device_id:02x}".encode() + b2a_hex(data) + f"{checksum:02x}".encode() + self.END ).upper() - return packet + return frame @classmethod def compute_LRC(cls, data: bytes) -> int: diff --git a/pymodbus/framer/base.py b/pymodbus/framer/base.py index 392698f50..ab4efd8d2 100644 --- a/pymodbus/framer/base.py +++ b/pymodbus/framer/base.py @@ -11,7 +11,7 @@ from pymodbus.exceptions import ModbusIOException from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.logging import Log -from pymodbus.pdu import ModbusRequest, ModbusResponse +from pymodbus.pdu import ModbusPDU class FramerType(str, Enum): @@ -39,37 +39,20 @@ def __init__( if 0 in dev_ids: dev_ids = [] self.dev_ids = dev_ids - self.incoming_dev_id = 0 - self.incoming_tid = 0 self.databuffer = b"" - def decode(self, data: bytes) -> tuple[int, bytes]: + def decode(self, _data: bytes) -> tuple[int, int, int, bytes]: """Decode ADU. returns: used_len (int) or 0 to read more + dev_id, + tid, modbus request/response (bytes) """ - if (data_len := len(data)) < self.MIN_SIZE: - Log.debug("Very short frame (NO MBAP): {} wait for more data", data, ":hex") - return 0, self.EMPTY - used_len, res_data = self.specific_decode(data, data_len) - if not res_data: - self.incoming_dev_id = 0 - self.incoming_tid = 0 - return used_len, res_data - - def specific_decode(self, data: bytes, data_len: int) -> tuple[int, bytes]: - """Decode ADU. + return 0, 0, 0, self.EMPTY - returns: - used_len (int) or 0 to read more - modbus request/response (bytes) - """ - return data_len, data - - - def encode(self, pdu: bytes, dev_id: int, _tid: int) -> bytes: + def encode(self, data: bytes, dev_id: int, _tid: int) -> bytes: """Encode ADU. returns: @@ -77,51 +60,62 @@ def encode(self, pdu: bytes, dev_id: int, _tid: int) -> bytes: """ if dev_id and dev_id not in self.dev_ids: self.dev_ids.append(dev_id) - return pdu + return data - def buildPacket(self, message: ModbusRequest | ModbusResponse) -> bytes: + def buildFrame(self, message: ModbusPDU) -> bytes: """Create a ready to send modbus packet. :param message: The populated request/response to send """ data = message.function_code.to_bytes(1,'big') + message.encode() - packet = self.encode(data, message.slave_id, message.transaction_id) - return packet + frame = self.encode(data, message.slave_id, message.transaction_id) + return frame - def processIncomingPacket(self, data: bytes, callback, tid=None): + def processIncomingFrame(self, data: bytes, tid=None) -> ModbusPDU | None: """Process new packet pattern. This takes in a new request packet, adds it to the current packet stream, and performs framing on it. That is, checks for complete messages, and once found, will process all that - exist. This handles the case when we read N + 1 or 1 // N - messages at a time instead of 1. - - The processed and decoded messages are pushed to the callback - function to process and send. + exist. """ - Log.debug("Processing: {}", data, ":hex") self.databuffer += data while True: - if self.databuffer == b'': - return - used_len, data = self.decode(self.databuffer) + try: + used_len, pdu = self._processIncomingFrame(self.databuffer, tid=tid) + if not used_len: + return None + if pdu: + self.databuffer = self.databuffer[used_len:] + return pdu + except ModbusIOException as exc: + self.databuffer = self.EMPTY + raise exc self.databuffer = self.databuffer[used_len:] + + def _processIncomingFrame(self, data: bytes, tid=None) -> tuple[int, ModbusPDU | None]: + """Process new packet pattern. + + This takes in a new request packet, adds it to the current + packet stream, and performs framing on it. That is, checks + for complete messages, and once found, will process all that + exist. + """ + Log.debug("Processing: {}", data, ":hex") + while True: if not data: - if used_len: - continue - return - if self.dev_ids and self.incoming_dev_id not in self.dev_ids: - Log.debug("Not a valid slave id - {}, ignoring!!", self.incoming_dev_id) - self.databuffer = b'' - continue - if (result := self.decoder.decode(data)) is None: - self.databuffer = b'' + return 0, None + used_len, dev_id, tid, frame_data = self.decode(self.databuffer) + if not frame_data: + return used_len, None + if self.dev_ids and dev_id not in self.dev_ids: + Log.debug("Not a valid slave id - {}, ignoring!!", dev_id) + return used_len, None + if (result := self.decoder.decode(frame_data)) is None: raise ModbusIOException("Unable to decode request") - result.slave_id = self.incoming_dev_id - result.transaction_id = self.incoming_tid + result.slave_id = dev_id + result.transaction_id = tid Log.debug("Frame advanced, resetting header!!") if tid and result.transaction_id and tid != result.transaction_id: - self.databuffer = b'' - else: - callback(result) # defer or push to a thread? + return used_len, None + return used_len, result diff --git a/pymodbus/framer/rtu.py b/pymodbus/framer/rtu.py index b58f39b3e..76fe07675 100644 --- a/pymodbus/framer/rtu.py +++ b/pymodbus/framer/rtu.py @@ -97,39 +97,37 @@ def generate_crc16_table(cls) -> list[int]: crc16_table: list[int] = [0] - def specific_decode(self, data: bytes, data_len: int) -> tuple[int, bytes]: + def decode(self, data: bytes) -> tuple[int, int, int, bytes]: """Decode ADU.""" + data_len = len(data) for used_len in range(data_len): if data_len - used_len < self.MIN_SIZE: Log.debug("Short frame: {} wait for more data", data, ":hex") - return used_len, self.EMPTY - self.incoming_dev_id = int(data[used_len]) + return used_len, 0, 0, self.EMPTY + dev_id = int(data[used_len]) func_code = int(data[used_len + 1]) - if (self.dev_ids and self.incoming_dev_id not in self.dev_ids) or func_code & 0x7F not in self.decoder.lookup: + if func_code & 0x7F not in self.decoder.lookup: continue pdu_class = self.decoder.lookupPduClass(func_code) if not (size := pdu_class.calculateRtuFrameSize(data[used_len:])): size = data_len +1 if data_len < used_len +size: Log.debug("Frame - not ready") - # if no_recur: - # return used_len, self.EMPTY - # res_len, res_data = self.hunt_second_frame(data[used_len:], data_len - used_len) - return used_len, self.EMPTY + return used_len, dev_id, 0, self.EMPTY start_crc = used_len + size -2 crc = data[start_crc : start_crc + 2] crc_val = (int(crc[0]) << 8) + int(crc[1]) if not FramerRTU.check_CRC(data[used_len : start_crc], crc_val): Log.debug("Frame check failed, ignoring!!") continue - return start_crc + 2, data[used_len + 1 : start_crc] - return 0, self.EMPTY + return start_crc + 2, dev_id, 0, data[used_len + 1 : start_crc] + return 0, 0, 0, self.EMPTY def encode(self, pdu: bytes, device_id: int, _tid: int) -> bytes: """Encode ADU.""" - packet = device_id.to_bytes(1,'big') + pdu - return packet + FramerRTU.compute_CRC(packet).to_bytes(2,'big') + frame = device_id.to_bytes(1,'big') + pdu + return frame + FramerRTU.compute_CRC(frame).to_bytes(2,'big') @classmethod def check_CRC(cls, data: bytes, check: int) -> bool: diff --git a/pymodbus/framer/socket.py b/pymodbus/framer/socket.py index 83f9ef1c8..eb2617683 100644 --- a/pymodbus/framer/socket.py +++ b/pymodbus/framer/socket.py @@ -19,25 +19,28 @@ class FramerSocket(FramerBase): MIN_SIZE = 8 - def specific_decode(self, data: bytes, data_len: int) -> tuple[int, bytes]: + def decode(self, data: bytes) -> tuple[int, int, int, bytes]: """Decode ADU.""" - self.incoming_tid = int.from_bytes(data[0:2], 'big') + if (data_len := len(data)) < self.MIN_SIZE: + Log.debug("Very short frame (NO MBAP): {} wait for more data", data, ":hex") + return 0, 0, 0, self.EMPTY + tid = int.from_bytes(data[0:2], 'big') msg_len = int.from_bytes(data[4:6], 'big') + 6 - self.incoming_dev_id = int(data[6]) + dev_id = int(data[6]) if data_len < msg_len: Log.debug("Short frame: {} wait for more data", data, ":hex") - return 0, self.EMPTY + return 0, 0, 0, self.EMPTY if msg_len == 8 and data_len == 9: msg_len = 9 - return msg_len, data[7:msg_len] + return msg_len, dev_id, tid, data[7:msg_len] def encode(self, pdu: bytes, device_id: int, tid: int) -> bytes: """Encode ADU.""" - packet = ( + frame = ( tid.to_bytes(2, 'big') + b'\x00\x00' + (len(pdu) + 1).to_bytes(2, 'big') + device_id.to_bytes(1, 'big') + pdu ) - return packet + return frame diff --git a/pymodbus/framer/tls.py b/pymodbus/framer/tls.py index 675deeaeb..dd10f1f6e 100644 --- a/pymodbus/framer/tls.py +++ b/pymodbus/framer/tls.py @@ -12,9 +12,9 @@ class FramerTLS(FramerBase): 1b Nb """ - def specific_decode(self, data: bytes, data_len: int) -> tuple[int, bytes]: + def decode(self, data: bytes) -> tuple[int, int, int, bytes]: """Decode ADU.""" - return data_len, data + return len(data), 0, 0, data def encode(self, pdu: bytes, _device_id: int, _tid: int) -> bytes: """Encode ADU.""" diff --git a/pymodbus/pdu/pdu.py b/pymodbus/pdu/pdu.py index ebd41b62f..8ddbd3fab 100644 --- a/pymodbus/pdu/pdu.py +++ b/pymodbus/pdu/pdu.py @@ -40,6 +40,8 @@ class ModbusPDU: of encoding it again. """ + function_code = -1 + def __init__(self, slave, transaction, skip_encode): """Initialize the base data for a modbus request. diff --git a/pymodbus/server/async_io.py b/pymodbus/server/async_io.py index 7ad9d19d4..28fafadcb 100644 --- a/pymodbus/server/async_io.py +++ b/pymodbus/server/async_io.py @@ -123,10 +123,8 @@ async def inner_handle(self): # if broadcast is enabled make sure to # process requests to address 0 Log.debug("Handling data: {}", data, ":hex") - self.framer.processIncomingPacket( - data=data, - callback=lambda x: self.execute(x, *addr), - ) + if (pdu := self.framer.processIncomingFrame(data)): + self.execute(pdu, *addr) async def handle(self) -> None: """Coroutine which represents a single master <=> slave conversation. @@ -152,7 +150,7 @@ async def handle(self) -> None: self._log_exception() self.running = False except Exception as exc: # pylint: disable=broad-except - # force TCP socket termination as processIncomingPacket + # force TCP socket termination as framer # should handle application layer errors Log.error( 'Unknown exception "{}" on stream {} forcing disconnect', @@ -212,7 +210,7 @@ def server_send(self, message, addr, **kwargs): if kwargs.get("skip_encoding", False): self.send(message, addr=addr) elif message.should_respond: - pdu = self.framer.buildPacket(message) + pdu = self.framer.buildFrame(message) self.send(pdu, addr=addr) else: Log.debug("Skipping sending response!!") diff --git a/pymodbus/transaction.py b/pymodbus/transaction.py index 327f84657..a11ae8505 100644 --- a/pymodbus/transaction.py +++ b/pymodbus/transaction.py @@ -24,7 +24,7 @@ FramerTLS, ) from pymodbus.logging import Log -from pymodbus.pdu import ModbusRequest +from pymodbus.pdu import ModbusPDU from pymodbus.transport import CommType from pymodbus.utilities import ModbusTransactionState, hexlify_packets @@ -45,7 +45,7 @@ class ModbusTransactionManager: def __init__(self): """Initialize an instance of the ModbusTransactionManager.""" self.tid = 0 - self.transactions: dict[int, ModbusRequest] = {} + self.transactions: dict[int, ModbusPDU] = {} def __iter__(self): """Iterate over the current managed transactions. @@ -54,7 +54,7 @@ def __iter__(self): """ return iter(self.transactions.keys()) - def addTransaction(self, request: ModbusRequest): + def addTransaction(self, request: ModbusPDU): """Add a transaction to the handler. This holds the request in case it needs to be resent. @@ -175,7 +175,7 @@ def _validate_response(self, response): return False return True - def execute(self, request: ModbusRequest): # noqa: C901 + def execute(self, request: ModbusPDU): # noqa: C901 """Start the producer to send the next request to consumer.write(Frame(request)).""" with self._transaction_lock: try: @@ -232,11 +232,11 @@ def execute(self, request: ModbusRequest): # noqa: C901 self._no_response_devices.append(request.slave_id) # No response received and retries not enabled break - self.client.framer.processIncomingPacket( + if (pdu := self.client.framer.processIncomingFrame( response, - self.addTransaction, tid=request.transaction_id, - ) + )): + self.addTransaction(pdu) if not (response := self.getTransaction(request.transaction_id)): if len(self.transactions): response = self.getTransaction(tid=0) @@ -279,7 +279,7 @@ def _retry_transaction(self, retries, reason, packet, response_length, full=Fals return result, None return self._transact(packet, response_length, full=full) - def _transact(self, request: ModbusRequest, response_length, full=False, broadcast=False): + def _transact(self, request: ModbusPDU, response_length, full=False, broadcast=False): """Do a Write and Read transaction. :param packet: packet to be sent @@ -292,7 +292,7 @@ def _transact(self, request: ModbusRequest, response_length, full=False, broadca last_exception = None try: self.client.connect() - packet = self.client.framer.buildPacket(request) + packet = self.client.framer.buildFrame(request) Log.debug("SEND: {}", packet, ":hex") size = self._send(packet) if ( diff --git a/test/framers/generator.py b/test/framers/generator.py index 37e4e46c8..8f5775611 100755 --- a/test/framers/generator.py +++ b/test/framers/generator.py @@ -26,20 +26,20 @@ def set_calls(): client = framer(ClientDecoder(), [0]) request = ReadHoldingRegistersRequest(124, 2, dev_id) request.transaction_id = tid - result = client.buildPacket(request) + result = client.buildFrame(request) print(f" request --> {result}") print(f" request --> {result.hex()}") server = framer(ServerDecoder(), [0]) response = ReadHoldingRegistersResponse([141,142]) response.slave_id = dev_id response.transaction_id = tid - result = server.buildPacket(response) + result = server.buildFrame(response) print(f" response --> {result}") print(f" response --> {result.hex()}") exception = request.doException(merror.IllegalAddress) exception.transaction_id = tid exception.slave_id = dev_id - result = server.buildPacket(exception) + result = server.buildFrame(exception) print(f" exception --> {result}") print(f" exception --> {result.hex()}") diff --git a/test/framers/test_framer.py b/test/framers/test_framer.py index 89bc10e57..d33d6d468 100644 --- a/test/framers/test_framer.py +++ b/test/framers/test_framer.py @@ -204,21 +204,21 @@ def test_encode_type(self, frame, frame_expected, data, dev_id, tr_id, inx1, inx (FramerType.ASCII, True, b':0003007C00027F\r\n', 0, 0, b"\x03\x00\x7c\x00\x02",), # Request (FramerType.ASCII, False, b':000304008D008EDE\r\n', 0, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response (FramerType.ASCII, False, b':0083027B\r\n', 0, 0, b'\x83\x02',), # Exception - (FramerType.ASCII, True, b':1103007C00026E\r\n', 17, 17, b"\x03\x00\x7c\x00\x02",), # Request - (FramerType.ASCII, False, b':110304008D008ECD\r\n', 17, 17, b"\x03\x04\x00\x8d\x00\x8e",), # Response - (FramerType.ASCII, False, b':1183026A\r\n', 17, 17, b'\x83\x02',), # Exception - (FramerType.ASCII, True, b':FF03007C000280\r\n', 255, 255, b"\x03\x00\x7c\x00\x02",), # Request - (FramerType.ASCII, False, b':FF0304008D008EDF\r\n', 255, 255, b"\x03\x04\x00\x8d\x00\x8e",), # Response - (FramerType.ASCII, False, b':FF83027C\r\n', 255, 255, b'\x83\x02',), # Exception + (FramerType.ASCII, True, b':1103007C00026E\r\n', 17, 0, b"\x03\x00\x7c\x00\x02",), # Request + (FramerType.ASCII, False, b':110304008D008ECD\r\n', 17, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response + (FramerType.ASCII, False, b':1183026A\r\n', 17, 0, b'\x83\x02',), # Exception + (FramerType.ASCII, True, b':FF03007C000280\r\n', 255, 0, b"\x03\x00\x7c\x00\x02",), # Request + (FramerType.ASCII, False, b':FF0304008D008EDF\r\n', 255, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response + (FramerType.ASCII, False, b':FF83027C\r\n', 255, 0, b'\x83\x02',), # Exception (FramerType.RTU, True, b'\x00\x03\x00\x7c\x00\x02\x04\x02', 0, 0, b"\x03\x00\x7c\x00\x02",), # Request (FramerType.RTU, False, b'\x00\x03\x04\x00\x8d\x00\x8e\xfa\xbc', 0, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response (FramerType.RTU, False, b'\x00\x83\x02\x91\x31', 0, 0, b'\x83\x02',), # Exception - (FramerType.RTU, True, b'\x11\x03\x00\x7c\x00\x02\x07\x43', 17, 17, b"\x03\x00\x7c\x00\x02",), # Request - (FramerType.RTU, False, b'\x11\x03\x04\x00\x8d\x00\x8e\xfb\xbd', 17, 17, b"\x03\x04\x00\x8d\x00\x8e",), # Response - (FramerType.RTU, False, b'\x11\x83\x02\xc1\x34', 17, 17, b'\x83\x02',), # Exception - (FramerType.RTU, True, b'\xff\x03\x00|\x00\x02\x10\x0d', 255, 255, b"\x03\x00\x7c\x00\x02",), # Request - (FramerType.RTU, False, b'\xff\x03\x04\x00\x8d\x00\x8e\xf5\xb3', 255, 255, b"\x03\x04\x00\x8d\x00\x8e",), # Response - (FramerType.RTU, False, b'\xff\x83\x02\xa1\x01', 255, 255, b'\x83\x02',), # Exception + (FramerType.RTU, True, b'\x11\x03\x00\x7c\x00\x02\x07\x43', 17, 0, b"\x03\x00\x7c\x00\x02",), # Request + (FramerType.RTU, False, b'\x11\x03\x04\x00\x8d\x00\x8e\xfb\xbd', 17, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response + (FramerType.RTU, False, b'\x11\x83\x02\xc1\x34', 17, 0, b'\x83\x02',), # Exception + (FramerType.RTU, True, b'\xff\x03\x00|\x00\x02\x10\x0d', 255, 0, b"\x03\x00\x7c\x00\x02",), # Request + (FramerType.RTU, False, b'\xff\x03\x04\x00\x8d\x00\x8e\xf5\xb3', 255, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response + (FramerType.RTU, False, b'\xff\x83\x02\xa1\x01', 255, 0, b'\x83\x02',), # Exception (FramerType.SOCKET, True, b'\x00\x00\x00\x00\x00\x06\x00\x03\x00\x7c\x00\x02', 0, 0, b"\x03\x00\x7c\x00\x02",), # Request (FramerType.SOCKET, False, b'\x00\x00\x00\x00\x00\x07\x00\x03\x04\x00\x8d\x00\x8e', 0, 0, b"\x03\x04\x00\x8d\x00\x8e",), # Response (FramerType.SOCKET, False, b'\x00\x00\x00\x00\x00\x03\x00\x83\x02', 0, 0, b'\x83\x02',), # Exception @@ -257,28 +257,28 @@ async def test_decode_type(self, entry, test_framer, data, dev_id, tr_id, expect if entry == FramerType.RTU: return if split == "no": - used_len, res_data = test_framer.decode(data) + used_len, res_dev_id, res_tid, res_data = test_framer.decode(data) elif split == "half": split_len = int(len(data) / 2) - used_len, res_data = test_framer.decode(data[0:split_len]) + used_len, res_dev_id, res_tid, res_data = test_framer.decode(data[0:split_len]) assert not used_len assert not res_data - assert not test_framer.incoming_dev_id - assert not test_framer.incoming_tid - used_len, res_data = test_framer.decode(data) + assert not res_dev_id + assert not res_tid + used_len, res_dev_id, res_tid, res_data = test_framer.decode(data) else: last = len(data) for i in range(0, last -1): - used_len, res_data = test_framer.decode(data[0:i+1]) + used_len, res_dev_id, res_tid, res_data = test_framer.decode(data[0:i+1]) assert not used_len assert not res_data - assert not test_framer.incoming_dev_id - assert not test_framer.incoming_tid - used_len, res_data = test_framer.decode(data) + assert not res_dev_id + assert not res_tid + used_len, res_dev_id, res_tid, res_data = test_framer.decode(data) assert used_len == len(data) assert res_data == expected - assert dev_id == test_framer.incoming_dev_id - assert tr_id == test_framer.incoming_tid + assert dev_id == res_dev_id + assert tr_id == res_tid @pytest.mark.parametrize( ("entry", "data", "exp"), @@ -336,7 +336,7 @@ async def test_decode_type(self, entry, test_framer, data, dev_id, tr_id, expect async def test_decode_complicated(self, test_framer, data, exp): """Test encode method.""" for ent in exp: - used_len, res_data = test_framer.decode(data) + used_len, _, _, res_data = test_framer.decode(data) data = data[used_len:] assert used_len == ent[0] assert res_data == ent[1] @@ -364,15 +364,15 @@ async def test_decode_complicated(self, test_framer, data, exp): def test_roundtrip(self, test_framer, data, dev_id, res_msg): """Test encode.""" msg = test_framer.encode(data, dev_id, 0) - res_len, res_data = test_framer.decode(msg) + res_len, res_dev_id, _, res_data = test_framer.decode(msg) assert data == res_data - assert dev_id == test_framer.incoming_dev_id + assert dev_id == res_dev_id assert res_len == len(res_msg) @pytest.mark.parametrize(("entry"), [FramerType.RTU]) - def test_specific_decode(self, test_framer): + def test_framer_decode(self, test_framer): """Test dummy decode.""" msg = b'' - res_len, res_data = test_framer.specific_decode(msg, 0) + res_len, _, _, res_data = test_framer.decode(msg) assert not res_len assert not res_data diff --git a/test/framers/test_multidrop.py b/test/framers/test_multidrop.py index 5f8f0f8ed..8e26971bc 100644 --- a/test/framers/test_multidrop.py +++ b/test/framers/test_multidrop.py @@ -23,31 +23,28 @@ def fixture_callback(self): """Prepare dummy callback.""" return mock.Mock() - def test_ok_frame(self, framer, callback): + def test_ok_frame(self, framer): """Test ok frame.""" serial_event = self.good_frame - framer.processIncomingPacket(serial_event, callback) - callback.assert_called_once() + assert framer.processIncomingFrame(serial_event) - def test_ok_2frame(self, framer, callback): + def test_ok_2frame(self, framer): """Test ok frame.""" serial_event = self.good_frame + self.good_frame - framer.processIncomingPacket(serial_event, callback) - assert callback.call_count == 2 + assert framer.processIncomingFrame(serial_event) + assert framer.processIncomingFrame(b'') - def test_bad_crc(self, framer, callback): + def test_bad_crc(self, framer): """Test bad crc.""" serial_event = b"\x02\x03\x00\x01\x00}\xd4\x19" # Manually mangled crc - framer.processIncomingPacket(serial_event, callback) - callback.assert_not_called() + assert not framer.processIncomingFrame(serial_event) - def test_wrong_id(self, framer, callback): + def test_wrong_id(self, framer): """Test frame wrong id.""" serial_event = b"\x01\x03\x00\x01\x00}\xd4+" # Frame with good CRC but other id - framer.processIncomingPacket(serial_event, callback) - callback.assert_not_called() + assert not framer.processIncomingFrame(serial_event) - def test_big_split_response_frame_from_other_id(self, framer, callback): + def test_big_split_response_frame_from_other_id(self, framer): """Test split response.""" # This is a single *response* from device id 1 after being queried for 125 holding register values # Because the response is so long it spans several serial events @@ -63,37 +60,32 @@ def test_big_split_response_frame_from_other_id(self, framer, callback): b"\x00\x00\x00\x00\x00\x00\x00N,", ] for serial_event in serial_events: - framer.processIncomingPacket(serial_event, callback) - callback.assert_not_called() + assert not framer.processIncomingFrame(serial_event) - def test_split_frame(self, framer, callback): + def test_split_frame(self, framer): """Test split frame.""" serial_events = [self.good_frame[:5], self.good_frame[5:]] - for serial_event in serial_events: - framer.processIncomingPacket(serial_event, callback) - callback.assert_called_once() + assert not framer.processIncomingFrame(serial_events[0]) + assert framer.processIncomingFrame(serial_events[1]) - def test_complete_frame_trailing_data_without_id(self, framer, callback): + def test_complete_frame_trailing_data_without_id(self, framer): """Test trailing data.""" garbage = b"\x05\x04\x03" # without id serial_event = garbage + self.good_frame - framer.processIncomingPacket(serial_event, callback) - callback.assert_called_once() + assert framer.processIncomingFrame(serial_event) - def test_complete_frame_trailing_data_with_id(self, framer, callback): + def test_complete_frame_trailing_data_with_id(self, framer): """Test trailing data.""" garbage = b"\x05\x04\x03\x02\x01\x00" # with id serial_event = garbage + self.good_frame - framer.processIncomingPacket(serial_event, callback) - callback.assert_called_once() + assert framer.processIncomingFrame(serial_event) - def test_split_frame_trailing_data_with_id(self, framer, callback): + def test_split_frame_trailing_data_with_id(self, framer): """Test split frame.""" garbage = b"ABCDEF" serial_events = [garbage + self.good_frame[:5], self.good_frame[5:]] - for serial_event in serial_events: - framer.processIncomingPacket(serial_event, callback) - callback.assert_called_once() + assert not framer.processIncomingFrame(serial_events[0]) + assert framer.processIncomingFrame(serial_events[1]) @pytest.mark.parametrize( ("garbage"), [ @@ -101,47 +93,35 @@ def test_split_frame_trailing_data_with_id(self, framer, callback): b"\x02\x10\x07", b"\x02\x10\x07\x10", ]) - def test_coincidental(self, garbage, framer, callback): + def test_coincidental(self, garbage, framer): """Test conincidental.""" serial_events = [garbage, self.good_frame[:5], self.good_frame[5:]] - for serial_event in serial_events: - framer.processIncomingPacket(serial_event, callback) - callback.assert_called_once() + assert not framer.processIncomingFrame(serial_events[0]) + assert not framer.processIncomingFrame(serial_events[1]) - def test_wrapped_frame(self, framer, callback): + def test_wrapped_frame(self, framer): """Test wrapped frame.""" garbage = b"\x05\x04\x03\x02\x01\x00" serial_event = garbage + self.good_frame + garbage - framer.processIncomingPacket(serial_event, callback) - # We probably should not respond in this case; in this case we've likely become desynchronized # i.e. this probably represents a case where a command came for us, but we didn't get # to the serial buffer in time (some other co-routine or perhaps a block on the USB bus) # and the master moved on and queried another device - callback.assert_called_once() + assert framer.processIncomingFrame(serial_event) - def test_frame_with_trailing_data(self, framer, callback): + def test_frame_with_trailing_data(self, framer): """Test trailing data.""" garbage = b"\x05\x04\x03\x02\x01\x00" serial_event = self.good_frame + garbage - framer.processIncomingPacket(serial_event, callback) - # We should not respond in this case for identical reasons as test_wrapped_frame - callback.assert_called_once() + assert framer.processIncomingFrame(serial_event) - def test_wrong_dev_id(self, callback): + def test_wrong_dev_id(self): """Test conincidental.""" framer = FramerAscii(ServerDecoder(), [87]) - framer.processIncomingPacket(b':0003007C00027F\r\n', callback) - callback.assert_not_called() + assert not framer.processIncomingFrame(b':0003007C00027F\r\n') - def test_wrong_tid(self, callback): - """Test conincidental.""" - framer = FramerAscii(ServerDecoder(), []) - framer.processIncomingPacket(b':1103007C00026E\r\n', callback, tid=117) - callback.assert_not_called() - - def test_wrong_class(self, callback): + def test_wrong_class(self): """Test conincidental.""" def return_none(_data): @@ -151,38 +131,27 @@ def return_none(_data): framer = FramerAscii(ServerDecoder(), []) framer.decoder.decode = return_none with pytest.raises(ModbusIOException): - framer.processIncomingPacket(b':1103007C00026E\r\n', callback) - callback.assert_not_called() + framer.processIncomingFrame(b':1103007C00026E\r\n') - @pytest.mark.skip - def test_getFrameStart(self, framer): # pragma: no cover + def test_getFrameStart(self, framer): """Test getFrameStart.""" - result = None - count = 0 - def test_callback(data): - """Check callback.""" - nonlocal result, count - count += 1 - result = data.function_code.to_bytes(1,'big')+data.encode() - framer_ok = b"\x02\x03\x00\x01\x00\x7d\xd4\x18" - framer.processIncomingPacket(framer_ok, test_callback) - assert framer_ok[1:-2] == result + result = framer.processIncomingFrame(framer_ok) + assert framer_ok[1:-2] == result.function_code.to_bytes(1,'big')+result.encode() - count = 0 framer_2ok = framer_ok + framer_ok - framer.processIncomingPacket(framer_2ok, test_callback) - assert count == 2 - assert not framer._buffer # pylint: disable=protected-access + assert framer.processIncomingFrame(framer_2ok) + assert framer.processIncomingFrame(b'') + assert not framer.databuffer - framer._buffer = framer_ok[:2] # pylint: disable=protected-access - framer.processIncomingPacket(b'', test_callback) - assert framer_ok[:2] == framer._buffer # pylint: disable=protected-access + framer.databuffer = framer_ok[:2] + assert not framer.processIncomingFrame(b'') + assert framer_ok[:2] == framer.databuffer - framer._buffer = framer_ok[:3] # pylint: disable=protected-access - framer.processIncomingPacket(b'', test_callback) - assert framer_ok[:3] == framer._buffer # pylint: disable=protected-access + framer.databuffer = framer_ok[:3] + assert not framer.processIncomingFrame(b'') + assert framer_ok[:3] == framer.databuffer framer_ok = b"\xF0\x03\x00\x01\x00}\xd4\x18" - framer.processIncomingPacket(framer_ok, test_callback) - assert framer._buffer == framer_ok[-3:] # pylint: disable=protected-access + assert not framer.processIncomingFrame(framer_ok) + assert framer.databuffer == framer_ok[-6:] diff --git a/test/sub_client/test_client.py b/test/sub_client/test_client.py index 894cf4a70..21f3be665 100755 --- a/test/sub_client/test_client.py +++ b/test/sub_client/test_client.py @@ -387,7 +387,7 @@ async def delayed_resp(self): """Send a response to a received packet.""" await asyncio.sleep(0.05) resp = await self.req.execute(self.ctx) - pkt = self.base.ctx.framer.buildPacket(resp) + pkt = self.base.ctx.framer.buildFrame(resp) self.base.ctx.data_received(pkt) def write(self, data, addr=None): diff --git a/test/sub_client/test_client_faulty_response.py b/test/sub_client/test_client_faulty_response.py index a14e1890f..6d6966853 100644 --- a/test/sub_client/test_client_faulty_response.py +++ b/test/sub_client/test_client_faulty_response.py @@ -1,5 +1,4 @@ """Test server working as slave on a multidrop RS485 line.""" -from unittest import mock import pytest @@ -18,28 +17,19 @@ def fixture_framer(self): """Prepare framer.""" return FramerSocket(ClientDecoder(), []) - @pytest.fixture(name="callback") - def fixture_callback(self): - """Prepare dummy callback.""" - return mock.Mock() - - def test_ok_frame(self, framer, callback): + def test_ok_frame(self, framer): """Test ok frame.""" - framer.processIncomingPacket(self.good_frame, callback) - callback.assert_called_once() + assert framer.processIncomingFrame(self.good_frame) - def test_1917_frame(self, callback): + def test_1917_frame(self): """Test invalid frame in issue 1917.""" recv = b"\x01\x86\x02\x00\x01" framer = FramerRTU(ClientDecoder(), [0]) - framer.processIncomingPacket(recv, callback) - callback.assert_not_called() + assert not framer.processIncomingFrame(recv) - def test_faulty_frame1(self, framer, callback): + def test_faulty_frame1(self, framer): """Test ok frame.""" faulty_frame = b"\x00\x04\x00\x00\x00\x05\x00\x03\x0a\x00\x04" with pytest.raises(ModbusIOException): - framer.processIncomingPacket(faulty_frame, callback) - callback.assert_not_called() - framer.processIncomingPacket(self.good_frame, callback) - callback.assert_called_once() + framer.processIncomingFrame(faulty_frame) + assert framer.processIncomingFrame(self.good_frame) diff --git a/test/sub_client/test_client_sync.py b/test/sub_client/test_client_sync.py index 68d0080aa..bac978476 100755 --- a/test/sub_client/test_client_sync.py +++ b/test/sub_client/test_client_sync.py @@ -88,8 +88,8 @@ def test_udp_client_recv_duplicate(self): client.socket.mock_prepare_receive(test_msg) reply_ok = client.read_input_registers(0x820, 1, 1) assert not reply_ok.isError() - reply_none = client.read_input_registers(0x40, 10, 1) - assert reply_none.isError() + reply_ok = client.read_input_registers(0x40, 10, 1) + assert not reply_ok.isError() client.close() def test_udp_client_repr(self): diff --git a/test/sub_current/test_transaction.py b/test/sub_current/test_transaction.py index bef0eda22..e475ceebc 100755 --- a/test/sub_current/test_transaction.py +++ b/test/sub_current/test_transaction.py @@ -98,10 +98,10 @@ def test_execute(self, mock_get_transaction, mock_recv): client = mock.MagicMock() client.framer = self._ascii client.framer._buffer = b"deadbeef" # pylint: disable=protected-access - client.framer.processIncomingPacket = mock.MagicMock() - client.framer.processIncomingPacket.return_value = None - client.framer.buildPacket = mock.MagicMock() - client.framer.buildPacket.return_value = b"deadbeef" + client.framer.processIncomingFrame = mock.MagicMock() + client.framer.processIncomingFrame.return_value = None + client.framer.buildFrame = mock.MagicMock() + client.framer.buildFrame.return_value = b"deadbeef" client.send = mock.MagicMock() client.send.return_value = len(b"deadbeef") request = mock.MagicMock() @@ -152,7 +152,7 @@ def test_execute(self, mock_get_transaction, mock_recv): mock_recv.reset_mock( side_effect=ModbusIOException() ) - client.framer.processIncomingPacket.side_effect = mock.MagicMock( + client.framer.processIncomingFrame.side_effect = mock.MagicMock( side_effect=ModbusIOException() ) assert isinstance(trans.execute(request), ModbusIOException) @@ -189,121 +189,59 @@ def test_delete_transaction_manager_transaction(self): # ----------------------------------------------------------------------- # def test_tcp_framer_transaction_ready(self): """Test a tcp frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x01\x02\x00\x08" - self._tcp.processIncomingPacket(msg, callback) - self._tcp._buffer = msg # pylint: disable=protected-access - callback(b'') + assert self._tcp.processIncomingFrame(msg) + self._tcp.databuffer = msg def test_tcp_framer_transaction_full(self): """Test a full tcp frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x01\x02\x00\x08" - self._tcp.processIncomingPacket(msg, callback) + result = self._tcp.processIncomingFrame(msg) assert result.function_code.to_bytes(1,'big') + result.encode() == msg[7:] def test_tcp_framer_transaction_half(self): """Test a half completed tcp frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg1 = b"\x00\x01\x12\x34\x00" msg2 = b"\x06\xff\x02\x01\x02\x00\x08" - self._tcp.processIncomingPacket(msg1, callback) - assert not result - self._tcp.processIncomingPacket(msg2, callback) + assert not self._tcp.processIncomingFrame(msg1) + result = self._tcp.processIncomingFrame(msg2) assert result assert result.function_code.to_bytes(1,'big') + result.encode() == msg2[2:] def test_tcp_framer_transaction_half2(self): """Test a half completed tcp frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg1 = b"\x00\x01\x12\x34\x00\x06\xff" msg2 = b"\x02\x01\x02\x00\x08" - self._tcp.processIncomingPacket(msg1, callback) - assert not result - self._tcp.processIncomingPacket(msg2, callback) + assert not self._tcp.processIncomingFrame(msg1) + result = self._tcp.processIncomingFrame(msg2) assert result assert result.function_code.to_bytes(1,'big') + result.encode() == msg2 def test_tcp_framer_transaction_half3(self): """Test a half completed tcp frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg1 = b"\x00\x01\x12\x34\x00\x06\xff\x02\x01\x02\x00" msg2 = b"\x08" - self._tcp.processIncomingPacket(msg1, callback) - assert not result - self._tcp.processIncomingPacket(msg2, callback) + assert not self._tcp.processIncomingFrame(msg1) + result = self._tcp.processIncomingFrame(msg2) assert result assert result.function_code.to_bytes(1,'big') + result.encode() == msg1[7:] + msg2 def test_tcp_framer_transaction_short(self): """Test that we can get back on track after an invalid message.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - - # msg1 = b"\x99\x99\x99\x99\x00\x01\x00\x17" msg1 = b'' msg2 = b"\x00\x01\x12\x34\x00\x06\xff\x02\x01\x02\x00\x08" - self._tcp.processIncomingPacket(msg1, callback) - assert not result - self._tcp.processIncomingPacket(msg2, callback) + assert not self._tcp.processIncomingFrame(msg1) + result = self._tcp.processIncomingFrame(msg2) assert result assert result.function_code.to_bytes(1,'big') + result.encode() == msg2[7:] def test_tcp_framer_populate(self): """Test a tcp frame packet build.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - - expected = ModbusRequest(0, 0, False) - expected.transaction_id = 0x0001 - expected.slave_id = 0xFF msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - self._tcp.processIncomingPacket(msg, callback) + result = self._tcp.processIncomingFrame(msg) + assert result + assert result.slave_id == 0xFF + assert result.transaction_id == 0x0001 @mock.patch.object(ModbusRequest, "encode") def test_tcp_framer_packet(self, mock_encode): @@ -314,7 +252,7 @@ def test_tcp_framer_packet(self, mock_encode): message.function_code = 0x01 expected = b"\x00\x01\x00\x00\x00\x02\xff\x01" mock_encode.return_value = b"" - actual = self._tcp.buildPacket(message) + actual = self._tcp.buildFrame(message) assert expected == actual # ----------------------------------------------------------------------- # @@ -322,93 +260,25 @@ def test_tcp_framer_packet(self, mock_encode): # ----------------------------------------------------------------------- # def test_framer_tls_framer_transaction_ready(self): """Test a tls frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - self._tcp.processIncomingPacket(msg[0:4], callback) - assert not result - self._tcp.processIncomingPacket(msg[4:], callback) - assert result + assert not self._tcp.processIncomingFrame(msg[0:4]) + assert self._tcp.processIncomingFrame(msg[4:]) def test_framer_tls_framer_transaction_full(self): """Test a full tls frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - self._tcp.processIncomingPacket(msg, callback) - assert result - - def test_framer_tls_framer_transaction_half(self): - """Test a half completed tls frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - self._tcp.processIncomingPacket(msg[0:8], callback) - assert not result - self._tcp.processIncomingPacket(msg[8:], callback) - assert result - - def test_framer_tls_framer_transaction_short(self): - """Test that we can get back on track after an invalid message.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - self._tcp.processIncomingPacket(msg[0:2], callback) - assert not result - self._tcp.processIncomingPacket(msg[2:], callback) - assert result + assert self._tcp.processIncomingFrame(msg) def test_framer_tls_incoming_packet(self): """Framer tls incoming packet.""" msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - msg_result = None - - def mock_callback(result): - """Mock callback.""" - nonlocal msg_result - - msg_result = result.encode() - - self._tls.processIncomingPacket(msg, mock_callback) - # assert msg == msg_result + result = self._tls.processIncomingFrame(msg) + assert result def test_framer_tls_framer_populate(self): """Test a tls frame packet build.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x12\x34\x00\x06\xff\x02\x12\x34\x01\x02" - self._tcp.processIncomingPacket(msg, callback) - assert result + assert self._tcp.processIncomingFrame(msg) @mock.patch.object(ModbusRequest, "encode") def test_framer_tls_framer_packet(self, mock_encode): @@ -417,7 +287,7 @@ def test_framer_tls_framer_packet(self, mock_encode): message.function_code = 0x01 expected = b"\x01" mock_encode.return_value = b"" - actual = self._tls.buildPacket(message) + actual = self._tls.buildFrame(message) assert expected == actual # ----------------------------------------------------------------------- # @@ -425,63 +295,26 @@ def test_framer_tls_framer_packet(self, mock_encode): # ----------------------------------------------------------------------- # def test_rtu_framer_transaction_ready(self): """Test if the checks for a complete frame work.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg_parts = [b"\x00\x01\x00", b"\x00\x00\x01\xfc\x1b"] - self._rtu.processIncomingPacket(msg_parts[0], callback) - assert not result - self._rtu.processIncomingPacket(msg_parts[1], callback) - assert result + assert not self._rtu.processIncomingFrame(msg_parts[0]) + assert self._rtu.processIncomingFrame(msg_parts[1]) def test_rtu_framer_transaction_full(self): """Test a full rtu frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x00\x00\x00\x01\xfc\x1b" - self._rtu.processIncomingPacket(msg, callback) - assert result + assert self._rtu.processIncomingFrame(msg) def test_rtu_framer_transaction_half(self): """Test a half completed rtu frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg_parts = [b"\x00\x01\x00", b"\x00\x00\x01\xfc\x1b"] - self._rtu.processIncomingPacket(msg_parts[0], callback) - assert not result - self._rtu.processIncomingPacket(msg_parts[1], callback) - assert result + assert not self._rtu.processIncomingFrame(msg_parts[0]) + assert self._rtu.processIncomingFrame(msg_parts[1]) def test_rtu_framer_populate(self): """Test a rtu frame packet build.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x00\x00\x00\x01\xfc\x1b" - self._rtu.processIncomingPacket(msg, callback) - assert int(msg[0]) == self._rtu.incoming_dev_id + result = self._rtu.processIncomingFrame(msg) + assert int(msg[0]) == result.slave_id @mock.patch.object(ModbusRequest, "encode") def test_rtu_framer_packet(self, mock_encode): @@ -491,108 +324,44 @@ def test_rtu_framer_packet(self, mock_encode): message.function_code = 0x01 expected = b"\xff\x01\x81\x80" # only header + CRC - no data mock_encode.return_value = b"" - actual = self._rtu.buildPacket(message) + actual = self._rtu.buildFrame(message) assert expected == actual def test_rtu_decode_exception(self): """Test that the RTU framer can decode errors.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x90\x02\x9c\x01" - self._rtu.processIncomingPacket(msg, callback) - assert result + assert self._rtu.processIncomingFrame(msg) def test_process(self): """Test process.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x00\x00\x00\x01\xfc\x1b" - self._rtu.processIncomingPacket(msg, callback) - assert result + assert self._rtu.processIncomingFrame(msg) def test_rtu_process_incoming_packets(self): """Test rtu process incoming packets.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"\x00\x01\x00\x00\x00\x01\xfc\x1b" - self._rtu.processIncomingPacket(msg, callback) - assert result + assert self._rtu.processIncomingFrame(msg) # ----------------------------------------------------------------------- # # ASCII tests # ----------------------------------------------------------------------- # def test_ascii_framer_transaction_ready(self): """Test a ascii frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b":F7031389000A60\r\n" - self._ascii.processIncomingPacket(msg, callback) - assert result + assert self._ascii.processIncomingFrame(msg) def test_ascii_framer_transaction_full(self): """Test a full ascii frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b"sss:F7031389000A60\r\n" - self._ascii.processIncomingPacket(msg, callback) - assert result + assert self._ascii.processIncomingFrame(msg) def test_ascii_framer_transaction_half(self): """Test a half completed ascii frame transaction.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg_parts = (b"sss:F7031389", b"000A60\r\n") - self._ascii.processIncomingPacket(msg_parts[0], callback) - assert not result - self._ascii.processIncomingPacket(msg_parts[1], callback) - assert result + assert not self._ascii.processIncomingFrame(msg_parts[0]) + assert self._ascii.processIncomingFrame(msg_parts[1]) def test_ascii_process_incoming_packets(self): """Test ascii process incoming packet.""" - count = 0 - result = None - def callback(data): - """Simulate callback.""" - nonlocal count, result - count += 1 - result = data - msg = b":F7031389000A60\r\n" - self._ascii.processIncomingPacket(msg, callback) - assert result + assert self._ascii.processIncomingFrame(msg) diff --git a/test/sub_server/test_server_asyncio.py b/test/sub_server/test_server_asyncio.py index fb9d8dbfc..a8027583f 100755 --- a/test/sub_server/test_server_asyncio.py +++ b/test/sub_server/test_server_asyncio.py @@ -215,12 +215,12 @@ async def test_async_tcp_server_receive_data(self): BasicClient.data = b"\x01\x00\x00\x00\x00\x06\x01\x03\x00\x00\x00\x19" await self.start_server() with mock.patch( - "pymodbus.framer.FramerSocket.processIncomingPacket", + "pymodbus.framer.FramerSocket.processIncomingFrame", new_callable=mock.Mock, ) as process: await self.connect_server() process.assert_called_once() - assert process.call_args[1]["data"] == BasicClient.data + assert process.call_args[0][0] == BasicClient.data async def test_async_tcp_server_roundtrip(self): """Test sending and receiving data on tcp socket.""" @@ -345,7 +345,7 @@ async def test_async_udp_server_exception(self): BasicClient.done = asyncio.Future() await self.start_server(do_udp=True) with mock.patch( - "pymodbus.framer.FramerSocket.processIncomingPacket", + "pymodbus.framer.FramerSocket.processIncomingFrame", new_callable=lambda: mock.Mock(side_effect=Exception), ): # get the random server port pylint: disable=protected-access @@ -361,7 +361,7 @@ async def test_async_tcp_server_exception(self): BasicClient.data = b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" await self.start_server() with mock.patch( - "pymodbus.framer.FramerSocket.processIncomingPacket", + "pymodbus.framer.FramerSocket.processIncomingFrame", new_callable=lambda: mock.Mock(side_effect=Exception), ): await self.connect_server()