diff --git a/API_changes.rst b/API_changes.rst index 8da7eb23d..49c740642 100644 --- a/API_changes.rst +++ b/API_changes.rst @@ -8,7 +8,8 @@ API changes 3.8.0 - Remove skip_encode parameter. - rename ModbusExceptions enums to legal constants. - enforce client keyword only parameters (positional not allowed). - +- added trace_packet/pdu/connect to client +- removed on_connect_callback from client API changes 3.7.0 ----------------- diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 7d30667c0..270a9a783 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -5,7 +5,6 @@ from collections.abc import Awaitable, Callable from pymodbus.client.mixin import ModbusClientMixin -from pymodbus.client.modbusclientprotocol import ModbusClientProtocol from pymodbus.exceptions import ConnectionException from pymodbus.framer import FRAMER_NAME_TO_CLASS, FramerBase, FramerType from pymodbus.logging import Log @@ -25,8 +24,10 @@ def __init__( self, framer: FramerType, retries: int, - on_connect_callback: Callable[[bool], None] | None, comm_params: CommParams, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize a client instance. @@ -34,11 +35,14 @@ def __init__( """ ModbusClientMixin.__init__(self) # type: ignore[arg-type] self.comm_params = comm_params - self.ctx = ModbusClientProtocol( + self.ctx = TransactionManager( + comm_params, (FRAMER_NAME_TO_CLASS[framer])(DecodePDU(False)), - self.comm_params, retries, - on_connect_callback, + False, + trace_packet=trace_packet, + trace_pdu=trace_pdu, + trace_connect=trace_connect, ) self.state = ModbusTransactionState.IDLE @@ -115,6 +119,9 @@ def __init__( framer: FramerType, retries: int, comm_params: CommParams, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize a client instance. @@ -132,7 +139,10 @@ def __init__( self.framer, retries, False, - self, + trace_packet=trace_packet, + trace_pdu=trace_pdu, + trace_connect=trace_connect, + sync_client=self, ) self.reconnect_delay_current = self.comm_params.reconnect_delay or 0 self.use_udp = False diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index 7f6888845..21cbc3c45 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -2,6 +2,7 @@ from __future__ import annotations import struct +from abc import abstractmethod from enum import Enum from typing import Generic, TypeVar @@ -48,17 +49,9 @@ class ModbusClientMixin(Generic[T]): # pylint: disable=too-many-public-methods def __init__(self): """Initialize.""" - def execute(self, _no_response_expected: bool, _request: ModbusPDU,) -> T: - """Execute request (code ???). - - :raises ModbusException: - - Call with custom function codes. - - .. tip:: - Response is not interpreted. - """ - raise NotImplementedError("execute of ModbusClientMixin needs to be overridden") + @abstractmethod + def execute(self, no_response_expected: bool, request: ModbusPDU,) -> T: + """Execute request.""" def read_coils(self, address: int, *, count: int = 1, slave: int = 1, no_response_expected: bool = False) -> T: """Read coils (code 0x01). diff --git a/pymodbus/client/modbusclientprotocol.py b/pymodbus/client/modbusclientprotocol.py deleted file mode 100644 index bb47c6578..000000000 --- a/pymodbus/client/modbusclientprotocol.py +++ /dev/null @@ -1,49 +0,0 @@ -"""ModbusProtocol implementation for all clients.""" -from __future__ import annotations - -from collections.abc import Callable - -from pymodbus.framer import FramerBase -from pymodbus.logging import Log -from pymodbus.transaction import TransactionManager -from pymodbus.transport import CommParams - - -class ModbusClientProtocol(TransactionManager): - """**ModbusClientProtocol**. - - :mod:`ModbusClientProtocol` is normally not referenced outside :mod:`pymodbus`. - """ - - def __init__( - self, - framer: FramerBase, - params: CommParams, - retries: int, - on_connect_callback: Callable[[bool], None] | None = None, - ) -> None: - """Initialize a client instance.""" - super().__init__(params, framer, retries, False) - self.on_connect_callback = on_connect_callback - - def callback_connected(self) -> None: - """Call when connection is succcesfull.""" - super().callback_connected() - if self.on_connect_callback: - self.loop.call_soon(self.on_connect_callback, True) - - def callback_disconnected(self, exc: Exception | None) -> None: - """Call when connection is lost.""" - Log.debug("callback_disconnected called: {}", exc) - super().callback_disconnected(exc) - if self.on_connect_callback: - self.loop.call_soon(self.on_connect_callback, False) - - def __str__(self): - """Build a string representation of the connection. - - :returns: The string representation - """ - return ( - f"{self.__class__.__name__} {self.comm_params.host}:{self.comm_params.port}" - ) diff --git a/pymodbus/client/serial.py b/pymodbus/client/serial.py index aa5499cdd..f400354f2 100644 --- a/pymodbus/client/serial.py +++ b/pymodbus/client/serial.py @@ -11,6 +11,7 @@ from pymodbus.exceptions import ConnectionException from pymodbus.framer import FramerType from pymodbus.logging import Log +from pymodbus.pdu import ModbusPDU from pymodbus.transport import CommParams, CommType from pymodbus.utilities import ModbusTransactionState @@ -39,7 +40,12 @@ class AsyncModbusSerialClient(ModbusBaseClient): :param reconnect_delay_max: Maximum delay in seconds.milliseconds before reconnecting. :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. - :param on_connect_callback: Function that will be called just before a connection attempt. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected + + .. tip:: + The trace methods allow to modify the datastream/pdu ! .. tip:: **reconnect_delay** doubles automatically with each unsuccessful connect, from @@ -75,7 +81,9 @@ def __init__( # pylint: disable=too-many-arguments reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, - on_connect_callback: Callable[[bool], None] | None = None, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize Asyncio Modbus Serial Client.""" if "serial" not in sys.modules: # pragma: no cover @@ -102,7 +110,6 @@ def __init__( # pylint: disable=too-many-arguments self, framer, retries, - on_connect_callback, self.comm_params, ) @@ -127,11 +134,12 @@ class ModbusSerialClient(ModbusBaseSyncClient): :param reconnect_delay_max: Not used in the sync client :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected - Note that unlike the async client, the sync client does not perform - retries. If the connection has closed, the client will attempt to reconnect - once before executing each read/write request, and will raise a - ConnectionException if this fails. + .. tip:: + The trace methods allow to modify the datastream/pdu ! Example:: @@ -166,6 +174,9 @@ def __init__( # pylint: disable=too-many-arguments reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize Modbus Serial Client.""" if "serial" not in sys.modules: # pragma: no cover @@ -192,6 +203,9 @@ def __init__( # pylint: disable=too-many-arguments framer, retries, self.comm_params, + trace_connect=trace_connect, + trace_packet=trace_packet, + trace_pdu=trace_pdu, ) self.socket: serial.Serial | None = None self.last_frame_end = None diff --git a/pymodbus/client/tcp.py b/pymodbus/client/tcp.py index a8cb51815..0e662addd 100644 --- a/pymodbus/client/tcp.py +++ b/pymodbus/client/tcp.py @@ -11,6 +11,7 @@ from pymodbus.exceptions import ConnectionException from pymodbus.framer import FramerType from pymodbus.logging import Log +from pymodbus.pdu import ModbusPDU from pymodbus.transport import CommParams, CommType @@ -31,7 +32,12 @@ class AsyncModbusTcpClient(ModbusBaseClient): :param reconnect_delay_max: Maximum delay in seconds.milliseconds before reconnecting. :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. - :param on_connect_callback: Function that will be called just before a connection attempt. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected + + .. tip:: + The trace methods allow to modify the datastream/pdu ! .. tip:: **reconnect_delay** doubles automatically with each unsuccessful connect, from @@ -64,7 +70,9 @@ def __init__( # pylint: disable=too-many-arguments reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, - on_connect_callback: Callable[[bool], None] | None = None, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize Asyncio Modbus TCP Client.""" if not hasattr(self,"comm_params"): @@ -84,7 +92,6 @@ def __init__( # pylint: disable=too-many-arguments self, framer, retries, - on_connect_callback, self.comm_params, ) @@ -106,12 +113,12 @@ class ModbusTcpClient(ModbusBaseSyncClient): :param reconnect_delay_max: Not used in the sync client :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected .. tip:: - Unlike the async client, the sync client does not perform - retries. If the connection has closed, the client will attempt to reconnect - once before executing each read/write request, and will raise a - ConnectionException if this fails. + The trace methods allow to modify the datastream/pdu ! Example:: @@ -129,7 +136,7 @@ async def run(): socket: socket.socket | None - def __init__( + def __init__( # pylint: disable=too-many-arguments self, host: str, *, @@ -141,6 +148,9 @@ def __init__( reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize Modbus TCP Client.""" if not hasattr(self,"comm_params"): diff --git a/pymodbus/client/tls.py b/pymodbus/client/tls.py index 94b7060e3..852546ca2 100644 --- a/pymodbus/client/tls.py +++ b/pymodbus/client/tls.py @@ -8,6 +8,7 @@ from pymodbus.client.tcp import AsyncModbusTcpClient, ModbusTcpClient from pymodbus.framer import FramerType from pymodbus.logging import Log +from pymodbus.pdu import ModbusPDU from pymodbus.transport import CommParams, CommType @@ -29,7 +30,12 @@ class AsyncModbusTlsClient(AsyncModbusTcpClient): :param reconnect_delay_max: Maximum delay in seconds.milliseconds before reconnecting. :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. - :param on_connect_callback: Function that will be called just before a connection attempt. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected + + .. tip:: + The trace methods allow to modify the datastream/pdu ! .. tip:: **reconnect_delay** doubles automatically with each unsuccessful connect, from @@ -63,7 +69,9 @@ def __init__( # pylint: disable=too-many-arguments reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, - on_connect_callback: Callable[[bool], None] | None = None, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ): """Initialize Asyncio Modbus TLS Client.""" if framer not in [FramerType.TLS]: @@ -84,7 +92,6 @@ def __init__( # pylint: disable=too-many-arguments "", framer=framer, retries=retries, - on_connect_callback=on_connect_callback, ) @classmethod @@ -127,12 +134,12 @@ class ModbusTlsClient(ModbusTcpClient): :param reconnect_delay_max: Not used in the sync client :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected .. tip:: - Unlike the async client, the sync client does not perform - retries. If the connection has closed, the client will attempt to reconnect - once before executing each read/write request, and will raise a - ConnectionException if this fails. + The trace methods allow to modify the datastream/pdu ! Example:: @@ -161,6 +168,9 @@ def __init__( # pylint: disable=too-many-arguments reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ): """Initialize Modbus TLS Client.""" if framer not in [FramerType.TLS]: diff --git a/pymodbus/client/udp.py b/pymodbus/client/udp.py index cf5651d82..76087afec 100644 --- a/pymodbus/client/udp.py +++ b/pymodbus/client/udp.py @@ -9,6 +9,7 @@ from pymodbus.exceptions import ConnectionException from pymodbus.framer import FramerType from pymodbus.logging import Log +from pymodbus.pdu import ModbusPDU from pymodbus.transport import CommParams, CommType @@ -32,7 +33,12 @@ class AsyncModbusUdpClient(ModbusBaseClient): :param reconnect_delay_max: Maximum delay in seconds.milliseconds before reconnecting. :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. - :param on_connect_callback: Function that will be called just before a connection attempt. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected + + .. tip:: + The trace methods allow to modify the datastream/pdu ! .. tip:: **reconnect_delay** doubles automatically with each unsuccessful connect, from @@ -65,7 +71,9 @@ def __init__( # pylint: disable=too-many-arguments reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, - on_connect_callback: Callable[[bool], None] | None = None, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize Asyncio Modbus UDP Client.""" self.comm_params = CommParams( @@ -84,7 +92,6 @@ def __init__( # pylint: disable=too-many-arguments self, framer, retries, - on_connect_callback, self.comm_params, ) self.source_address = source_address @@ -107,12 +114,12 @@ class ModbusUdpClient(ModbusBaseSyncClient): :param reconnect_delay_max: Not used in the sync client :param timeout: Timeout for connecting and receiving data, in seconds. :param retries: Max number of retries per request. + :param trace_packet: Called with bytestream received/to be sent + :param trace_pdu: Called with PDU received/to be sent + :param trace_connect: Called when connected/disconnected .. tip:: - Unlike the async client, the sync client does not perform - retries. If the connection has closed, the client will attempt to reconnect - once before executing each read/write request, and will raise a - ConnectionException if this fails. + The trace methods allow to modify the datastream/pdu ! Example:: @@ -130,7 +137,7 @@ async def run(): socket: socket.socket | None - def __init__( + def __init__( # pylint: disable=too-many-arguments self, host: str, *, @@ -142,6 +149,9 @@ def __init__( reconnect_delay_max: float = 300, timeout: float = 3, retries: int = 3, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, ) -> None: """Initialize Modbus UDP Client.""" if framer not in [FramerType.SOCKET, FramerType.RTU, FramerType.ASCII]: diff --git a/pymodbus/server/async_io.py b/pymodbus/server/async_io.py index 40d837613..1f92ea015 100644 --- a/pymodbus/server/async_io.py +++ b/pymodbus/server/async_io.py @@ -52,12 +52,6 @@ def __init__(self, owner): 0, True) - def _log_exception(self): - """Show log exception.""" - Log.debug( - "Handler for stream [{}] has been canceled", self.comm_params.comm_name - ) - def callback_new_connection(self) -> ModbusProtocol: """Call when listener receive new connection request.""" Log.debug("callback_new_connection called") @@ -92,7 +86,9 @@ def callback_disconnected(self, call_exc: Exception | None) -> None: if hasattr(self.server, "on_connection_lost"): self.server.on_connection_lost() if call_exc is None: - self._log_exception() + Log.debug( + "Handler for stream [{}] has been canceled", self.comm_params.comm_name + ) else: Log.debug( "Client Disconnection {} due to {}", @@ -136,7 +132,9 @@ async def handle(self) -> None: except asyncio.CancelledError: # catch and ignore cancellation errors if self.running: - self._log_exception() + Log.debug( + "Handler for stream [{}] has been canceled", self.comm_params.comm_name + ) self.running = False except Exception as exc: # pylint: disable=broad-except # force TCP socket termination as framer @@ -655,22 +653,22 @@ async def StartAsyncSerialServer( # pylint: disable=invalid-name,dangerous-defa def StartSerialServer(**kwargs): # pylint: disable=invalid-name - """Start and run a serial modbus server.""" + """Start and run a modbus serial server.""" return asyncio.run(StartAsyncSerialServer(**kwargs)) def StartTcpServer(**kwargs): # pylint: disable=invalid-name - """Start and run a serial modbus server.""" + """Start and run a modbus TCP server.""" return asyncio.run(StartAsyncTcpServer(**kwargs)) def StartTlsServer(**kwargs): # pylint: disable=invalid-name - """Start and run a serial modbus server.""" + """Start and run a modbus TLS server.""" return asyncio.run(StartAsyncTlsServer(**kwargs)) def StartUdpServer(**kwargs): # pylint: disable=invalid-name - """Start and run a serial modbus server.""" + """Start and run a modbus UDP server.""" return asyncio.run(StartAsyncUdpServer(**kwargs)) diff --git a/pymodbus/transaction/transaction.py b/pymodbus/transaction/transaction.py index 5533ab835..a7c38b39b 100644 --- a/pymodbus/transaction/transaction.py +++ b/pymodbus/transaction/transaction.py @@ -40,6 +40,9 @@ def __init__( framer: FramerBase, retries: int, is_server: bool, + trace_packet: Callable[[bool, bytes | None], bytes] | None = None, + trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None, + trace_connect: Callable[[bool], None] | None = None, sync_client = None, ) -> None: """Initialize an instance of the ModbusTransactionManager.""" @@ -47,10 +50,9 @@ def __init__( self.framer = framer self.retries = retries self.next_tid: int = 0 - self.trace_recv_packet: Callable[[bytes | None], bytes] | None = None - self.trace_recv_pdu: Callable[[ModbusPDU | None], ModbusPDU] | None = None - self.trace_send_packet: Callable[[bytes | None], bytes] | None = None - self.trace_send_pdu: Callable[[ModbusPDU | None], ModbusPDU] | None = None + self.trace_packet = trace_packet or self.dummy_trace_packet + self.trace_pdu = trace_pdu or self.dummy_trace_pdu + self.trace_connect = trace_connect or self.dummy_trace_connect self.accept_no_response_limit = retries + 3 self.count_no_responses = 0 if sync_client: @@ -60,6 +62,20 @@ def __init__( self._lock = asyncio.Lock() self.response_future: asyncio.Future = asyncio.Future() + def dummy_trace_packet(self, sending: bool, data: bytes) -> bytes: + """Do dummy trace.""" + _ = sending + return data + + def dummy_trace_pdu(self, sending: bool, pdu: ModbusPDU) -> ModbusPDU: + """Do dummy trace.""" + _ = sending + return pdu + + def dummy_trace_connect(self, connect: bool) -> None: + """Do dummy trace.""" + _ = connect + def sync_get_response(self) -> ModbusPDU: """Receive until PDU is correct or timeout.""" databuffer = b'' @@ -84,14 +100,10 @@ def sync_execute(self, no_response_expected: bool, request: ModbusPDU) -> Modbus raise ConnectionException("Client cannot connect (automatic retry continuing) !!") with self._sync_lock: request.transaction_id = self.getNextTID() - if self.trace_send_pdu: - request = self.trace_send_pdu(request) # pylint: disable=not-callable - packet = self.framer.buildFrame(request) + packet = self.framer.buildFrame(self.trace_pdu(True, request)) count_retries = 0 while count_retries <= self.retries: - if self.trace_send_packet: - packet = self.trace_send_packet(packet) # pylint: disable=not-callable - self.sync_client.send(packet) + self.sync_client.send(self.trace_packet(True, packet)) if no_response_expected: return ExceptionResponse(0xff) try: @@ -120,14 +132,10 @@ async def execute(self, no_response_expected: bool, request: ModbusPDU) -> Modbu raise ConnectionException("Client cannot connect (automatic retry continuing) !!") async with self._lock: request.transaction_id = self.getNextTID() - if self.trace_send_pdu: - request = self.trace_send_pdu(request) # pylint: disable=not-callable - packet = self.framer.buildFrame(request) + packet = self.framer.buildFrame(self.trace_pdu(True, request)) count_retries = 0 while count_retries <= self.retries: - if self.trace_send_packet: - packet = self.trace_send_packet(packet) # pylint: disable=not-callable - self.send(packet) + self.send(self.trace_packet(True, packet)) if no_response_expected: return None try: @@ -166,30 +174,23 @@ def callback_connected(self) -> None: """Call when connection is succcesfull.""" self.count_no_responses = 0 self.next_tid = 0 + self.trace_connect(True) def callback_disconnected(self, exc: Exception | None) -> None: """Call when connection is lost.""" - for call in (self.trace_recv_packet, - self.trace_recv_pdu, - self.trace_send_packet, - self.trace_send_pdu): - if call: - call(None) # pylint: disable=not-callable + self.trace_connect(False) def callback_data(self, data: bytes, addr: tuple | None = None) -> int: """Handle received data.""" - if self.trace_recv_packet: - data = self.trace_recv_packet(data) # pylint: disable=not-callable try: - used_len, pdu = self.framer.processIncomingFrame(data) + used_len, pdu = self.framer.processIncomingFrame(self.trace_packet(False, data)) except ModbusIOException as exc: if self.is_server: self.response_future.set_result((None, addr, exc)) return len(data) raise exc if pdu: - if self.trace_recv_pdu: - pdu = self.trace_recv_pdu(pdu) # pylint: disable=not-callable + pdu = self.trace_pdu(False, pdu) result = (pdu, addr, None) if self.is_server else pdu self.response_future.set_result(result) return used_len diff --git a/test/client/test_client.py b/test/client/test_client.py index 6a6a80a94..f6dbb79ba 100755 --- a/test/client/test_client.py +++ b/test/client/test_client.py @@ -183,15 +183,6 @@ def test_client_mixin_convert_fail(self): with pytest.raises(ModbusException): ModbusClientMixin.convert_from_registers([123], ModbusClientMixin.DATATYPE.FLOAT64) - async def test_client_mixin_execute(self): - """Test dummy execute for both sync and async.""" - client = ModbusClientMixin() - pdu = ModbusPDU() - with pytest.raises(NotImplementedError): - client.execute(False, pdu) - with pytest.raises(NotImplementedError): - await client.execute(False, pdu) - class TestClientBase: """Test client code.""" @@ -330,7 +321,6 @@ async def test_client_modbusbaseclient(self): client = ModbusBaseClient( FramerType.ASCII, 3, - None, CommParams( host="localhost", port=BASE_PORT + 1, @@ -357,7 +347,6 @@ async def test_client_base_async(self): async with ModbusBaseClient( FramerType.ASCII, 3, - None, CommParams( host="localhost", port=BASE_PORT + 2, @@ -377,7 +366,7 @@ async def test_client_protocol(self): client.ctx.loop = mock.Mock() client.ctx.callback_connected() client.ctx.callback_disconnected(None) - client = lib_client.AsyncModbusTcpClient("127.0.0.1", on_connect_callback=1) + client = lib_client.AsyncModbusTcpClient("127.0.0.1", trace_connect=1) client.ctx.loop = mock.Mock() client.ctx.callback_connected() client.ctx.callback_disconnected(None) @@ -388,7 +377,6 @@ async def test_client_async_execute(self): async with ModbusBaseClient( FramerType.ASCII, 3, - None, CommParams( host="localhost", port=BASE_PORT + 2, diff --git a/test/transaction/test_transaction.py b/test/transaction/test_transaction.py index 78fea0287..c7d6755c2 100755 --- a/test/transaction/test_transaction.py +++ b/test/transaction/test_transaction.py @@ -41,16 +41,13 @@ async def test_transaction_calls(self, use_clc): async def test_transaction_disconnect(self, use_clc): """Test tracers in disconnect.""" transact = TransactionManager(use_clc, FramerRTU(DecodePDU(False)), 5, False) + transact.trace_packet = mock.Mock() + transact.trace_pdu = mock.Mock() + transact.trace_connect = mock.Mock() transact.callback_disconnected(None) - transact.trace_recv_packet = mock.Mock() - transact.trace_recv_pdu = mock.Mock() - transact.trace_send_packet = mock.Mock() - transact.trace_send_pdu = mock.Mock() - transact.callback_disconnected(None) - transact.trace_recv_packet.assert_called_once_with(None) - transact.trace_recv_pdu.assert_called_once_with(None) - transact.trace_send_packet.assert_called_once_with(None) - transact.trace_send_pdu.assert_called_once_with(None) + transact.trace_connect.assert_called_once_with(False) + transact.trace_packet.assert_not_called() + transact.trace_pdu.assert_not_called() @pytest.mark.parametrize("test", [True, False]) async def test_transaction_data(self, use_clc, test): @@ -60,19 +57,19 @@ async def test_transaction_data(self, use_clc, test): packet = b'123' transact.callback_data(packet) assert not transact.response_future.done() - transact.trace_recv_packet = mock.Mock() + transact.trace_packet = mock.Mock(return_value=packet) pdu = "dummy pdu" if test: transact.framer.processIncomingFrame.return_value = (1, pdu) transact.callback_data(packet) - transact.trace_recv_packet.assert_called_once_with(packet) + transact.trace_packet.assert_called_once_with(False, packet) else: - transact.trace_recv_pdu = mock.Mock(return_value=pdu) + transact.trace_pdu = mock.Mock(return_value=pdu) transact.framer.processIncomingFrame.return_value = (1, pdu) transact.callback_data(packet) - transact.trace_recv_packet.assert_called_with(packet) - transact.trace_recv_pdu.assert_called_once_with(pdu) + transact.trace_packet.assert_called_with(False, packet) + transact.trace_pdu.assert_called_once_with(False, pdu) assert transact.response_future.result() == pdu @pytest.mark.parametrize("scenario", range(6)) @@ -93,11 +90,11 @@ async def test_transaction_execute(self, use_clc, scenario): transact.connect = mock.AsyncMock(return_value=1) await transact.execute(True, request) elif scenario == 2: # transport ok, trace and send - transact.trace_send_pdu = mock.Mock(return_value=request) - transact.trace_send_packet = mock.Mock(return_value=b'123') + transact.trace_pdu = mock.Mock(return_value=request) + transact.trace_packet = mock.Mock(return_value=b'123') await transact.execute(True, request) - transact.trace_send_pdu.assert_called_once_with(request) - transact.trace_send_packet.assert_called_once_with(b'\x00\x01\x00u\x00\x05\xec\x02') + transact.trace_pdu.assert_called_once_with(True, request) + transact.trace_packet.assert_called_once_with(True, b'\x00\x01\x00u\x00\x05\xec\x02') elif scenario == 3: # wait receive,timeout, no_responses transact.comm_params.timeout_connect = 0.1 transact.count_no_responses = 10 @@ -179,11 +176,11 @@ async def test_sync_transaction_execute(self, use_clc, scenario): transact.sync_client.connect = mock.Mock(return_value=True) transact.sync_execute(True, request) elif scenario == 2: # transport ok, trace and send - transact.trace_send_pdu = mock.Mock(return_value=request) - transact.trace_send_packet = mock.Mock(return_value=b'123') + transact.trace_pdu = mock.Mock(return_value=request) + transact.trace_packet = mock.Mock(return_value=b'123') transact.sync_execute(True, request) - transact.trace_send_pdu.assert_called_once_with(request) - transact.trace_send_packet.assert_called_once_with(b'\x00\x01\x00u\x00\x05\xec\x02') + transact.trace_pdu.assert_called_once_with(True, request) + transact.trace_packet.assert_called_once_with(True, b'\x00\x01\x00u\x00\x05\xec\x02') elif scenario == 3: # wait receive,timeout, no_responses transact.comm_params.timeout_connect = 0.1 transact.count_no_responses = 10