Skip to content

Commit 6913e9c

Browse files
authored
Allow socket frames to be split in multiple packets (#1923)
1 parent 81df5b1 commit 6913e9c

File tree

2 files changed

+55
-34
lines changed

2 files changed

+55
-34
lines changed

pymodbus/framer/socket_framer.py

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,21 @@ def checkFrame(self):
5252
5353
Return true if we were successful.
5454
"""
55-
if self.isFrameReady():
56-
(
57-
self._header["tid"],
58-
self._header["pid"],
59-
self._header["len"],
60-
self._header["uid"],
61-
) = struct.unpack(">HHHB", self._buffer[0 : self._hsize])
62-
63-
# someone sent us an error? ignore it
64-
if self._header["len"] < 2:
65-
self.advanceFrame()
66-
# we have at least a complete message, continue
67-
elif len(self._buffer) - self._hsize + 1 >= self._header["len"]:
68-
return True
55+
if not self.isFrameReady():
56+
return False
57+
(
58+
self._header["tid"],
59+
self._header["pid"],
60+
self._header["len"],
61+
self._header["uid"],
62+
) = struct.unpack(">HHHB", self._buffer[0 : self._hsize])
63+
64+
# someone sent us an error? ignore it
65+
if self._header["len"] < 2:
66+
self.advanceFrame()
67+
# we have at least a complete message, continue
68+
elif len(self._buffer) - self._hsize + 1 >= self._header["len"]:
69+
return True
6970
# we don't have enough of a message yet, wait
7071
return False
7172

@@ -128,23 +129,15 @@ def frameProcessIncomingPacket(self, single, callback, slave, tid=None, **kwargs
128129
The processed and decoded messages are pushed to the callback
129130
function to process and send.
130131
"""
131-
while True:
132-
if not self.isFrameReady():
133-
if len(self._buffer):
134-
# Possible error ???
135-
if self._header["len"] < 2:
136-
self._process(callback, tid, error=True)
137-
break
138-
if not self.checkFrame():
139-
Log.debug("Frame check failed, ignoring!!")
140-
self.resetFrame()
141-
continue
142-
if not self._validate_slave_id(slave, single):
143-
header_txt = self._header["uid"]
144-
Log.debug("Not a valid slave id - {}, ignoring!!", header_txt)
145-
self.resetFrame()
146-
continue
147-
self._process(callback, tid)
132+
if not self.checkFrame():
133+
Log.debug("Frame check failed, ignoring!!")
134+
return
135+
if not self._validate_slave_id(slave, single):
136+
header_txt = self._header["uid"]
137+
Log.debug("Not a valid slave id - {}, ignoring!!", header_txt)
138+
self.resetFrame()
139+
return
140+
self._process(callback, tid)
148141

149142
def _process(self, callback, tid, error=False):
150143
"""Process incoming packets irrespective error condition."""

test/test_framers.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
from pymodbus.client.base import ModbusBaseClient
99
from pymodbus.exceptions import ModbusIOException
1010
from pymodbus.factory import ClientDecoder
11-
from pymodbus.framer.ascii_framer import ModbusAsciiFramer
12-
from pymodbus.framer.binary_framer import ModbusBinaryFramer
13-
from pymodbus.framer.rtu_framer import ModbusRtuFramer
11+
from pymodbus.framer import (
12+
ModbusAsciiFramer,
13+
ModbusBinaryFramer,
14+
ModbusRtuFramer,
15+
ModbusSocketFramer,
16+
)
1417
from pymodbus.transport import CommType
1518
from pymodbus.utilities import ModbusTransactionState
1619

@@ -26,6 +29,10 @@ def fixture_rtu_framer():
2629
"""RTU framer."""
2730
return ModbusRtuFramer(ClientDecoder())
2831

32+
@pytest.fixture(name="socket_framer")
33+
def fixture_socket_framer():
34+
"""Socket framer."""
35+
return ModbusSocketFramer(ClientDecoder())
2936

3037
@pytest.fixture(name="ascii_framer")
3138
def fixture_ascii_framer():
@@ -359,3 +366,24 @@ def test_decode_ascii_data(ascii_framer, data):
359366
assert data.get("fcode") == 1
360367
else:
361368
assert not data
369+
370+
def test_recv_split_packet():
371+
"""Test receive packet."""
372+
response_ok = False
373+
374+
def _handle_response(_reply):
375+
"""Handle response."""
376+
nonlocal response_ok
377+
response_ok = True
378+
379+
message = bytearray(b"\x00\x01\x00\x00\x00\x0b\x01\x03\x08\x00\xb5\x12\x2f\x37\x21\x00\x03")
380+
for i in range(0, len(message)):
381+
part1 = message[:i]
382+
part2 = message[i:]
383+
response_ok = False
384+
framer = ModbusSocketFramer(ClientDecoder())
385+
if i:
386+
framer.processIncomingPacket(part1, _handle_response, slave=0)
387+
assert not response_ok, "Response should not be accepted"
388+
framer.processIncomingPacket(part2, _handle_response, slave=0)
389+
assert response_ok, "Response is valid, but not accepted"

0 commit comments

Comments
 (0)