Skip to content

Commit

Permalink
Step.
Browse files Browse the repository at this point in the history
  • Loading branch information
janiversen committed Nov 26, 2024
1 parent afd04ca commit fb22bfa
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 49 deletions.
3 changes: 3 additions & 0 deletions pymodbus/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ def __init__(
self.framer,
retries,
False,
None,
None,
None,
self,
)
self.reconnect_delay_current = self.comm_params.reconnect_delay or 0
Expand Down
55 changes: 28 additions & 27 deletions pymodbus/transaction/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,19 @@ def __init__(
framer: FramerBase,
retries: int,
is_server: bool,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
sync_client = None,
) -> None:
"""Initialize an instance of the ModbusTransactionManager."""
super().__init__(params, is_server, is_sync=bool(sync_client))
self.framer = framer
self.retries = retries
self.next_tid: int = 0
self.trace_recv_packet: Callable[[bytes | None], bytes] | None = None
self.trace_recv_pdu: Callable[[ModbusPDU | None], ModbusPDU] | None = None
self.trace_send_packet: Callable[[bytes | None], bytes] | None = None
self.trace_send_pdu: Callable[[ModbusPDU | None], ModbusPDU] | None = None
self.trace_packet = trace_packet or self.dummy_trace_packet
self.trace_pdu = trace_pdu or self.dummy_trace_pdu
self.trace_connect = trace_connect or self.dummy_trace_connect
self.accept_no_response_limit = retries + 3
self.count_no_responses = 0
if sync_client:
Expand All @@ -60,6 +62,20 @@ def __init__(
self._lock = asyncio.Lock()
self.response_future: asyncio.Future = asyncio.Future()

def dummy_trace_packet(self, sending: bool, data: bytes) -> bytes:
"""Do dummy trace."""
_ = sending
return data

def dummy_trace_pdu(self, sending: bool, pdu: ModbusPDU) -> ModbusPDU:
"""Do dummy trace."""
_ = sending
return pdu

def dummy_trace_connect(self, connect: bool) -> None:
"""Do dummy trace."""
_ = connect

def sync_get_response(self) -> ModbusPDU:
"""Receive until PDU is correct or timeout."""
databuffer = b''
Expand All @@ -84,14 +100,10 @@ def sync_execute(self, no_response_expected: bool, request: ModbusPDU) -> Modbus
raise ConnectionException("Client cannot connect (automatic retry continuing) !!")
with self._sync_lock:
request.transaction_id = self.getNextTID()
if self.trace_send_pdu:
request = self.trace_send_pdu(request) # pylint: disable=not-callable
packet = self.framer.buildFrame(request)
packet = self.framer.buildFrame(self.trace_pdu(True, request))
count_retries = 0
while count_retries <= self.retries:
if self.trace_send_packet:
packet = self.trace_send_packet(packet) # pylint: disable=not-callable
self.sync_client.send(packet)
self.sync_client.send(self.trace_packet(True, packet))
if no_response_expected:
return ExceptionResponse(0xff)
try:
Expand Down Expand Up @@ -120,14 +132,10 @@ async def execute(self, no_response_expected: bool, request: ModbusPDU) -> Modbu
raise ConnectionException("Client cannot connect (automatic retry continuing) !!")
async with self._lock:
request.transaction_id = self.getNextTID()
if self.trace_send_pdu:
request = self.trace_send_pdu(request) # pylint: disable=not-callable
packet = self.framer.buildFrame(request)
packet = self.framer.buildFrame(self.trace_pdu(True, request))
count_retries = 0
while count_retries <= self.retries:
if self.trace_send_packet:
packet = self.trace_send_packet(packet) # pylint: disable=not-callable
self.send(packet)
self.send(self.trace_packet(True, packet))
if no_response_expected:
return None
try:
Expand Down Expand Up @@ -166,30 +174,23 @@ def callback_connected(self) -> None:
"""Call when connection is succcesfull."""
self.count_no_responses = 0
self.next_tid = 0
self.trace_connect(True)

def callback_disconnected(self, exc: Exception | None) -> None:
"""Call when connection is lost."""
for call in (self.trace_recv_packet,
self.trace_recv_pdu,
self.trace_send_packet,
self.trace_send_pdu):
if call:
call(None) # pylint: disable=not-callable
self.trace_connect(False)

def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
"""Handle received data."""
if self.trace_recv_packet:
data = self.trace_recv_packet(data) # pylint: disable=not-callable
try:
used_len, pdu = self.framer.processIncomingFrame(data)
used_len, pdu = self.framer.processIncomingFrame(self.trace_packet(False, data))
except ModbusIOException as exc:
if self.is_server:
self.response_future.set_result((None, addr, exc))
return len(data)
raise exc
if pdu:
if self.trace_recv_pdu:
pdu = self.trace_recv_pdu(pdu) # pylint: disable=not-callable
pdu = self.trace_pdu(False, pdu)
result = (pdu, addr, None) if self.is_server else pdu
self.response_future.set_result(result)
return used_len
Expand Down
41 changes: 19 additions & 22 deletions test/transaction/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,13 @@ async def test_transaction_calls(self, use_clc):
async def test_transaction_disconnect(self, use_clc):
"""Test tracers in disconnect."""
transact = TransactionManager(use_clc, FramerRTU(DecodePDU(False)), 5, False)
transact.trace_packet = mock.Mock()
transact.trace_pdu = mock.Mock()
transact.trace_connect = mock.Mock()
transact.callback_disconnected(None)
transact.trace_recv_packet = mock.Mock()
transact.trace_recv_pdu = mock.Mock()
transact.trace_send_packet = mock.Mock()
transact.trace_send_pdu = mock.Mock()
transact.callback_disconnected(None)
transact.trace_recv_packet.assert_called_once_with(None)
transact.trace_recv_pdu.assert_called_once_with(None)
transact.trace_send_packet.assert_called_once_with(None)
transact.trace_send_pdu.assert_called_once_with(None)
transact.trace_connect.assert_called_once_with(False)
transact.trace_packet.assert_not_called()
transact.trace_pdu.assert_not_called()

@pytest.mark.parametrize("test", [True, False])
async def test_transaction_data(self, use_clc, test):
Expand All @@ -60,19 +57,19 @@ async def test_transaction_data(self, use_clc, test):
packet = b'123'
transact.callback_data(packet)
assert not transact.response_future.done()
transact.trace_recv_packet = mock.Mock()
transact.trace_packet = mock.Mock(return_value=packet)
pdu = "dummy pdu"

if test:
transact.framer.processIncomingFrame.return_value = (1, pdu)
transact.callback_data(packet)
transact.trace_recv_packet.assert_called_once_with(packet)
transact.trace_packet.assert_called_once_with(False, packet)
else:
transact.trace_recv_pdu = mock.Mock(return_value=pdu)
transact.trace_pdu = mock.Mock(return_value=pdu)
transact.framer.processIncomingFrame.return_value = (1, pdu)
transact.callback_data(packet)
transact.trace_recv_packet.assert_called_with(packet)
transact.trace_recv_pdu.assert_called_once_with(pdu)
transact.trace_packet.assert_called_with(False, packet)
transact.trace_pdu.assert_called_once_with(False, pdu)
assert transact.response_future.result() == pdu

@pytest.mark.parametrize("scenario", range(6))
Expand All @@ -93,11 +90,11 @@ async def test_transaction_execute(self, use_clc, scenario):
transact.connect = mock.AsyncMock(return_value=1)
await transact.execute(True, request)
elif scenario == 2: # transport ok, trace and send
transact.trace_send_pdu = mock.Mock(return_value=request)
transact.trace_send_packet = mock.Mock(return_value=b'123')
transact.trace_pdu = mock.Mock(return_value=request)
transact.trace_packet = mock.Mock(return_value=b'123')
await transact.execute(True, request)
transact.trace_send_pdu.assert_called_once_with(request)
transact.trace_send_packet.assert_called_once_with(b'\x00\x01\x00u\x00\x05\xec\x02')
transact.trace_pdu.assert_called_once_with(True, request)
transact.trace_packet.assert_called_once_with(True, b'\x00\x01\x00u\x00\x05\xec\x02')
elif scenario == 3: # wait receive,timeout, no_responses
transact.comm_params.timeout_connect = 0.1
transact.count_no_responses = 10
Expand Down Expand Up @@ -179,11 +176,11 @@ async def test_sync_transaction_execute(self, use_clc, scenario):
transact.sync_client.connect = mock.Mock(return_value=True)
transact.sync_execute(True, request)
elif scenario == 2: # transport ok, trace and send
transact.trace_send_pdu = mock.Mock(return_value=request)
transact.trace_send_packet = mock.Mock(return_value=b'123')
transact.trace_pdu = mock.Mock(return_value=request)
transact.trace_packet = mock.Mock(return_value=b'123')
transact.sync_execute(True, request)
transact.trace_send_pdu.assert_called_once_with(request)
transact.trace_send_packet.assert_called_once_with(b'\x00\x01\x00u\x00\x05\xec\x02')
transact.trace_pdu.assert_called_once_with(True, request)
transact.trace_packet.assert_called_once_with(True, b'\x00\x01\x00u\x00\x05\xec\x02')
elif scenario == 3: # wait receive,timeout, no_responses
transact.comm_params.timeout_connect = 0.1
transact.count_no_responses = 10
Expand Down

0 comments on commit fb22bfa

Please sign in to comment.