Skip to content

Commit 9cf0724

Browse files
authored
Update framers to ease message integration (only decode/encode) (#2064)
1 parent 7c2aa39 commit 9cf0724

11 files changed

+2487
-1136
lines changed

pymodbus/framer/ascii_framer.py

+28-65
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ def __init__(self, decoder, client=None):
4242
self._start = b":"
4343
self._end = b"\r\n"
4444

45-
# ----------------------------------------------------------------------- #
46-
# Private Helper Functions
47-
# ----------------------------------------------------------------------- #
48-
def _process(self, callback, error=False):
49-
"""Process incoming packets irrespective error condition."""
50-
5145
def decode_data(self, data):
5246
"""Decode data."""
5347
if len(data) > 1:
@@ -56,77 +50,46 @@ def decode_data(self, data):
5650
return {"slave": uid, "fcode": fcode}
5751
return {}
5852

59-
def checkFrame(self):
60-
"""Check and decode the next frame.
61-
62-
:returns: True if we successful, False otherwise
63-
"""
64-
start = self._buffer.find(self._start)
65-
if start == -1:
66-
return False
67-
if start > 0: # go ahead and skip old bad data
68-
self._buffer = self._buffer[start:]
69-
start = 0
70-
71-
if (end := self._buffer.find(self._end)) != -1:
72-
self._header["len"] = end
73-
self._header["uid"] = int(self._buffer[1:3], 16)
74-
self._header["lrc"] = int(self._buffer[end - 2 : end], 16)
75-
data = a2b_hex(self._buffer[start + 1 : end - 2])
76-
return MessageAscii.check_LRC(data, self._header["lrc"])
77-
return False
78-
79-
def advanceFrame(self):
80-
"""Skip over the current framed message.
81-
82-
This allows us to skip over the current message after we have processed
83-
it or determined that it contains an error. It also has to reset the
84-
current frame header handle
85-
"""
86-
self._buffer = self._buffer[self._header["len"] + 2 :]
87-
self._header = {"lrc": "0000", "len": 0, "uid": 0x00}
88-
89-
def isFrameReady(self):
90-
"""Check if we should continue decode logic.
91-
92-
This is meant to be used in a while loop in the decoding phase to let
93-
the decoder know that there is still data in the buffer.
94-
95-
:returns: True if ready, False otherwise
96-
"""
97-
return len(self._buffer) > 1
98-
99-
def getFrame(self):
100-
"""Get the next frame from the buffer.
101-
102-
:returns: The frame data or ""
103-
"""
104-
start = self._hsize + 1
105-
end = self._header["len"] - 2
106-
buffer = self._buffer[start:end]
107-
if end > 0:
108-
return a2b_hex(buffer)
109-
return b""
110-
111-
# ----------------------------------------------------------------------- #
112-
# Public Member Functions
113-
# ----------------------------------------------------------------------- #
11453
def frameProcessIncomingPacket(self, single, callback, slave, _tid=None, **kwargs):
11554
"""Process new packet pattern."""
116-
while self.isFrameReady():
117-
if not self.checkFrame():
55+
def check_frame(self):
56+
"""Check and decode the next frame."""
57+
start = self._buffer.find(self._start)
58+
if start == -1:
59+
return False
60+
if start > 0: # go ahead and skip old bad data
61+
self._buffer = self._buffer[start:]
62+
start = 0
63+
64+
if (end := self._buffer.find(self._end)) != -1:
65+
self._header["len"] = end
66+
self._header["uid"] = int(self._buffer[1:3], 16)
67+
self._header["lrc"] = int(self._buffer[end - 2 : end], 16)
68+
data = a2b_hex(self._buffer[start + 1 : end - 2])
69+
return MessageAscii.check_LRC(data, self._header["lrc"])
70+
return False
71+
72+
while len(self._buffer) > 1:
73+
if not check_frame(self):
11874
break
11975
if not self._validate_slave_id(slave, single):
12076
header_txt = self._header["uid"]
12177
Log.error("Not a valid slave id - {}, ignoring!!", header_txt)
12278
self.resetFrame()
12379
continue
12480

125-
frame = self.getFrame()
81+
start = self._hsize + 1
82+
end = self._header["len"] - 2
83+
buffer = self._buffer[start:end]
84+
if end > 0:
85+
frame = a2b_hex(buffer)
86+
else:
87+
frame = b""
12688
if (result := self.decoder.decode(frame)) is None:
12789
raise ModbusIOException("Unable to decode response")
12890
self.populateResult(result)
129-
self.advanceFrame()
91+
self._buffer = self._buffer[self._header["len"] + 2 :]
92+
self._header = {"lrc": "0000", "len": 0, "uid": 0x00}
13093
callback(result) # defer this
13194

13295
def buildPacket(self, message):

pymodbus/framer/binary_framer.py

+28-65
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,6 @@ def __init__(self, decoder, client=None):
5353
self._end = b"\x7d" # }
5454
self._repeat = [b"}"[0], b"{"[0]] # python3 hack
5555

56-
# ----------------------------------------------------------------------- #
57-
# Private Helper Functions
58-
# ----------------------------------------------------------------------- #
59-
def _process(self, callback, error=False):
60-
"""Process incoming packets irrespective error condition."""
61-
6256
def decode_data(self, data):
6357
"""Decode data."""
6458
if len(data) > self._hsize:
@@ -67,76 +61,45 @@ def decode_data(self, data):
6761
return {"slave": uid, "fcode": fcode}
6862
return {}
6963

70-
def checkFrame(self) -> bool:
71-
"""Check and decode the next frame.
72-
73-
:returns: True if we are successful, False otherwise
74-
"""
75-
start = self._buffer.find(self._start)
76-
if start == -1:
77-
return False
78-
if start > 0: # go ahead and skip old bad data
79-
self._buffer = self._buffer[start:]
80-
81-
if (end := self._buffer.find(self._end)) != -1:
82-
self._header["len"] = end
83-
self._header["uid"] = struct.unpack(">B", self._buffer[1:2])[0]
84-
self._header["crc"] = struct.unpack(">H", self._buffer[end - 2 : end])[0]
85-
data = self._buffer[1 : end - 2]
86-
return checkCRC(data, self._header["crc"])
87-
return False
88-
89-
def advanceFrame(self) -> None:
90-
"""Skip over the current framed message.
91-
92-
This allows us to skip over the current message after we have processed
93-
it or determined that it contains an error. It also has to reset the
94-
current frame header handle
95-
"""
96-
self._buffer = self._buffer[self._header["len"] + 2 :]
97-
self._header = {"crc": 0x0000, "len": 0, "uid": 0x00}
98-
99-
def isFrameReady(self) -> bool:
100-
"""Check if we should continue decode logic.
101-
102-
This is meant to be used in a while loop in the decoding phase to let
103-
the decoder know that there is still data in the buffer.
104-
105-
:returns: True if ready, False otherwise
106-
"""
107-
return len(self._buffer) > 1
108-
109-
def getFrame(self):
110-
"""Get the next frame from the buffer.
111-
112-
:returns: The frame data or ""
113-
"""
114-
start = self._hsize + 1
115-
end = self._header["len"] - 2
116-
buffer = self._buffer[start:end]
117-
if end > 0:
118-
return buffer
119-
return b""
120-
121-
# ----------------------------------------------------------------------- #
122-
# Public Member Functions
123-
# ----------------------------------------------------------------------- #
12464
def frameProcessIncomingPacket(self, single, callback, slave, _tid=None, **kwargs):
12565
"""Process new packet pattern."""
126-
while self.isFrameReady():
127-
if not self.checkFrame():
66+
def check_frame(self) -> bool:
67+
"""Check and decode the next frame."""
68+
start = self._buffer.find(self._start)
69+
if start == -1:
70+
return False
71+
if start > 0: # go ahead and skip old bad data
72+
self._buffer = self._buffer[start:]
73+
74+
if (end := self._buffer.find(self._end)) != -1:
75+
self._header["len"] = end
76+
self._header["uid"] = struct.unpack(">B", self._buffer[1:2])[0]
77+
self._header["crc"] = struct.unpack(">H", self._buffer[end - 2 : end])[0]
78+
data = self._buffer[1 : end - 2]
79+
return checkCRC(data, self._header["crc"])
80+
return False
81+
82+
while len(self._buffer) > 1:
83+
if not check_frame(self):
12884
Log.debug("Frame check failed, ignoring!!")
129-
self.resetFrame()
13085
break
13186
if not self._validate_slave_id(slave, single):
13287
header_txt = self._header["uid"]
13388
Log.debug("Not a valid slave id - {}, ignoring!!", header_txt)
13489
self.resetFrame()
13590
break
136-
if (result := self.decoder.decode(self.getFrame())) is None:
91+
start = self._hsize + 1
92+
end = self._header["len"] - 2
93+
buffer = self._buffer[start:end]
94+
if end > 0:
95+
frame = buffer
96+
else:
97+
frame = b""
98+
if (result := self.decoder.decode(frame)) is None:
13799
raise ModbusIOException("Unable to decode response")
138100
self.populateResult(result)
139-
self.advanceFrame()
101+
self._buffer = self._buffer[self._header["len"] + 2 :]
102+
self._header = {"crc": 0x0000, "len": 0, "uid": 0x00}
140103
callback(result) # defer or push to a thread?
141104

142105
def buildPacket(self, message):

0 commit comments

Comments
 (0)