Skip to content

Commit a838255

Browse files
authored
Duplicate transactions in UDP. (#1486)
1 parent f9febb9 commit a838255

File tree

3 files changed

+14
-39
lines changed

3 files changed

+14
-39
lines changed

pymodbus/framer/socket_framer.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def decode_data(self, data):
137137
}
138138
return {}
139139

140-
def processIncomingPacket(self, data, callback, slave, **kwargs):
140+
def processIncomingPacket(self, data, callback, slave, tid: int = None, **kwargs):
141141
"""Process new packet pattern.
142142
143143
This takes in a new request packet, adds it to the current
@@ -148,12 +148,6 @@ def processIncomingPacket(self, data, callback, slave, **kwargs):
148148
149149
The processed and decoded messages are pushed to the callback
150150
function to process and send.
151-
152-
:param data: The new packet data
153-
:param callback: The function to send results to
154-
:param slave: Process if slave id matches, ignore otherwise (could be a
155-
list of slave ids (server) or single slave id(client/server)
156-
:param kwargs:
157151
"""
158152
if not isinstance(slave, (list, tuple)):
159153
slave = [slave]
@@ -165,7 +159,7 @@ def processIncomingPacket(self, data, callback, slave, **kwargs):
165159
if len(self._buffer):
166160
# Possible error ???
167161
if self._header["len"] < 2:
168-
self._process(callback, error=True)
162+
self._process(callback, tid, error=True)
169163
break
170164
if not self.checkFrame():
171165
Log.debug("Frame check failed, ignoring!!")
@@ -176,9 +170,9 @@ def processIncomingPacket(self, data, callback, slave, **kwargs):
176170
Log.debug("Not a valid slave id - {}, ignoring!!", header_txt)
177171
self.resetFrame()
178172
continue
179-
self._process(callback)
173+
self._process(callback, tid)
180174

181-
def _process(self, callback, error=False):
175+
def _process(self, callback, tid, error=False):
182176
"""Process incoming packets irrespective error condition."""
183177
data = self.getRawFrame() if error else self.getFrame()
184178
if (result := self.decoder.decode(data)) is None:
@@ -187,7 +181,10 @@ def _process(self, callback, error=False):
187181
raise InvalidMessageReceivedException(result)
188182
self.populateResult(result)
189183
self.advanceFrame()
190-
callback(result) # defer or push to a thread?
184+
if tid and tid != result.transaction_id:
185+
self.resetFrame()
186+
else:
187+
callback(result) # defer or push to a thread?
191188

192189
def resetFrame(self):
193190
"""Reset the entire message frame.

pymodbus/transaction.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,10 @@ def execute(self, request): # pylint: disable=too-complex
217217
tid=request.transaction_id,
218218
)
219219
self.client.framer.processIncomingPacket(
220-
response, addTransaction, request.slave_id
220+
response,
221+
addTransaction,
222+
request.slave_id,
223+
tid=request.transaction_id,
221224
)
222225
if not (response := self.getTransaction(request.transaction_id)):
223226
if len(self.transactions):

test/test_client_sync.py

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -84,37 +84,12 @@ def test_udp_client_recv_duplicate(self):
8484
test_msg = b"\x00\x01\x00\x00\x00\x05\x01\x04\x02\x00\x03"
8585
client = ModbusUdpClient("127.0.0.1")
8686
client.socket = mockSocket(copy_send=False)
87-
88-
# test normal receive
89-
client.socket.mock_prepare_receive(test_msg)
90-
reply_ok = client.read_input_registers(0x820, 1, 1)
91-
assert not reply_ok.isError()
92-
reply_timeout = client.read_input_registers(0x820, 1, 1)
93-
assert reply_timeout.isError()
94-
client.close()
95-
96-
# test duplicate receive
97-
client = ModbusUdpClient("127.0.0.1")
98-
client.socket = mockSocket(copy_send=False)
9987
client.socket.mock_prepare_receive(test_msg)
10088
client.socket.mock_prepare_receive(test_msg)
10189
reply_ok = client.read_input_registers(0x820, 1, 1)
10290
assert not reply_ok.isError()
103-
# ERROR hanging transaction --> reply_timeout = client.read_input_registers(0x820, 1, 1)
104-
# ERROR hanging transaction --> assert reply_timeout.isError()
105-
client.close()
106-
107-
# test duplicate receive with garbage
108-
client = ModbusUdpClient("127.0.0.1")
109-
client.socket = mockSocket(copy_send=False)
110-
client.socket.mock_prepare_receive(test_msg)
111-
client.socket.mock_prepare_receive(test_msg + b"\xf6\x3e")
112-
reply_ok = client.read_input_registers(0x820, 1, 1)
113-
assert not reply_ok.isError()
114-
# ERROR hanging transaction --> reply_timeout = client.read_input_registers(0x820, 1, 1)
115-
# ERROR hanging transaction --> assert reply_timeout.isError()
116-
# ERROR hanging transaction --> reply_timeout = client.read_input_registers(0x820, 1, 1)
117-
# ERROR hanging transaction --> assert reply_timeout.isError()
91+
reply_none = client.read_input_registers(0x40, 10, 1)
92+
assert reply_none.isError()
11893
client.close()
11994

12095
def test_udp_client_repr(self):

0 commit comments

Comments
 (0)