From af6630a35995be60f5fdc14a0d2b69d52cc7f783 Mon Sep 17 00:00:00 2001 From: Laurin Schmidt Date: Thu, 8 Feb 2024 14:30:38 +0100 Subject: [PATCH 1/7] make client typehints generic so that concrete client can decide on awaitable vs sync --- pymodbus/client/base.py | 10 +-- pymodbus/client/mixin.py | 136 +++++++++++---------------------- test/sub_client/test_client.py | 3 +- 3 files changed, 52 insertions(+), 97 deletions(-) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 80fa4b46c..0833bf387 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -4,7 +4,7 @@ import asyncio import socket from dataclasses import dataclass -from typing import Any, Callable, Type, cast +from typing import Any, Awaitable, Callable, Type, cast, TypeVar, Generic from pymodbus.client.mixin import ModbusClientMixin from pymodbus.exceptions import ConnectionException, ModbusIOException @@ -17,7 +17,7 @@ from pymodbus.utilities import ModbusTransactionState -class ModbusBaseClient(ModbusClientMixin, ModbusProtocol): +class ModbusBaseClient(ModbusClientMixin[Awaitable[ModbusResponse]], ModbusProtocol): """**ModbusBaseClient**. Fixed parameters: @@ -136,7 +136,7 @@ def idle_time(self) -> float: return 0 return self.last_frame_end + self.silent_interval - def execute(self, request: ModbusRequest | None = None) -> ModbusResponse: + def execute(self, request: ModbusRequest | None = None): """Execute request and get response (call **sync/async**). :param request: The request to process @@ -150,7 +150,7 @@ def execute(self, request: ModbusRequest | None = None) -> ModbusResponse: # ----------------------------------------------------------------------- # # Merged client methods # ----------------------------------------------------------------------- # - async def async_execute(self, request=None): + async def async_execute(self, request=None) -> ModbusResponse: """Execute requests asynchronously.""" request.transaction_id = self.transaction.getNextTID() packet = self.framer.buildPacket(request) @@ -266,7 +266,7 @@ def __str__(self): f"{self.__class__.__name__} {self.comm_params.host}:{self.comm_params.port}" ) -class ModbusBaseSyncClient(ModbusClientMixin, ModbusProtocol): +class ModbusBaseSyncClient(ModbusClientMixin[ModbusResponse], ModbusProtocol): """**ModbusBaseClient**. Fixed parameters: diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index 87ee7e202..97f821b8f 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -2,9 +2,8 @@ from __future__ import annotations import struct -from collections.abc import Awaitable from enum import Enum -from typing import Any +from typing import Any, Generic, TypeVar import pymodbus.bit_read_message as pdu_bit_read import pymodbus.bit_write_message as pdu_bit_write @@ -15,10 +14,13 @@ import pymodbus.register_read_message as pdu_reg_read import pymodbus.register_write_message as pdu_req_write from pymodbus.exceptions import ModbusException -from pymodbus.pdu import ModbusRequest, ModbusResponse +from pymodbus.pdu import ModbusRequest -class ModbusClientMixin: # pylint: disable=too-many-public-methods +T = TypeVar("T", covariant=False) + + +class ModbusClientMixin(Generic[T]): # pylint: disable=too-many-public-methods """**ModbusClientMixin**. This is an interface class to facilitate the sending requests/receiving responses like read_coils. @@ -47,9 +49,7 @@ class ModbusClientMixin: # pylint: disable=too-many-public-methods def __init__(self): """Initialize.""" - def execute( - self, _request: ModbusRequest - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def execute(self, _request: ModbusRequest) -> T: """Execute request (code ???). :raises ModbusException: @@ -59,11 +59,13 @@ def execute( .. tip:: Response is not interpreted. """ - return ModbusResponse() + raise NotImplementedError( + "The execute method of ModbusClientMixin needs to be overridden and cannot be used directly" + ) def read_coils( self, address: int, count: int = 1, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Read coils (code 0x01). :param address: Start address to read from @@ -78,7 +80,7 @@ def read_coils( def read_discrete_inputs( self, address: int, count: int = 1, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Read discrete inputs (code 0x02). :param address: Start address to read from @@ -93,7 +95,7 @@ def read_discrete_inputs( def read_holding_registers( self, address: int, count: int = 1, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Read holding registers (code 0x03). :param address: Start address to read from @@ -108,7 +110,7 @@ def read_holding_registers( def read_input_registers( self, address: int, count: int = 1, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Read input registers (code 0x04). :param address: Start address to read from @@ -121,9 +123,7 @@ def read_input_registers( pdu_reg_read.ReadInputRegistersRequest(address, count, slave, **kwargs) ) - def write_coil( - self, address: int, value: bool, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def write_coil(self, address: int, value: bool, slave: int = 0, **kwargs: Any) -> T: """Write single coil (code 0x05). :param address: Address to write to @@ -138,7 +138,7 @@ def write_coil( def write_register( self, address: int, value: int, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Write register (code 0x06). :param address: Address to write to @@ -151,9 +151,7 @@ def write_register( pdu_req_write.WriteSingleRegisterRequest(address, value, slave, **kwargs) ) - def read_exception_status( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def read_exception_status(self, slave: int = 0, **kwargs: Any) -> T: """Read Exception Status (code 0x07). :param slave: (optional) Modbus slave ID @@ -162,9 +160,7 @@ def read_exception_status( """ return self.execute(pdu_other_msg.ReadExceptionStatusRequest(slave, **kwargs)) - def diag_query_data( - self, msg: bytearray, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_query_data(self, msg: bytearray, slave: int = 0, **kwargs: Any) -> T: """Diagnose query data (code 0x08 sub 0x00). :param msg: Message to be returned @@ -176,7 +172,7 @@ def diag_query_data( def diag_restart_communication( self, toggle: bool, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Diagnose restart communication (code 0x08 sub 0x01). :param toggle: True if toggled. @@ -188,9 +184,7 @@ def diag_restart_communication( pdu_diag.RestartCommunicationsOptionRequest(toggle, slave=slave, **kwargs) ) - def diag_read_diagnostic_register( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_diagnostic_register(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read diagnostic register (code 0x08 sub 0x02). :param slave: (optional) Modbus slave ID @@ -201,9 +195,7 @@ def diag_read_diagnostic_register( pdu_diag.ReturnDiagnosticRegisterRequest(slave=slave, **kwargs) ) - def diag_change_ascii_input_delimeter( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_change_ascii_input_delimeter(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose change ASCII input delimiter (code 0x08 sub 0x03). :param slave: (optional) Modbus slave ID @@ -214,9 +206,7 @@ def diag_change_ascii_input_delimeter( pdu_diag.ChangeAsciiInputDelimiterRequest(slave=slave, **kwargs) ) - def diag_force_listen_only( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_force_listen_only(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose force listen only (code 0x08 sub 0x04). :param slave: (optional) Modbus slave ID @@ -225,9 +215,7 @@ def diag_force_listen_only( """ return self.execute(pdu_diag.ForceListenOnlyModeRequest(slave=slave, **kwargs)) - def diag_clear_counters( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_clear_counters(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose clear counters (code 0x08 sub 0x0A). :param slave: (optional) Modbus slave ID @@ -236,9 +224,7 @@ def diag_clear_counters( """ return self.execute(pdu_diag.ClearCountersRequest(slave=slave, **kwargs)) - def diag_read_bus_message_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_bus_message_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read bus message count (code 0x08 sub 0x0B). :param slave: (optional) Modbus slave ID @@ -249,9 +235,7 @@ def diag_read_bus_message_count( pdu_diag.ReturnBusMessageCountRequest(slave=slave, **kwargs) ) - def diag_read_bus_comm_error_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_bus_comm_error_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Bus Communication Error Count (code 0x08 sub 0x0C). :param slave: (optional) Modbus slave ID @@ -262,9 +246,7 @@ def diag_read_bus_comm_error_count( pdu_diag.ReturnBusCommunicationErrorCountRequest(slave=slave, **kwargs) ) - def diag_read_bus_exception_error_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_bus_exception_error_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Bus Exception Error Count (code 0x08 sub 0x0D). :param slave: (optional) Modbus slave ID @@ -275,9 +257,7 @@ def diag_read_bus_exception_error_count( pdu_diag.ReturnBusExceptionErrorCountRequest(slave=slave, **kwargs) ) - def diag_read_slave_message_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_slave_message_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Slave Message Count (code 0x08 sub 0x0E). :param slave: (optional) Modbus slave ID @@ -288,9 +268,7 @@ def diag_read_slave_message_count( pdu_diag.ReturnSlaveMessageCountRequest(slave=slave, **kwargs) ) - def diag_read_slave_no_response_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_slave_no_response_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Slave No Response Count (code 0x08 sub 0x0F). :param slave: (optional) Modbus slave ID @@ -301,9 +279,7 @@ def diag_read_slave_no_response_count( pdu_diag.ReturnSlaveNoResponseCountRequest(slave=slave, **kwargs) ) - def diag_read_slave_nak_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_slave_nak_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Slave NAK Count (code 0x08 sub 0x10). :param slave: (optional) Modbus slave ID @@ -312,9 +288,7 @@ def diag_read_slave_nak_count( """ return self.execute(pdu_diag.ReturnSlaveNAKCountRequest(slave=slave, **kwargs)) - def diag_read_slave_busy_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_slave_busy_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Slave Busy Count (code 0x08 sub 0x11). :param slave: (optional) Modbus slave ID @@ -323,9 +297,7 @@ def diag_read_slave_busy_count( """ return self.execute(pdu_diag.ReturnSlaveBusyCountRequest(slave=slave, **kwargs)) - def diag_read_bus_char_overrun_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_bus_char_overrun_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Bus Character Overrun Count (code 0x08 sub 0x12). :param slave: (optional) Modbus slave ID @@ -336,9 +308,7 @@ def diag_read_bus_char_overrun_count( pdu_diag.ReturnSlaveBusCharacterOverrunCountRequest(slave=slave, **kwargs) ) - def diag_read_iop_overrun_count( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_read_iop_overrun_count(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose read Iop overrun count (code 0x08 sub 0x13). :param slave: (optional) Modbus slave ID @@ -349,9 +319,7 @@ def diag_read_iop_overrun_count( pdu_diag.ReturnIopOverrunCountRequest(slave=slave, **kwargs) ) - def diag_clear_overrun_counter( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_clear_overrun_counter(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose Clear Overrun Counter and Flag (code 0x08 sub 0x14). :param slave: (optional) Modbus slave ID @@ -360,9 +328,7 @@ def diag_clear_overrun_counter( """ return self.execute(pdu_diag.ClearOverrunCountRequest(slave=slave, **kwargs)) - def diag_getclear_modbus_response( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_getclear_modbus_response(self, slave: int = 0, **kwargs: Any) -> T: """Diagnose Get/Clear modbus plus (code 0x08 sub 0x15). :param slave: (optional) Modbus slave ID @@ -371,9 +337,7 @@ def diag_getclear_modbus_response( """ return self.execute(pdu_diag.GetClearModbusPlusRequest(slave=slave, **kwargs)) - def diag_get_comm_event_counter( - self, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_get_comm_event_counter(self, **kwargs: Any) -> T: """Diagnose get event counter (code 0x0B). :param kwargs: (optional) Experimental parameters. @@ -381,9 +345,7 @@ def diag_get_comm_event_counter( """ return self.execute(pdu_other_msg.GetCommEventCounterRequest(**kwargs)) - def diag_get_comm_event_log( - self, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def diag_get_comm_event_log(self, **kwargs: Any) -> T: """Diagnose get event counter (code 0x0C). :param kwargs: (optional) Experimental parameters. @@ -397,7 +359,7 @@ def write_coils( values: list[bool] | bool, slave: int = 0, **kwargs: Any, - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Write coils (code 0x0F). :param address: Start address to write to @@ -412,7 +374,7 @@ def write_coils( def write_registers( self, address: int, values: list[int] | int, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Write registers (code 0x10). :param address: Start address to write to @@ -427,9 +389,7 @@ def write_registers( ) ) - def report_slave_id( - self, slave: int = 0, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def report_slave_id(self, slave: int = 0, **kwargs: Any) -> T: """Report slave ID (code 0x11). :param slave: (optional) Modbus slave ID @@ -438,9 +398,7 @@ def report_slave_id( """ return self.execute(pdu_other_msg.ReportSlaveIdRequest(slave, **kwargs)) - def read_file_record( - self, records: list[tuple], **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def read_file_record(self, records: list[tuple], **kwargs: Any) -> T: """Read file record (code 0x14). :param records: List of (Reference type, File number, Record Number, Record Length) @@ -449,9 +407,7 @@ def read_file_record( """ return self.execute(pdu_file_msg.ReadFileRecordRequest(records, **kwargs)) - def write_file_record( - self, records: list[tuple], **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def write_file_record(self, records: list[tuple], **kwargs: Any) -> T: """Write file record (code 0x15). :param records: List of (Reference type, File number, Record Number, Record Length) @@ -466,7 +422,7 @@ def mask_write_register( and_mask: int = 0xFFFF, or_mask: int = 0x0000, **kwargs: Any, - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Mask write register (code 0x16). :param address: The mask pointer address (0x0000 to 0xffff) @@ -487,7 +443,7 @@ def readwrite_registers( values: list[int] | int = 0, slave: int = 0, **kwargs, - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Read/Write registers (code 0x17). :param read_address: The address to start reading from @@ -509,9 +465,7 @@ def readwrite_registers( ) ) - def read_fifo_queue( - self, address: int = 0x0000, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + def read_fifo_queue(self, address: int = 0x0000, **kwargs: Any) -> T: """Read FIFO queue (code 0x18). :param address: The address to start reading from @@ -524,7 +478,7 @@ def read_fifo_queue( def read_device_information( self, read_code: int | None = None, object_id: int = 0x00, **kwargs: Any - ) -> ModbusResponse | Awaitable[ModbusResponse]: + ) -> T: """Read FIFO queue (code 0x2B sub 0x0E). :param read_code: The device information read code diff --git a/test/sub_client/test_client.py b/test/sub_client/test_client.py index 334c5357b..33842bc4e 100755 --- a/test/sub_client/test_client.py +++ b/test/sub_client/test_client.py @@ -599,4 +599,5 @@ async def test_client_build_response(): async def test_client_mixin_execute(): """Test dummy execute.""" client = ModbusClientMixin() - assert client.execute(None) + with pytest.raises(NotImplementedError): + client.execute(None) From ba736743de76fe6a8a7f4debc0e6efb9a5bcd465 Mon Sep 17 00:00:00 2001 From: Laurin Schmidt Date: Thu, 8 Feb 2024 15:10:27 +0100 Subject: [PATCH 2/7] fix unused typevar --- pymodbus/client/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 0833bf387..cc082ae39 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -4,7 +4,7 @@ import asyncio import socket from dataclasses import dataclass -from typing import Any, Awaitable, Callable, Type, cast, TypeVar, Generic +from typing import Any, Awaitable, Callable, Type, cast from pymodbus.client.mixin import ModbusClientMixin from pymodbus.exceptions import ConnectionException, ModbusIOException From aee85b813399372fc36b8cb0ad27b3424631f40e Mon Sep 17 00:00:00 2001 From: Laurin Schmidt Date: Thu, 8 Feb 2024 15:20:53 +0100 Subject: [PATCH 3/7] remove mixin init, ignore async bytes return --- pymodbus/client/base.py | 4 +--- pymodbus/client/mixin.py | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index cc082ae39..c0b3a005b 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -60,7 +60,6 @@ def __init__( **kwargs: Any, ) -> None: """Initialize a client instance.""" - ModbusClientMixin.__init__(self) ModbusProtocol.__init__( self, CommParams( @@ -176,7 +175,7 @@ async def async_execute(self, request=None) -> ModbusResponse: f"ERROR: No response received after {self.retries} retries" ) - return resp + return resp # type: ignore def callback_data(self, data: bytes, addr: tuple | None = None) -> int: """Handle received data. @@ -319,7 +318,6 @@ def __init__( **kwargs: Any, ) -> None: """Initialize a client instance.""" - ModbusClientMixin.__init__(self) ModbusProtocol.__init__( self, CommParams( diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index 97f821b8f..947755d40 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -46,9 +46,6 @@ class ModbusClientMixin(Generic[T]): # pylint: disable=too-many-public-methods with await (asynchronous) depending on the client used. """ - def __init__(self): - """Initialize.""" - def execute(self, _request: ModbusRequest) -> T: """Execute request (code ???). From c89124d974dea006ae81086a3bd0f8cee73b5d8f Mon Sep 17 00:00:00 2001 From: Laurin Schmidt Date: Thu, 8 Feb 2024 15:48:01 +0100 Subject: [PATCH 4/7] more type error fix attempts --- pymodbus/client/base.py | 2 +- pymodbus/framer/base.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index c0b3a005b..833fef6e0 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -175,7 +175,7 @@ async def async_execute(self, request=None) -> ModbusResponse: f"ERROR: No response received after {self.retries} retries" ) - return resp # type: ignore + return resp # type: ignore[return-value] def callback_data(self, data: bytes, addr: tuple | None = None) -> int: """Handle received data. diff --git a/pymodbus/framer/base.py b/pymodbus/framer/base.py index d27eb354b..1c9b91f0a 100644 --- a/pymodbus/framer/base.py +++ b/pymodbus/framer/base.py @@ -142,3 +142,9 @@ def frameProcessIncomingPacket( self, _single, _callback, _slave, _tid=None, **kwargs ): """Process new packet pattern.""" + + def buildPacket(self, message): + """Create a ready to send modbus packet. + + :param message: The populated request/response to send + """ From c7dd2fbba4036e94b9a011df4ce73dedd31fab93 Mon Sep 17 00:00:00 2001 From: Laurin Schmidt Date: Thu, 8 Feb 2024 16:22:11 +0100 Subject: [PATCH 5/7] add back mixin init --- pymodbus/client/base.py | 7 +++++-- pymodbus/client/mixin.py | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 833fef6e0..e0772cf5c 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -60,6 +60,7 @@ def __init__( **kwargs: Any, ) -> None: """Initialize a client instance.""" + ModbusClientMixin.__init__(self) # type: ignore[arg-type] ModbusProtocol.__init__( self, CommParams( @@ -175,7 +176,7 @@ async def async_execute(self, request=None) -> ModbusResponse: f"ERROR: No response received after {self.retries} retries" ) - return resp # type: ignore[return-value] + return resp # type: ignore[return-value] def callback_data(self, data: bytes, addr: tuple | None = None) -> int: """Handle received data. @@ -265,6 +266,7 @@ def __str__(self): f"{self.__class__.__name__} {self.comm_params.host}:{self.comm_params.port}" ) + class ModbusBaseSyncClient(ModbusClientMixin[ModbusResponse], ModbusProtocol): """**ModbusBaseClient**. @@ -318,6 +320,7 @@ def __init__( **kwargs: Any, ) -> None: """Initialize a client instance.""" + ModbusClientMixin.__init__(self) # type: ignore[arg-type] ModbusProtocol.__init__( self, CommParams( @@ -335,7 +338,7 @@ def __init__( parity=kwargs.get("parity", None), stopbits=kwargs.get("stopbits", None), handle_local_echo=kwargs.get("handle_local_echo", False), - on_reconnect_callback = on_reconnect_callback, + on_reconnect_callback=on_reconnect_callback, ), False, ) diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index 947755d40..97f821b8f 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -46,6 +46,9 @@ class ModbusClientMixin(Generic[T]): # pylint: disable=too-many-public-methods with await (asynchronous) depending on the client used. """ + def __init__(self): + """Initialize.""" + def execute(self, _request: ModbusRequest) -> T: """Execute request (code ???). From 264444c3801870cf7979251a95df15e4e3ce1678 Mon Sep 17 00:00:00 2001 From: Alex Ruddick Date: Thu, 8 Feb 2024 23:28:55 -0600 Subject: [PATCH 6/7] more thorough mixin test --- test/sub_client/test_client.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/sub_client/test_client.py b/test/sub_client/test_client.py index 33842bc4e..a75ba1bc7 100755 --- a/test/sub_client/test_client.py +++ b/test/sub_client/test_client.py @@ -20,6 +20,7 @@ from pymodbus.datastore import ModbusSlaveContext from pymodbus.datastore.store import ModbusSequentialDataBlock from pymodbus.exceptions import ConnectionException, ModbusException, ModbusIOException +from pymodbus.pdu import ModbusRequest from pymodbus.transport import CommType @@ -597,7 +598,9 @@ async def test_client_build_response(): await client.build_response(0) async def test_client_mixin_execute(): - """Test dummy execute.""" + """Test dummy execute for both sync and async.""" client = ModbusClientMixin() with pytest.raises(NotImplementedError): - client.execute(None) + client.execute(ModbusRequest()) + with pytest.raises(NotImplementedError): + await client.execute(ModbusRequest()) From 6222e0702a2a87d5d166bd004b7ac4d24b1dd4e1 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Fri, 9 Feb 2024 10:02:59 +0100 Subject: [PATCH 7/7] Update mixin.py --- pymodbus/client/mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index 700ec8cb2..f686761aa 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -160,7 +160,7 @@ def read_exception_status(self, slave: int = 0, **kwargs: Any) -> T: """ return self.execute(pdu_other_msg.ReadExceptionStatusRequest(slave, **kwargs)) - + def diag_query_data( self, msg: bytes, slave: int = 0, **kwargs: Any ) -> T: