diff --git a/pymodbus/transaction/transaction.py b/pymodbus/transaction/transaction.py index 9d58a9857..188be10c7 100644 --- a/pymodbus/transaction/transaction.py +++ b/pymodbus/transaction/transaction.py @@ -11,7 +11,7 @@ from ..framer import FramerAscii, FramerBase, FramerRTU from ..logging import Log from ..pdu import ModbusPDU -from ..transport import CommParams, ModbusProtocol +from ..transport import CommParams, CommType, ModbusProtocol class TransactionManager(ModbusProtocol): @@ -169,7 +169,8 @@ def sync_execute(self, no_response_expected: bool, request: ModbusPDU) -> Modbus except asyncio.exceptions.TimeoutError: count_retries += 1 if self.count_until_disconnect < 0: - self.connection_lost(asyncio.TimeoutError("Server not responding")) + if self.comm_params.comm_type != CommType.SERIAL: + self.connection_lost(asyncio.TimeoutError("Server not responding")) raise self._io_exception_from_request( "ERROR: No response received of the last requests (default: retries+3), CLOSING CONNECTION.", request, @@ -225,7 +226,8 @@ async def execute( except asyncio.exceptions.TimeoutError: count_retries += 1 if self.count_until_disconnect < 0: - self.connection_lost(asyncio.TimeoutError("Server not responding")) + if self.comm_params.comm_type != CommType.SERIAL: + self.connection_lost(asyncio.TimeoutError("Server not responding")) raise self._io_exception_from_request( "ERROR: No response received of the last requests (default: retries+3), CLOSING CONNECTION.", request, diff --git a/test/transaction/test_transaction.py b/test/transaction/test_transaction.py index 66b9ed4d4..6972ca0e5 100755 --- a/test/transaction/test_transaction.py +++ b/test/transaction/test_transaction.py @@ -12,6 +12,7 @@ from pymodbus.pdu import DecodePDU, ExceptionResponse from pymodbus.pdu.bit_message import ReadCoilsRequest, ReadCoilsResponse from pymodbus.transaction import TransactionManager +from pymodbus.transport import CommType @pytest.mark.parametrize("use_port", [5098]) @@ -186,8 +187,8 @@ async def test_transaction_data_2(self, use_clc, test): transact.response_future.set_result((1, pdu)) transact.callback_data(packet) - @pytest.mark.parametrize("scenario", range(10)) - async def test_transaction_execute(self, use_clc, scenario): + @pytest.mark.parametrize("scenario", range(11)) + async def test_transaction_execute(self, use_clc, scenario): # noqa: C901 """Test tracers in disconnect.""" transact = TransactionManager( use_clc, @@ -236,12 +237,21 @@ async def test_transaction_execute(self, use_clc, scenario): await transact.execute(False, request) assert exc_info.value.fcode == request.function_code assert exc_info.value.transaction_id == request.transaction_id - elif scenario == 5: # wait receive,timeout, no_responses pass + elif scenario == 5: # wait receive,timeout, disconnect + transact.comm_params.timeout_connect = 0.1 + transact.comm_params.comm_type = CommType.SERIAL + transact.count_until_disconnect = -1 + transact.connection_lost = mock.Mock() # type: ignore[method-assign] + with pytest.raises(ModbusIOException) as exc_info: + await transact.execute(False, request) + assert exc_info.value.fcode == request.function_code + assert exc_info.value.transaction_id == request.transaction_id + elif scenario == 6: # wait receive,timeout, no_responses pass transact.comm_params.timeout_connect = 0.1 transact.connection_lost = mock.Mock() # type: ignore[method-assign] with pytest.raises(ModbusIOException): await transact.execute(False, request) - elif scenario == 6: # wait receive, cancel + elif scenario == 7: # wait receive, cancel transact.comm_params.timeout_connect = 0.2 resp = asyncio.create_task(transact.execute(False, request)) await asyncio.sleep(0.1) @@ -250,14 +260,14 @@ async def test_transaction_execute(self, use_clc, scenario): with pytest.raises(asyncio.CancelledError): await resp assert resp.cancelled() - elif scenario == 7: # response + elif scenario == 8: # response transact.comm_params.timeout_connect = 0.2 resp = asyncio.create_task(transact.execute(False, request)) await asyncio.sleep(0.1) transact.response_future.set_result(response) await asyncio.sleep(0.1) assert response == await resp - elif scenario == 8: # response wrong dev_id + elif scenario == 9: # response wrong dev_id transact.comm_params.timeout_connect = 0.2 resp = asyncio.create_task(transact.execute(False, request)) await asyncio.sleep(0.1) @@ -270,7 +280,7 @@ async def test_transaction_execute(self, use_clc, scenario): assert exc_info.value.fcode == request.function_code assert exc_info.value.dev_id == request.dev_id assert exc_info.value.transaction_id == request.transaction_id - else: # if scenario == 9: # response wrong tid + else: # if scenario == 10: # response wrong tid transact.comm_params.timeout_connect = 0.2 resp = asyncio.create_task(transact.execute(False, request)) await asyncio.sleep(0.1) @@ -421,8 +431,8 @@ def test_sync_transaction_instance(self, use_clc): sync_client=client, ) - @pytest.mark.parametrize("scenario", range(10)) - async def test_sync_transaction_execute(self, use_clc, scenario): + @pytest.mark.parametrize("scenario", range(11)) + async def test_sync_transaction_execute(self, use_clc, scenario): # noqa: C901 """Test tracers in disconnect.""" client = self.dummy_client(use_clc) transact = TransactionManager( @@ -473,11 +483,19 @@ async def test_sync_transaction_execute(self, use_clc, scenario): transact.sync_execute(False, request) assert exc_info.value.fcode == request.function_code assert exc_info.value.transaction_id == request.transaction_id - elif scenario == 5: # wait receive,timeout, no_responses pass + elif scenario == 5: # wait receive,timeout, disconnect + transact.comm_params.timeout_connect = 0.1 + transact.comm_params.comm_type = CommType.SERIAL + transact.count_until_disconnect = -1 + with pytest.raises(ModbusIOException) as exc_info: + transact.sync_execute(False, request) + assert exc_info.value.fcode == request.function_code + assert exc_info.value.transaction_id == request.transaction_id + elif scenario == 6: # wait receive,timeout, no_responses pass transact.comm_params.timeout_connect = 0.1 with pytest.raises(ModbusIOException): transact.sync_execute(False, request) - elif scenario == 6: # response + elif scenario == 7: # response transact.transport = 1 # type: ignore[assignment] resp_bytes = transact.framer.buildFrame(response) transact.sync_client.recv = mock.Mock(return_value=resp_bytes) @@ -485,7 +503,7 @@ async def test_sync_transaction_execute(self, use_clc, scenario): transact.comm_params.timeout_connect = 0.2 resp = transact.sync_execute(False, request) assert response.bits == resp.bits - elif scenario == 7: # response wrong dev_id + elif scenario == 8: # response wrong dev_id transact.transport = 1 # type: ignore[assignment] pdu = copy.deepcopy(response) pdu.dev_id = 17 @@ -497,7 +515,7 @@ async def test_sync_transaction_execute(self, use_clc, scenario): assert exc_info.value.fcode == request.function_code assert exc_info.value.dev_id == request.dev_id assert exc_info.value.transaction_id == request.transaction_id - elif scenario == 8: # response wrong tid + elif scenario == 9: # response wrong tid transact.transport = 1 # type: ignore[assignment] pdu = copy.deepcopy(response) pdu.transaction_id = 17 @@ -509,7 +527,7 @@ async def test_sync_transaction_execute(self, use_clc, scenario): assert exc_info.value.fcode == request.function_code assert exc_info.value.dev_id == request.dev_id assert exc_info.value.transaction_id == request.transaction_id - else: # if scenario == 9 # pdu_send from client + else: # if scenario == 10 # pdu_send from client transact.transport = 1 # type: ignore[assignment] transact.is_server = True resp_bytes = transact.framer.buildFrame(response)