From f592d56aab0ced6eb5f7141008a041e7a3d74f57 Mon Sep 17 00:00:00 2001 From: Mate Szabo Date: Tue, 15 Oct 2024 15:54:52 +0200 Subject: [PATCH 1/4] Add no_response_expected parameter to API calls. --- doc/source/client.rst | 9 +- pymodbus/client/base.py | 5 +- pymodbus/client/mixin.py | 235 +++++++++++++++++++-------- pymodbus/transaction.py | 10 +- test/sub_client/test_client.py | 8 +- test/sub_current/test_transaction.py | 4 +- 6 files changed, 181 insertions(+), 90 deletions(-) diff --git a/doc/source/client.rst b/doc/source/client.rst index 4343def75..e57057c18 100644 --- a/doc/source/client.rst +++ b/doc/source/client.rst @@ -199,14 +199,9 @@ The logical devices represented by the device is addressed with the :mod:`slave= With **Serial**, the comm port is defined when creating the object. The physical devices are addressed with the :mod:`slave=` parameter. -:mod:`slave=0` is used as broadcast in order to address all devices. -However experience shows that modern devices do not allow broadcast, mostly because it is -inheriently dangerous. With :mod:`slave=0` the application can get upto 254 responses on a single request, -and this is not handled with the normal API calls! - -The simple request calls (mixin) do NOT support broadcast, if an application wants to use broadcast -it must call :mod:`client.execute` and deal with the responses. +:mod:`slave=0` is used as broadcast (in accordance with the modbus standard). To accommodate non-standard behaviour of devices, all request calls to :mod:`slave=0` will expect and wait for one response. If multiple devices on the bus are expected to respond to broadcast requests (for example for the purpose of device detection), the application can get upto 254 responses on a single request, and this is not handled with the normal API calls. If an application is expecting multiple responses to a broadcast request, it must call :mod:`client.execute` and deal with the responses. +If no response is expected to a request, the :mod:`no_response_expected=True` argument can be used in the normal API calls. However, these requests will not return indication of failure or success, and will not respect the turnaround delay after sending a broadcast message, therefore, it is the application's responsibility to allow time for the slaves to process the broadcast request before the next request is made on the bus. Client response handling diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index ee3b18252..3fdd13138 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -96,7 +96,7 @@ def execute(self, request: ModbusPDU): raise ConnectionException(f"Not connected[{self!s}]") return self.async_execute(request) - async def async_execute(self, request) -> ModbusPDU: + async def async_execute(self, request) -> ModbusPDU | None: """Execute requests asynchronously. :meta private: @@ -109,6 +109,9 @@ async def async_execute(self, request) -> ModbusPDU: async with self._lock: req = self.build_response(request) self.ctx.send(packet) + if request.no_response_expected: + resp = None + break try: resp = await asyncio.wait_for( req, timeout=self.ctx.comm_params.timeout_connect diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index 784153543..cebd41aaa 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -61,305 +61,382 @@ def execute(self, _request: ModbusPDU) -> T: """ raise NotImplementedError("execute of ModbusClientMixin needs to be overridden") - def read_coils(self, address: int, count: int = 1, slave: int = 1) -> T: + def read_coils(self, address: int, count: int = 1, slave: int = 1, no_response_expected: bool = False) -> T: """Read coils (code 0x01). :param address: Start address to read from :param count: (optional) Number of coils to read :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_bit_read.ReadCoilsRequest(address, count, slave=slave)) + return self.execute(pdu_bit_read.ReadCoilsRequest(address, + count, + slave=slave, + no_response_expected=no_response_expected)) - def read_discrete_inputs(self, address: int, count: int = 1, slave: int = 1) -> T: + def read_discrete_inputs(self, + address: int, + count: int = 1, + slave: int = 1, + no_response_expected: bool = False) -> T: """Read discrete inputs (code 0x02). :param address: Start address to read from :param count: (optional) Number of coils to read :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_bit_read.ReadDiscreteInputsRequest(address, count, slave=slave)) + return self.execute(pdu_bit_read.ReadDiscreteInputsRequest(address, + count, + slave=slave, + no_response_expected=no_response_expected)) - def read_holding_registers(self, address: int, count: int = 1, slave: int = 1) -> T: + def read_holding_registers(self, + address: int, + count: int = 1, + slave: int = 1, + no_response_expected: bool = False) -> T: """Read holding registers (code 0x03). :param address: Start address to read from :param count: (optional) Number of coils to read :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_reg_read.ReadHoldingRegistersRequest(address, count, slave=slave)) + return self.execute(pdu_reg_read.ReadHoldingRegistersRequest(address, + count, + slave=slave, + no_response_expected=no_response_expected)) - def read_input_registers(self, address: int, count: int = 1, slave: int = 1) -> T: + def read_input_registers(self, + address: int, + count: int = 1, + slave: int = 1, + no_response_expected: bool = False) -> T: """Read input registers (code 0x04). :param address: Start address to read from :param count: (optional) Number of coils to read :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_reg_read.ReadInputRegistersRequest(address, count, slave=slave)) + return self.execute(pdu_reg_read.ReadInputRegistersRequest(address, + count, + slave=slave, + no_response_expected=no_response_expected)) - def write_coil(self, address: int, value: bool, slave: int = 1) -> T: + def write_coil(self, address: int, value: bool, slave: int = 1, no_response_expected: bool = False) -> T: """Write single coil (code 0x05). :param address: Address to write to :param value: Boolean to write :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_bit_write.WriteSingleCoilRequest(address, value, slave=slave)) + return self.execute(pdu_bit_write.WriteSingleCoilRequest(address, + value, + slave=slave, + no_response_expected=no_response_expected)) - def write_register(self, address: int, value: bytes | int, slave: int = 1) -> T: + def write_register(self, address: int, value: bytes | int, slave: int = 1, no_response_expected: bool = False) -> T: """Write register (code 0x06). :param address: Address to write to :param value: Value to write :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_req_write.WriteSingleRegisterRequest(address, value, slave=slave)) + return self.execute(pdu_req_write.WriteSingleRegisterRequest(address, + value, + slave=slave, + no_response_expected=no_response_expected)) - def read_exception_status(self, slave: int = 1) -> T: + def read_exception_status(self, slave: int = 1, no_response_expected: bool = False) -> T: """Read Exception Status (code 0x07). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.ReadExceptionStatusRequest(slave=slave)) + return self.execute(pdu_other_msg.ReadExceptionStatusRequest(slave=slave, + no_response_expected=no_response_expected)) - - def diag_query_data( - self, msg: bytes, slave: int = 1) -> T: + def diag_query_data(self, msg: bytes, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose query data (code 0x08 sub 0x00). :param msg: Message to be returned :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ReturnQueryDataRequest(msg, slave=slave)) + return self.execute(pdu_diag.ReturnQueryDataRequest(msg, + slave=slave, + no_response_expected=no_response_expected)) - def diag_restart_communication( - self, toggle: bool, slave: int = 1) -> T: + def diag_restart_communication(self, toggle: bool, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose restart communication (code 0x08 sub 0x01). :param toggle: True if toggled. :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.RestartCommunicationsOptionRequest(toggle, slave=slave) + pdu_diag.RestartCommunicationsOptionRequest(toggle, slave=slave, no_response_expected=no_response_expected) ) - def diag_read_diagnostic_register(self, slave: int = 1) -> T: + def diag_read_diagnostic_register(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read diagnostic register (code 0x08 sub 0x02). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnDiagnosticRegisterRequest(slave=slave) + pdu_diag.ReturnDiagnosticRegisterRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_change_ascii_input_delimeter(self, slave: int = 1) -> T: + def diag_change_ascii_input_delimeter(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose change ASCII input delimiter (code 0x08 sub 0x03). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ChangeAsciiInputDelimiterRequest(slave=slave) + pdu_diag.ChangeAsciiInputDelimiterRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_force_listen_only(self, slave: int = 1) -> T: + def diag_force_listen_only(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose force listen only (code 0x08 sub 0x04). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ForceListenOnlyModeRequest(slave=slave)) + return self.execute(pdu_diag.ForceListenOnlyModeRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_clear_counters(self, slave: int = 1) -> T: + def diag_clear_counters(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose clear counters (code 0x08 sub 0x0A). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ClearCountersRequest(slave=slave)) + return self.execute(pdu_diag.ClearCountersRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_read_bus_message_count(self, slave: int = 1) -> T: + def diag_read_bus_message_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read bus message count (code 0x08 sub 0x0B). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnBusMessageCountRequest(slave=slave) + pdu_diag.ReturnBusMessageCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_read_bus_comm_error_count(self, slave: int = 1) -> T: + def diag_read_bus_comm_error_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Bus Communication Error Count (code 0x08 sub 0x0C). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnBusCommunicationErrorCountRequest(slave=slave) + pdu_diag.ReturnBusCommunicationErrorCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_read_bus_exception_error_count(self, slave: int = 1) -> T: + def diag_read_bus_exception_error_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Bus Exception Error Count (code 0x08 sub 0x0D). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnBusExceptionErrorCountRequest(slave=slave) + pdu_diag.ReturnBusExceptionErrorCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_read_slave_message_count(self, slave: int = 1) -> T: + def diag_read_slave_message_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave Message Count (code 0x08 sub 0x0E). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnSlaveMessageCountRequest(slave=slave) + pdu_diag.ReturnSlaveMessageCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_read_slave_no_response_count(self, slave: int = 1) -> T: + def diag_read_slave_no_response_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave No Response Count (code 0x08 sub 0x0F). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnSlaveNoResponseCountRequest(slave=slave) + pdu_diag.ReturnSlaveNoResponseCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_read_slave_nak_count(self, slave: int = 1) -> T: + def diag_read_slave_nak_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave NAK Count (code 0x08 sub 0x10). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ReturnSlaveNAKCountRequest(slave=slave)) + return self.execute(pdu_diag.ReturnSlaveNAKCountRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_read_slave_busy_count(self, slave: int = 1) -> T: + def diag_read_slave_busy_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave Busy Count (code 0x08 sub 0x11). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ReturnSlaveBusyCountRequest(slave=slave)) + return self.execute(pdu_diag.ReturnSlaveBusyCountRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_read_bus_char_overrun_count(self, slave: int = 1) -> T: + def diag_read_bus_char_overrun_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Bus Character Overrun Count (code 0x08 sub 0x12). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnSlaveBusCharacterOverrunCountRequest(slave=slave) + pdu_diag.ReturnSlaveBusCharacterOverrunCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_read_iop_overrun_count(self, slave: int = 1) -> T: + def diag_read_iop_overrun_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Iop overrun count (code 0x08 sub 0x13). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_diag.ReturnIopOverrunCountRequest(slave=slave) + pdu_diag.ReturnIopOverrunCountRequest(slave=slave, no_response_expected=no_response_expected) ) - def diag_clear_overrun_counter(self, slave: int = 1) -> T: + def diag_clear_overrun_counter(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose Clear Overrun Counter and Flag (code 0x08 sub 0x14). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ClearOverrunCountRequest(slave=slave)) + return self.execute(pdu_diag.ClearOverrunCountRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_getclear_modbus_response(self, slave: int = 1) -> T: + def diag_getclear_modbus_response(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose Get/Clear modbus plus (code 0x08 sub 0x15). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.GetClearModbusPlusRequest(slave=slave)) + return self.execute(pdu_diag.GetClearModbusPlusRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_get_comm_event_counter(self, slave: int = 1) -> T: + def diag_get_comm_event_counter(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose get event counter (code 0x0B). + :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.GetCommEventCounterRequest(slave=slave)) + return self.execute(pdu_other_msg.GetCommEventCounterRequest(slave=slave, no_response_expected=no_response_expected)) - def diag_get_comm_event_log(self, slave: int = 1) -> T: + def diag_get_comm_event_log(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose get event counter (code 0x0C). + :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.GetCommEventLogRequest(slave=slave)) + return self.execute(pdu_other_msg.GetCommEventLogRequest(slave=slave, no_response_expected=no_response_expected)) def write_coils( self, address: int, values: list[bool] | bool, slave: int = 1, + no_response_expected: bool = False ) -> T: """Write coils (code 0x0F). :param address: Start address to write to :param values: List of booleans to write, or a single boolean to write :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_bit_write.WriteMultipleCoilsRequest(address, values, slave) + pdu_bit_write.WriteMultipleCoilsRequest(address, values, slave, no_response_expected=no_response_expected) ) def write_registers( - self, address: int, values: list[bytes | int], slave: int = 1, skip_encode: bool = False) -> T: + self, + address: int, + values: list[bytes | int], + slave: int = 1, + skip_encode: bool = False, + no_response_expected: bool = False + ) -> T: """Write registers (code 0x10). :param address: Start address to write to :param values: List of values to write :param slave: (optional) Modbus slave ID :param skip_encode: (optional) do not encode values + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_req_write.WriteMultipleRegistersRequest(address, values, slave=slave, skip_encode=skip_encode) + pdu_req_write.WriteMultipleRegistersRequest(address, + values, + slave=slave, + skip_encode=skip_encode, + no_response_expected=no_response_expected) ) - def report_slave_id(self, slave: int = 1) -> T: + def report_slave_id(self, slave: int = 1, no_response_expected: bool = False) -> T: """Report slave ID (code 0x11). :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.ReportSlaveIdRequest(slave=slave)) + return self.execute(pdu_other_msg.ReportSlaveIdRequest(slave=slave, no_response_expected=no_response_expected)) - def read_file_record(self, records: list[tuple], slave: int = 1) -> T: + def read_file_record(self, records: list[tuple], slave: int = 1, no_response_expected: bool = False) -> T: """Read file record (code 0x14). :param records: List of (Reference type, File number, Record Number, Record Length) :param slave: device id + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_file_msg.ReadFileRecordRequest(records, slave=slave)) + return self.execute(pdu_file_msg.ReadFileRecordRequest(records, slave=slave, no_response_expected=no_response_expected)) - def write_file_record(self, records: list[tuple], slave: int = 1) -> T: + def write_file_record(self, records: list[tuple], slave: int = 1, no_response_expected: bool = False) -> T: """Write file record (code 0x15). :param records: List of (Reference type, File number, Record Number, Record Length) :param slave: (optional) Device id + :param no_response_expected: (optional) The client will not expect a response to the request + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_file_msg.WriteFileRecordRequest(records, slave=slave)) + return self.execute(pdu_file_msg.WriteFileRecordRequest(records, + slave=slave, + no_response_expected=no_response_expected)) def mask_write_register( self, @@ -367,6 +444,7 @@ def mask_write_register( and_mask: int = 0xFFFF, or_mask: int = 0x0000, slave: int = 1, + no_response_expected: bool = False ) -> T: """Mask write register (code 0x16). @@ -374,10 +452,15 @@ def mask_write_register( :param and_mask: The and bitmask to apply to the register address :param or_mask: The or bitmask to apply to the register address :param slave: (optional) device id + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_req_write.MaskWriteRegisterRequest(address, and_mask, or_mask, slave=slave) + pdu_req_write.MaskWriteRegisterRequest(address, + and_mask, + or_mask, + slave=slave, + no_response_expected=no_response_expected) ) def readwrite_registers( @@ -388,6 +471,7 @@ def readwrite_registers( address: int | None = None, values: list[int] | int = 0, slave: int = 1, + no_response_expected: bool = False ) -> T: """Read/Write registers (code 0x17). @@ -397,6 +481,7 @@ def readwrite_registers( :param address: (optional) use as read/write address :param values: List of values to write, or a single value to write :param slave: (optional) Modbus slave ID + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ if address: @@ -409,31 +494,41 @@ def readwrite_registers( write_address=write_address, write_registers=values, slave=slave, + no_response_expected=no_response_expected ) ) - def read_fifo_queue(self, address: int = 0x0000, slave: int = 1) -> T: + def read_fifo_queue(self, address: int = 0x0000, slave: int = 1, no_response_expected: bool = False) -> T: """Read FIFO queue (code 0x18). :param address: The address to start reading from :param slave: (optional) device id + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_file_msg.ReadFifoQueueRequest(address, slave=slave)) + return self.execute(pdu_file_msg.ReadFifoQueueRequest(address, + slave=slave, + no_response_expected=no_response_expected)) # code 0x2B sub 0x0D: CANopen General Reference Request and Response, NOT IMPLEMENTED - def read_device_information( - self, read_code: int | None = None, object_id: int = 0x00, slave: int = 1) -> T: + def read_device_information(self, read_code: int | None = None, + object_id: int = 0x00, + slave: int = 1, + no_response_expected: bool = False) -> T: """Read FIFO queue (code 0x2B sub 0x0E). :param read_code: The device information read code :param object_id: The object to read from :param slave: (optional) Device id + :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ return self.execute( - pdu_mei.ReadDeviceInformationRequest(read_code, object_id, slave=slave) + pdu_mei.ReadDeviceInformationRequest(read_code, + object_id, + slave=slave, + no_response_expected=no_response_expected) ) # ------------------ diff --git a/pymodbus/transaction.py b/pymodbus/transaction.py index f03a5ce26..3524a86e4 100644 --- a/pymodbus/transaction.py +++ b/pymodbus/transaction.py @@ -193,7 +193,6 @@ def execute(self, request: ModbusPDU): # noqa: C901 ): Log.debug("Clearing current Frame: - {}", _buffer) self.client.framer.databuffer = b'' - broadcast = not request.slave_id expected_response_length = None if not isinstance(self.client.framer, FramerSocket): response_pdu_size = request.get_response_pdu_size() @@ -217,8 +216,9 @@ def execute(self, request: ModbusPDU): # noqa: C901 request, expected_response_length, full=full, - broadcast=broadcast, ) + if request.no_response_expected is True: + return "Broadcast write sent - no response expected" while retries > 0: if self._validate_response(response): if ( @@ -280,14 +280,12 @@ 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: ModbusPDU, response_length, full=False, broadcast=False): + def _transact(self, request: ModbusPDU, response_length, full=False): """Do a Write and Read transaction. - :param packet: packet to be sent :param response_length: Expected response length :param full: the target device was notorious for its no response. Dont waste time this time by partial querying - :param broadcast: :return: response """ last_exception = None @@ -309,7 +307,7 @@ def _transact(self, request: ModbusPDU, response_length, full=False, broadcast=F if self.client.comm_params.handle_local_echo is True: if self._recv(size, full) != packet: return b"", "Wrong local echo" - if broadcast: + if request.no_response_expected is True: if size: Log.debug( 'Changing transaction state from "SENDING" ' diff --git a/test/sub_client/test_client.py b/test/sub_client/test_client.py index 9871b2a85..8dd54fe98 100755 --- a/test/sub_client/test_client.py +++ b/test/sub_client/test_client.py @@ -241,7 +241,7 @@ async def test_client_instanciate( client.connect = lambda: False client.transport = None with pytest.raises(ConnectionException): - client.execute(ModbusPDU(0, 0, False)) + client.execute(ModbusPDU(0, 0, False, False)) async def test_client_modbusbaseclient(): """Test modbus base client class.""" @@ -678,13 +678,13 @@ async def test_client_build_response(): comm_params=CommParams(), ) with pytest.raises(ConnectionException): - await client.build_response(ModbusPDU(0, 0, False)) + await client.build_response(ModbusPDU(0, 0, False, False)) async def test_client_mixin_execute(): """Test dummy execute for both sync and async.""" client = ModbusClientMixin() with pytest.raises(NotImplementedError): - client.execute(ModbusPDU(0, 0, False)) + client.execute(ModbusPDU(0, 0, False, False)) with pytest.raises(NotImplementedError): - await client.execute(ModbusPDU(0, 0, False)) + await client.execute(ModbusPDU(0, 0, False, False)) diff --git a/test/sub_current/test_transaction.py b/test/sub_current/test_transaction.py index 25fdac103..2edde218d 100755 --- a/test/sub_current/test_transaction.py +++ b/test/sub_current/test_transaction.py @@ -166,7 +166,7 @@ def test_get_transaction_manager_transaction(self): """Test the getting a transaction from the transaction manager.""" self._manager.reset() handle = ModbusPDU( - 0, self._manager.getNextTID(), False + 0, self._manager.getNextTID(), False, False ) self._manager.addTransaction(handle) result = self._manager.getTransaction(handle.transaction_id) @@ -176,7 +176,7 @@ def test_delete_transaction_manager_transaction(self): """Test deleting a transaction from the dict transaction manager.""" self._manager.reset() handle = ModbusPDU( - 0, self._manager.getNextTID(), False + 0, self._manager.getNextTID(), False, False ) self._manager.addTransaction(handle) self._manager.delTransaction(handle.transaction_id) From ac5115cf9343375bab309d2a92c16294fb3b344a Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sun, 20 Oct 2024 14:09:20 +0200 Subject: [PATCH 2/4] Simplify use of parameter. --- examples/client_custom_msg.py | 4 +- pymodbus/client/base.py | 17 ++-- pymodbus/client/mixin.py | 145 +++++++-------------------- pymodbus/transaction.py | 23 ++--- test/sub_client/test_client.py | 18 ++-- test/sub_current/test_transaction.py | 20 ++-- 6 files changed, 71 insertions(+), 156 deletions(-) diff --git a/examples/client_custom_msg.py b/examples/client_custom_msg.py index ab64e7109..c28f5cf01 100755 --- a/examples/client_custom_msg.py +++ b/examples/client_custom_msg.py @@ -128,12 +128,12 @@ async def main(host="localhost", port=5020): client.register(CustomModbusPDU) slave=1 request = CustomRequest(32, slave=slave) - result = await client.execute(request) + result = await client.execute(False, request) print(result) # inherited request request = Read16CoilsRequest(32, slave) - result = await client.execute(request) + result = await client.execute(False, request) print(result) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 3fdd13138..884d8f017 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -83,20 +83,16 @@ def close(self) -> None: """Close connection.""" self.ctx.close() - def execute(self, request: ModbusPDU): + def execute(self, no_response_expected: bool, request: ModbusPDU): """Execute request and get response (call **sync/async**). - :param request: The request to process - :returns: The result of the request execution - :raises ConnectionException: Check exception text. - :meta private: """ if not self.ctx.transport: raise ConnectionException(f"Not connected[{self!s}]") - return self.async_execute(request) + return self.async_execute(no_response_expected, request) - async def async_execute(self, request) -> ModbusPDU | None: + async def async_execute(self, no_response_expected: bool, request) -> ModbusPDU | None: """Execute requests asynchronously. :meta private: @@ -109,7 +105,7 @@ async def async_execute(self, request) -> ModbusPDU | None: async with self._lock: req = self.build_response(request) self.ctx.send(packet) - if request.no_response_expected: + if no_response_expected: resp = None break try: @@ -228,9 +224,10 @@ def idle_time(self) -> float: return 0 return self.last_frame_end + self.silent_interval - def execute(self, request: ModbusPDU) -> ModbusPDU: + def execute(self, no_response_expected: bool, request: ModbusPDU) -> ModbusPDU: """Execute request and get response (call **sync/async**). + :param no_response_expected: The client will not expect a response to the request :param request: The request to process :returns: The result of the request execution :raises ConnectionException: Check exception text. @@ -239,7 +236,7 @@ def execute(self, request: ModbusPDU) -> ModbusPDU: """ if not self.connect(): raise ConnectionException(f"Failed to connect[{self!s}]") - return self.transaction.execute(request) + return self.transaction.execute(no_response_expected, request) # ----------------------------------------------------------------------- # # Internal methods diff --git a/pymodbus/client/mixin.py b/pymodbus/client/mixin.py index cebd41aaa..b31e1ef87 100644 --- a/pymodbus/client/mixin.py +++ b/pymodbus/client/mixin.py @@ -49,7 +49,7 @@ class ModbusClientMixin(Generic[T]): # pylint: disable=too-many-public-methods def __init__(self): """Initialize.""" - def execute(self, _request: ModbusPDU) -> T: + def execute(self, _no_response_expected: bool, _request: ModbusPDU,) -> T: """Execute request (code ???). :raises ModbusException: @@ -70,10 +70,7 @@ def read_coils(self, address: int, count: int = 1, slave: int = 1, no_response_e :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_bit_read.ReadCoilsRequest(address, - count, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_bit_read.ReadCoilsRequest(address=address, count=count, slave=slave)) def read_discrete_inputs(self, address: int, @@ -88,10 +85,7 @@ def read_discrete_inputs(self, :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_bit_read.ReadDiscreteInputsRequest(address, - count, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_bit_read.ReadDiscreteInputsRequest(address=address, count=count, slave=slave, )) def read_holding_registers(self, address: int, @@ -106,10 +100,7 @@ def read_holding_registers(self, :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_reg_read.ReadHoldingRegistersRequest(address, - count, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_reg_read.ReadHoldingRegistersRequest(address=address, count=count, slave=slave)) def read_input_registers(self, address: int, @@ -124,10 +115,7 @@ def read_input_registers(self, :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_reg_read.ReadInputRegistersRequest(address, - count, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_reg_read.ReadInputRegistersRequest(address, count, slave=slave)) def write_coil(self, address: int, value: bool, slave: int = 1, no_response_expected: bool = False) -> T: """Write single coil (code 0x05). @@ -138,10 +126,7 @@ def write_coil(self, address: int, value: bool, slave: int = 1, no_response_expe :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_bit_write.WriteSingleCoilRequest(address, - value, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_bit_write.WriteSingleCoilRequest(address, value, slave=slave)) def write_register(self, address: int, value: bytes | int, slave: int = 1, no_response_expected: bool = False) -> T: """Write register (code 0x06). @@ -152,10 +137,7 @@ def write_register(self, address: int, value: bytes | int, slave: int = 1, no_re :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_req_write.WriteSingleRegisterRequest(address, - value, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_req_write.WriteSingleRegisterRequest(address, value, slave=slave)) def read_exception_status(self, slave: int = 1, no_response_expected: bool = False) -> T: """Read Exception Status (code 0x07). @@ -164,8 +146,7 @@ def read_exception_status(self, slave: int = 1, no_response_expected: bool = Fal :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.ReadExceptionStatusRequest(slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_other_msg.ReadExceptionStatusRequest(slave=slave)) def diag_query_data(self, msg: bytes, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose query data (code 0x08 sub 0x00). @@ -175,9 +156,7 @@ def diag_query_data(self, msg: bytes, slave: int = 1, no_response_expected: bool :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ReturnQueryDataRequest(msg, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.ReturnQueryDataRequest(msg, slave=slave)) def diag_restart_communication(self, toggle: bool, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose restart communication (code 0x08 sub 0x01). @@ -187,9 +166,7 @@ def diag_restart_communication(self, toggle: bool, slave: int = 1, no_response_e :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.RestartCommunicationsOptionRequest(toggle, slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.RestartCommunicationsOptionRequest(toggle, slave=slave)) def diag_read_diagnostic_register(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read diagnostic register (code 0x08 sub 0x02). @@ -198,9 +175,7 @@ def diag_read_diagnostic_register(self, slave: int = 1, no_response_expected: bo :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnDiagnosticRegisterRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnDiagnosticRegisterRequest(slave=slave)) def diag_change_ascii_input_delimeter(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose change ASCII input delimiter (code 0x08 sub 0x03). @@ -209,9 +184,7 @@ def diag_change_ascii_input_delimeter(self, slave: int = 1, no_response_expected :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ChangeAsciiInputDelimiterRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ChangeAsciiInputDelimiterRequest(slave=slave)) def diag_force_listen_only(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose force listen only (code 0x08 sub 0x04). @@ -220,7 +193,7 @@ def diag_force_listen_only(self, slave: int = 1, no_response_expected: bool = Fa :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ForceListenOnlyModeRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.ForceListenOnlyModeRequest(slave=slave)) def diag_clear_counters(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose clear counters (code 0x08 sub 0x0A). @@ -229,7 +202,7 @@ def diag_clear_counters(self, slave: int = 1, no_response_expected: bool = False :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ClearCountersRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.ClearCountersRequest(slave=slave)) def diag_read_bus_message_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read bus message count (code 0x08 sub 0x0B). @@ -238,9 +211,7 @@ def diag_read_bus_message_count(self, slave: int = 1, no_response_expected: bool :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnBusMessageCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnBusMessageCountRequest(slave=slave)) def diag_read_bus_comm_error_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Bus Communication Error Count (code 0x08 sub 0x0C). @@ -249,9 +220,7 @@ def diag_read_bus_comm_error_count(self, slave: int = 1, no_response_expected: b :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnBusCommunicationErrorCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnBusCommunicationErrorCountRequest(slave=slave)) def diag_read_bus_exception_error_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Bus Exception Error Count (code 0x08 sub 0x0D). @@ -260,9 +229,7 @@ def diag_read_bus_exception_error_count(self, slave: int = 1, no_response_expect :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnBusExceptionErrorCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnBusExceptionErrorCountRequest(slave=slave)) def diag_read_slave_message_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave Message Count (code 0x08 sub 0x0E). @@ -271,9 +238,7 @@ def diag_read_slave_message_count(self, slave: int = 1, no_response_expected: bo :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnSlaveMessageCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnSlaveMessageCountRequest(slave=slave)) def diag_read_slave_no_response_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave No Response Count (code 0x08 sub 0x0F). @@ -282,9 +247,7 @@ def diag_read_slave_no_response_count(self, slave: int = 1, no_response_expected :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnSlaveNoResponseCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnSlaveNoResponseCountRequest(slave=slave)) def diag_read_slave_nak_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave NAK Count (code 0x08 sub 0x10). @@ -293,7 +256,7 @@ def diag_read_slave_nak_count(self, slave: int = 1, no_response_expected: bool = :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ReturnSlaveNAKCountRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.ReturnSlaveNAKCountRequest(slave=slave)) def diag_read_slave_busy_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Slave Busy Count (code 0x08 sub 0x11). @@ -302,7 +265,7 @@ def diag_read_slave_busy_count(self, slave: int = 1, no_response_expected: bool :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ReturnSlaveBusyCountRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.ReturnSlaveBusyCountRequest(slave=slave)) def diag_read_bus_char_overrun_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Bus Character Overrun Count (code 0x08 sub 0x12). @@ -311,9 +274,7 @@ def diag_read_bus_char_overrun_count(self, slave: int = 1, no_response_expected: :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnSlaveBusCharacterOverrunCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnSlaveBusCharacterOverrunCountRequest(slave=slave)) def diag_read_iop_overrun_count(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose read Iop overrun count (code 0x08 sub 0x13). @@ -322,9 +283,7 @@ def diag_read_iop_overrun_count(self, slave: int = 1, no_response_expected: bool :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_diag.ReturnIopOverrunCountRequest(slave=slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_diag.ReturnIopOverrunCountRequest(slave=slave)) def diag_clear_overrun_counter(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose Clear Overrun Counter and Flag (code 0x08 sub 0x14). @@ -333,7 +292,7 @@ def diag_clear_overrun_counter(self, slave: int = 1, no_response_expected: bool :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.ClearOverrunCountRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.ClearOverrunCountRequest(slave=slave)) def diag_getclear_modbus_response(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose Get/Clear modbus plus (code 0x08 sub 0x15). @@ -342,7 +301,7 @@ def diag_getclear_modbus_response(self, slave: int = 1, no_response_expected: bo :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_diag.GetClearModbusPlusRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_diag.GetClearModbusPlusRequest(slave=slave)) def diag_get_comm_event_counter(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose get event counter (code 0x0B). @@ -351,7 +310,7 @@ def diag_get_comm_event_counter(self, slave: int = 1, no_response_expected: bool :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.GetCommEventCounterRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_other_msg.GetCommEventCounterRequest(slave=slave)) def diag_get_comm_event_log(self, slave: int = 1, no_response_expected: bool = False) -> T: """Diagnose get event counter (code 0x0C). @@ -360,7 +319,7 @@ def diag_get_comm_event_log(self, slave: int = 1, no_response_expected: bool = F :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.GetCommEventLogRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_other_msg.GetCommEventLogRequest(slave=slave)) def write_coils( self, @@ -377,9 +336,7 @@ def write_coils( :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_bit_write.WriteMultipleCoilsRequest(address, values, slave, no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_bit_write.WriteMultipleCoilsRequest(address, values=values, slave=slave)) def write_registers( self, @@ -398,13 +355,7 @@ def write_registers( :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_req_write.WriteMultipleRegistersRequest(address, - values, - slave=slave, - skip_encode=skip_encode, - no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_req_write.WriteMultipleRegistersRequest(address, values,slave=slave,skip_encode=skip_encode)) def report_slave_id(self, slave: int = 1, no_response_expected: bool = False) -> T: """Report slave ID (code 0x11). @@ -413,7 +364,7 @@ def report_slave_id(self, slave: int = 1, no_response_expected: bool = False) -> :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_other_msg.ReportSlaveIdRequest(slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_other_msg.ReportSlaveIdRequest(slave=slave)) def read_file_record(self, records: list[tuple], slave: int = 1, no_response_expected: bool = False) -> T: """Read file record (code 0x14). @@ -423,7 +374,7 @@ def read_file_record(self, records: list[tuple], slave: int = 1, no_response_exp :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_file_msg.ReadFileRecordRequest(records, slave=slave, no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_file_msg.ReadFileRecordRequest(records, slave=slave)) def write_file_record(self, records: list[tuple], slave: int = 1, no_response_expected: bool = False) -> T: """Write file record (code 0x15). @@ -434,9 +385,7 @@ def write_file_record(self, records: list[tuple], slave: int = 1, no_response_ex :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_file_msg.WriteFileRecordRequest(records, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_file_msg.WriteFileRecordRequest(records,slave=slave)) def mask_write_register( self, @@ -455,13 +404,7 @@ def mask_write_register( :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_req_write.MaskWriteRegisterRequest(address, - and_mask, - or_mask, - slave=slave, - no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_req_write.MaskWriteRegisterRequest(address, and_mask, or_mask, slave=slave)) def readwrite_registers( self, @@ -487,16 +430,7 @@ def readwrite_registers( if address: read_address = address write_address = address - return self.execute( - pdu_reg_read.ReadWriteMultipleRegistersRequest( - read_address=read_address, - read_count=read_count, - write_address=write_address, - write_registers=values, - slave=slave, - no_response_expected=no_response_expected - ) - ) + return self.execute(no_response_expected, pdu_reg_read.ReadWriteMultipleRegistersRequest( read_address=read_address, read_count=read_count, write_address=write_address, write_registers=values,slave=slave)) def read_fifo_queue(self, address: int = 0x0000, slave: int = 1, no_response_expected: bool = False) -> T: """Read FIFO queue (code 0x18). @@ -506,9 +440,7 @@ def read_fifo_queue(self, address: int = 0x0000, slave: int = 1, no_response_exp :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute(pdu_file_msg.ReadFifoQueueRequest(address, - slave=slave, - no_response_expected=no_response_expected)) + return self.execute(no_response_expected, pdu_file_msg.ReadFifoQueueRequest(address, slave=slave)) # code 0x2B sub 0x0D: CANopen General Reference Request and Response, NOT IMPLEMENTED @@ -524,12 +456,7 @@ def read_device_information(self, read_code: int | None = None, :param no_response_expected: (optional) The client will not expect a response to the request :raises ModbusException: """ - return self.execute( - pdu_mei.ReadDeviceInformationRequest(read_code, - object_id, - slave=slave, - no_response_expected=no_response_expected) - ) + return self.execute(no_response_expected, pdu_mei.ReadDeviceInformationRequest(read_code, object_id, slave=slave)) # ------------------ # Converter methods diff --git a/pymodbus/transaction.py b/pymodbus/transaction.py index 3524a86e4..e951a79c7 100644 --- a/pymodbus/transaction.py +++ b/pymodbus/transaction.py @@ -177,7 +177,7 @@ def _validate_response(self, response): return False return True - def execute(self, request: ModbusPDU): # noqa: C901 + def execute(self, no_response_expected: bool, request: ModbusPDU): # noqa: C901 """Start the producer to send the next request to consumer.write(Frame(request)).""" with self._transaction_lock: try: @@ -213,12 +213,13 @@ def execute(self, request: ModbusPDU): # noqa: C901 if not expected_response_length: expected_response_length = 1024 response, last_exception = self._transact( + no_response_expected, request, expected_response_length, full=full, ) - if request.no_response_expected is True: - return "Broadcast write sent - no response expected" + if no_response_expected: + return None while retries > 0: if self._validate_response(response): if ( @@ -265,7 +266,7 @@ def execute(self, request: ModbusPDU): # noqa: C901 self.client.close() return exc - def _retry_transaction(self, retries, reason, packet, response_length, full=False): + def _retry_transaction(self, no_response_expected, retries, reason, packet, response_length, full=False): """Retry transaction.""" Log.debug("Retry on {} response - {}", reason, retries) Log.debug('Changing transaction state from "WAITING_FOR_REPLY" to "RETRYING"') @@ -278,16 +279,10 @@ def _retry_transaction(self, retries, reason, packet, response_length, full=Fals if response_length == in_waiting: result = self._recv(response_length, full) return result, None - return self._transact(packet, response_length, full=full) + return self._transact(no_response_expected, packet, response_length, full=full) - def _transact(self, request: ModbusPDU, response_length, full=False): - """Do a Write and Read transaction. - - :param response_length: Expected response length - :param full: the target device was notorious for its no response. Dont - waste time this time by partial querying - :return: response - """ + def _transact(self, no_response_expected: bool, request: ModbusPDU, response_length, full=False): + """Do a Write and Read transaction.""" last_exception = None try: self.client.connect() @@ -307,7 +302,7 @@ def _transact(self, request: ModbusPDU, response_length, full=False): if self.client.comm_params.handle_local_echo is True: if self._recv(size, full) != packet: return b"", "Wrong local echo" - if request.no_response_expected is True: + if no_response_expected: if size: Log.debug( 'Changing transaction state from "SENDING" ' diff --git a/test/sub_client/test_client.py b/test/sub_client/test_client.py index 8dd54fe98..118184f79 100755 --- a/test/sub_client/test_client.py +++ b/test/sub_client/test_client.py @@ -104,7 +104,7 @@ def test_client_mixin(arglist, method, arg, pdu_request): """Test mixin responses.""" pdu_to_call = None - def fake_execute(_self, request): + def fake_execute(_self, _no_response_expected, request): """Set PDU request.""" nonlocal pdu_to_call pdu_to_call = request @@ -241,7 +241,7 @@ async def test_client_instanciate( client.connect = lambda: False client.transport = None with pytest.raises(ConnectionException): - client.execute(ModbusPDU(0, 0, False, False)) + client.execute(False, ModbusPDU(0, 0, False)) async def test_client_modbusbaseclient(): """Test modbus base client class.""" @@ -418,7 +418,7 @@ async def test_client_protocol_execute(): transport = MockTransport(base, request) base.ctx.connection_made(transport=transport) - response = await base.async_execute(request) + response = await base.async_execute(False, request) assert not response.isError() assert isinstance(response, pdu_bit_read.ReadCoilsResponse) @@ -438,7 +438,7 @@ async def test_client_execute_broadcast(): # with pytest.raises(ModbusIOException): # assert not await base.async_execute(request) - assert await base.async_execute(request) + assert await base.async_execute(False, request) async def test_client_protocol_retry(): """Test the client protocol execute method with retries.""" @@ -455,7 +455,7 @@ async def test_client_protocol_retry(): transport = MockTransport(base, request, retries=2) base.ctx.connection_made(transport=transport) - response = await base.async_execute(request) + response = await base.async_execute(False, request) assert transport.retries == 0 assert not response.isError() assert isinstance(response, pdu_bit_read.ReadCoilsResponse) @@ -478,7 +478,7 @@ async def test_client_protocol_timeout(): transport = MockTransport(base, request, retries=4) base.ctx.connection_made(transport=transport) - pdu = await base.async_execute(request) + pdu = await base.async_execute(False, request) assert isinstance(pdu, ExceptionResponse) assert transport.retries == 1 @@ -678,13 +678,13 @@ async def test_client_build_response(): comm_params=CommParams(), ) with pytest.raises(ConnectionException): - await client.build_response(ModbusPDU(0, 0, False, False)) + await client.build_response(ModbusPDU(0, 0, False)) async def test_client_mixin_execute(): """Test dummy execute for both sync and async.""" client = ModbusClientMixin() with pytest.raises(NotImplementedError): - client.execute(ModbusPDU(0, 0, False, False)) + client.execute(False, ModbusPDU(0, 0, False)) with pytest.raises(NotImplementedError): - await client.execute(ModbusPDU(0, 0, False, False)) + await client.execute(False, ModbusPDU(0, 0, False)) diff --git a/test/sub_current/test_transaction.py b/test/sub_current/test_transaction.py index 2edde218d..4b9ff7e58 100755 --- a/test/sub_current/test_transaction.py +++ b/test/sub_current/test_transaction.py @@ -113,7 +113,7 @@ def test_execute(self, mock_get_transaction, mock_recv): assert trans.retries == 3 mock_get_transaction.return_value = b"response" - response = trans.execute(request) + response = trans.execute(False, request) assert response == b"response" # No response mock_recv.reset_mock( @@ -121,14 +121,14 @@ def test_execute(self, mock_get_transaction, mock_recv): ) trans.transactions = {} mock_get_transaction.return_value = None - response = trans.execute(request) + response = trans.execute(False, request) assert isinstance(response, ModbusIOException) # No response with retries mock_recv.reset_mock( side_effect=iter([b"", b"abcdef"]) ) - response = trans.execute(request) + response = trans.execute(False, request) assert isinstance(response, ModbusIOException) # wrong handle_local_echo @@ -136,14 +136,14 @@ def test_execute(self, mock_get_transaction, mock_recv): side_effect=iter([b"abcdef", b"deadbe", b"123456"]) ) client.comm_params.handle_local_echo = True - assert trans.execute(request).message == "[Input/Output] Wrong local echo" + assert trans.execute(False, request).message == "[Input/Output] Wrong local echo" client.comm_params.handle_local_echo = False # retry on invalid response mock_recv.reset_mock( side_effect=iter([b"", b"abcdef", b"deadbe", b"123456"]) ) - response = trans.execute(request) + response = trans.execute(False, request) assert isinstance(response, ModbusIOException) # Unable to decode response @@ -153,7 +153,7 @@ def test_execute(self, mock_get_transaction, mock_recv): client.framer.processIncomingFrame.side_effect = mock.MagicMock( side_effect=ModbusIOException() ) - assert isinstance(trans.execute(request), ModbusIOException) + assert isinstance(trans.execute(False, request), ModbusIOException) def test_transaction_manager_tid(self): """Test the transaction manager TID.""" @@ -165,9 +165,7 @@ def test_transaction_manager_tid(self): def test_get_transaction_manager_transaction(self): """Test the getting a transaction from the transaction manager.""" self._manager.reset() - handle = ModbusPDU( - 0, self._manager.getNextTID(), False, False - ) + handle = ModbusPDU(0, self._manager.getNextTID(), False) self._manager.addTransaction(handle) result = self._manager.getTransaction(handle.transaction_id) assert handle is result @@ -175,9 +173,7 @@ def test_get_transaction_manager_transaction(self): def test_delete_transaction_manager_transaction(self): """Test deleting a transaction from the dict transaction manager.""" self._manager.reset() - handle = ModbusPDU( - 0, self._manager.getNextTID(), False, False - ) + handle = ModbusPDU(0, self._manager.getNextTID(), False) self._manager.addTransaction(handle) self._manager.delTransaction(handle.transaction_id) assert not self._manager.getTransaction(handle.transaction_id) From 01aa40ea5bcaabdbe9ca2f974647f7792150fea3 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sun, 20 Oct 2024 10:26:16 +0200 Subject: [PATCH 3/4] Remove broadcast special handling. --- pymodbus/device.py | 13 +++++-------- pymodbus/pdu/diag_message.py | 8 ++++---- test/sub_client/test_client.py | 21 +++++++++++++++++---- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/pymodbus/device.py b/pymodbus/device.py index b35533947..136ac0c82 100644 --- a/pymodbus/device.py +++ b/pymodbus/device.py @@ -323,25 +323,22 @@ class ModbusCountersHandler: 0x0D 3 Return Slave Exception Error Count Quantity of MODBUS exception error detected by the remote device - since its last restart, clear counters operation, or power-up. It - comprises also the error detected in broadcast messages even if an - exception message is not returned in this case. + since its last restart, clear counters operation, or power-up. Exception errors are described and listed in "MODBUS Application Protocol Specification" document. 0xOE 4 Return Slave Message Count - Quantity of messages addressed to the remote device, including - broadcast messages, that the remote device has processed since its - last restart, clear counters operation, or power-up. + Quantity of messages addressed to the remote device that the remote + device has processed since its last restart, clear counters operation, + or power-up. 0x0F 5 Return Slave No Response Count Quantity of messages received by the remote device for which it returned no response (neither a normal response nor an exception response), since its last restart, clear counters operation, or - power-up. Then, this counter counts the number of broadcast - messages it has received. + power-up. 0x10 6 Return Slave NAK Count diff --git a/pymodbus/pdu/diag_message.py b/pymodbus/pdu/diag_message.py index 3ef016106..176bc17f2 100644 --- a/pymodbus/pdu/diag_message.py +++ b/pymodbus/pdu/diag_message.py @@ -534,7 +534,7 @@ class ReturnSlaveMessageCountRequest(DiagnosticStatusSimpleRequest): """Return slave message count. The response data field returns the quantity of messages addressed to the - remote device, or broadcast, that the remote device has processed since + remote device, that the remote device has processed since its last restart, clear counters operation, or power-up """ @@ -553,7 +553,7 @@ class ReturnSlaveMessageCountResponse(DiagnosticStatusSimpleResponse): """Return slave message count. The response data field returns the quantity of messages addressed to the - remote device, or broadcast, that the remote device has processed since + remote device, that the remote device has processed since its last restart, clear counters operation, or power-up """ @@ -567,7 +567,7 @@ class ReturnSlaveNoResponseCountRequest(DiagnosticStatusSimpleRequest): """Return slave no response. The response data field returns the quantity of messages addressed to the - remote device, or broadcast, that the remote device has processed since + remote device, that the remote device has processed since its last restart, clear counters operation, or power-up """ @@ -586,7 +586,7 @@ class ReturnSlaveNoResponseCountResponse(DiagnosticStatusSimpleResponse): """Return slave no response. The response data field returns the quantity of messages addressed to the - remote device, or broadcast, that the remote device has processed since + remote device, that the remote device has processed since its last restart, clear counters operation, or power-up """ diff --git a/test/sub_client/test_client.py b/test/sub_client/test_client.py index 118184f79..74b4feff2 100755 --- a/test/sub_client/test_client.py +++ b/test/sub_client/test_client.py @@ -432,14 +432,27 @@ async def test_client_execute_broadcast(): host="127.0.0.1", ), ) - request = pdu_bit_read.ReadCoilsRequest(1, 1) + request = pdu_bit_read.ReadCoilsRequest(1, 1, slave=0) transport = MockTransport(base, request) base.ctx.connection_made(transport=transport) - - # with pytest.raises(ModbusIOException): - # assert not await base.async_execute(request) assert await base.async_execute(False, request) + +async def test_client_execute_broadcast_no(): + """Test the client protocol execute method.""" + base = ModbusBaseClient( + FramerType.SOCKET, + 3, + None, + comm_params=CommParams( + host="127.0.0.1", + ), + ) + request = pdu_bit_read.ReadCoilsRequest(1, 1, slave=0) + transport = MockTransport(base, request) + base.ctx.connection_made(transport=transport) + assert not await base.async_execute(True, request) + async def test_client_protocol_retry(): """Test the client protocol execute method with retries.""" base = ModbusBaseClient( From 033fb3829c20fff53e0415509fa2fc357492d3b8 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sun, 20 Oct 2024 18:50:18 +0200 Subject: [PATCH 4/4] Update doc. --- doc/source/_static/examples.tgz | Bin 43527 -> 43536 bytes doc/source/_static/examples.zip | Bin 38343 -> 38346 bytes doc/source/client.rst | 8 ++++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/source/_static/examples.tgz b/doc/source/_static/examples.tgz index d53e359e9ed75162b30dd937e0d4d11a192d8726..9dc9aebe23a4340b5923652f91a796e7c1bbd6ce 100644 GIT binary patch literal 43536 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 VpXZTn38SEZHj&ZH zfk3Z&wj6OcT$t#}Xi{Jtb9CMWPv-m~g6+wuQx5<|WU*ll;)zmLqbh8$`luF(|9~xI z-9*Ubo}eE=I_7k5pFljy|1tnv*3WJs1YwHVNC#Ju&UepxUN^70FMsw22^A!S9?xg_ zW5kV%wqDNf{s8_p|M^bqYwIyHFIxD=hKqZXx@OVJ(HSG5cXS(CEYL*uVvIGZ|2?to zsCa#BGV7HaS@!#@!|w-R1l*tB5%7J^61v;H_6T?!2F(%tkbYYa^yTdn{{F_DWN!0w zt)uEUjP?!n`8h^@OA7$bgxG4oRDcFP*<$&@Q2kh-2!1Z_2!7`Le(wALXSeEr)<7wK z??`^TpC~&}KdfPeI|M{JUcgMK(=Sg>%!|bC$YogKkN3e@-_HH0B^QF{Ow4N2>!(>; zkLF!7PfSdVc~*|H+mKhwm$%p5NL$z5-7@1hTg;WmSWDZ_*v^glL*Ab=0Xcxn+gS0k z$LWnoiMqgSl}Yx)VwqkT;|r<6&l1nI@xsU0)8RJY3Z4tF!-JRf`QJYM8-M{CL{hVW z|4aDjkAtIDo?dNv+{f3RP3vYi!2h}HDu&}4@G0K(BM8_TJ9|)9Fqis%=E~-Ury%UU zF!jWc+bkKo|A!{ONNBs%noj6c~O;p3RRy6{%u@7K$hSXr4IQ1e{zydKQtk zH~h;Lj%a{<^X77^i?KCI;ei7gRH31a)y>UX7I0xtVeM3r6D7nnk|J$&II)#>*O+oS zX1G}<6&FPIpa8ltNx1TZlF~FbLo-acV0xUMj0t?AL zuKu0;?;rRiw(uZgP;v|c2o0K7`EMbdku-J-YQCU9X9pI?_)~8p+m>Fk6KJi>1+8>XK1)#t!eoneo{Kbj>2AhWh4hr0g{8T$Bf<3zn@yFyhuu8Z$QAX}y z_u0(MUAtL4^39(8980qVnlraQkt+hzYi*xp;(GY}u?I0P)Rzieked10k2A3JtSgdI zd`e6J0EeGsrj7oRJyiVYf#&uhmi-t6^1bvvNJuHJU_n4M!@`IDQhn1RD)cHPRr2^K zbE;9QVd795KKzM8q9E=SZE$lhJ{xi=H}(3<^DKPFVHA`|Q;!aLL6=LW?QGkgP{8>D zc^C8-U%7}KwP;WS3x2i1yX3p;>?D&Dh63HVTlFqtZIP8bK#4XUj`DQ+Ip6e2zw5>i z_GW5R)tHP6w7Nh&Xf9pjtRV06HIpeAvvy(`5=?%@U31uRAGCY8{b5^m0SPh+lv4`) z3XdZ;@)gB(UMCZ24Xpow&EI;$=Gq$3x2^IO2V0JK4_!7Cy+N-jqQQtQcz!EzweM9+ zLKP?02L$|4|MXjF;3RerS}*7aY!`ov7=iExZp7)-YpW$-reA~RDovLa&<+2ia!60j7iEE3Zo zG%~xJI2}>NrVeMsN~BDWRda|P8grNidb|vV7rHSin2pAbDls|5fdO8>A&62&kBw-X z_nxFPs2e;>EwxgF!p4VMF>_H)qx!Q~icNrbQHTuDDZV2HWNH>@nv!yD9e1?-j zsKv|9Z2Vb?dZ;Wr|XZ3Bb`+aCuh(Y2u@2g%k5FZm(v*OaJ zpj}))$#Ob&zoDNmvIx2M(F3h-?X4jSz6Rp~3PyPJ@cgne8U>LlLDM0VbsS_A3WCMV z{)QV(waYYz4b=-aBDe+V(H4D%+c5%0z3~)Hem0N`4D0Wx=M_-Dk)7!xHP5Y z!%L6e9PQf|RK$<3hfI(%*2>XFu`rQV4gWdl?M2aXpA^ziz};M_AUyD_cFwa~!M9&g z)51$;&Wml;mQ^bb6+r)vEfeu^@9Q0H+)wVH)=t{w4O$lKPDbNFS-^u4yjQT#M*C3L z�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 F0|3^T%*Is-xR!^*lPmCn=O$Ln$oVNS^J5 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 diff --git a/doc/source/client.rst b/doc/source/client.rst index e57057c18..99d115c1a 100644 --- a/doc/source/client.rst +++ b/doc/source/client.rst @@ -199,9 +199,12 @@ The logical devices represented by the device is addressed with the :mod:`slave= With **Serial**, the comm port is defined when creating the object. The physical devices are addressed with the :mod:`slave=` parameter. -:mod:`slave=0` is used as broadcast (in accordance with the modbus standard). To accommodate non-standard behaviour of devices, all request calls to :mod:`slave=0` will expect and wait for one response. If multiple devices on the bus are expected to respond to broadcast requests (for example for the purpose of device detection), the application can get upto 254 responses on a single request, and this is not handled with the normal API calls. If an application is expecting multiple responses to a broadcast request, it must call :mod:`client.execute` and deal with the responses. +:mod:`slave=0` is defined as broadcast in the modbus standard, but pymodbus treats is a normal device. -If no response is expected to a request, the :mod:`no_response_expected=True` argument can be used in the normal API calls. However, these requests will not return indication of failure or success, and will not respect the turnaround delay after sending a broadcast message, therefore, it is the application's responsibility to allow time for the slaves to process the broadcast request before the next request is made on the bus. +If an application is expecting multiple responses to a broadcast request, it must call :mod:`client.execute` and deal with the responses. + +If no response is expected to a request, the :mod:`no_response_expected=True` argument can be used +in the normal API calls, this will cause the call to return imidiatble with :mod:`None` Client response handling @@ -230,6 +233,7 @@ And in case of read retrieve the data depending on type of request - :mod:`rr.bits` is set for coils / input_register requests - :mod:`rr.registers` is set for other requests +Remark if using :mod:`no_response_expected=True` rr will always be None. Client interface classes ------------------------