Skip to content

Commit 28448cc

Browse files
committed
Simplify some BinaryPayload pack operations
1 parent a1696f7 commit 28448cc

File tree

2 files changed

+50
-39
lines changed

2 files changed

+50
-39
lines changed

pymodbus/payload.py

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"BinaryPayloadDecoder",
1212
]
1313

14+
from array import array
15+
1416
# pylint: disable=missing-type-doc
1517
from struct import pack, unpack
1618

@@ -19,13 +21,11 @@
1921
from pymodbus.logging import Log
2022
from pymodbus.utilities import (
2123
pack_bitstring,
24+
system_endian,
2225
unpack_bitstring,
2326
)
2427

2528

26-
WC = {"b": 1, "h": 2, "e": 2, "i": 4, "l": 4, "q": 8, "f": 4, "d": 8}
27-
28-
2929
class BinaryPayloadBuilder:
3030
"""A utility that helps build payload messages to be written with the various modbus messages.
3131
@@ -69,15 +69,14 @@ def _pack_words(self, fstring: str, value) -> bytes:
6969
:return:
7070
"""
7171
value = pack(f"!{fstring}", value)
72-
wordorder = WC.get(fstring.lower()) // 2 # type: ignore[operator]
73-
upperbyte = f"!{wordorder}H"
74-
payload = unpack(upperbyte, value)
75-
76-
if self._wordorder == Endian.LITTLE:
77-
payload = payload[::-1]
78-
79-
fstring = self._byteorder + "H"
80-
return b"".join(pack(fstring, word) for word in payload)
72+
if Endian.LITTLE in {self._byteorder, self._wordorder}:
73+
value = array("H", value)
74+
if self._byteorder == Endian.LITTLE:
75+
value.byteswap()
76+
if self._wordorder == Endian.LITTLE:
77+
value.reverse()
78+
value = value.tobytes()
79+
return value
8180

8281
def encode(self) -> bytes:
8382
"""Get the payload buffer encoded in bytes."""
@@ -99,15 +98,14 @@ def to_registers(self):
9998
10099
:returns: The register layout to use as a block
101100
"""
102-
# fstring = self._byteorder+"H"
103-
fstring = "!H"
104-
payload = self.build()
105-
if self._repack:
106-
payload = [unpack(self._byteorder + "H", value)[0] for value in payload]
107-
else:
108-
payload = [unpack(fstring, value)[0] for value in payload]
109-
Log.debug("{}", payload)
110-
return payload
101+
payload = array("H", b''.join(self.build()))
102+
swap = system_endian() != Endian.BIG
103+
if self._repack and self._byteorder == system_endian():
104+
swap = False
105+
if swap:
106+
payload.byteswap()
107+
Log.debug("{}", payload.tolist())
108+
return payload.tolist()
111109

112110
def to_coils(self) -> list[bool]:
113111
"""Convert the payload buffer into a coil layout that can be used as a context block.
@@ -295,8 +293,10 @@ def fromRegisters(
295293
"""
296294
Log.debug("{}", registers)
297295
if isinstance(registers, list): # repack into flat binary
298-
payload = b"".join(pack("!H", x) for x in registers)
299-
return cls(payload, byteorder, wordorder)
296+
payload = array("H", registers)
297+
if system_endian() != Endian.BIG:
298+
payload.byteswap()
299+
return cls(payload.tobytes(), byteorder, wordorder)
300300
raise ParameterException("Invalid collection of registers supplied")
301301

302302
@classmethod
@@ -324,7 +324,7 @@ def fromCoils(
324324
return cls(payload, byteorder)
325325
raise ParameterException("Invalid collection of coils supplied")
326326

327-
def _unpack_words(self, fstring: str, handle) -> bytes:
327+
def _unpack_words(self, handle) -> bytes:
328328
"""Unpack words based on the word order and byte order.
329329
330330
# ---------------------------------------------- #
@@ -336,15 +336,14 @@ def _unpack_words(self, fstring: str, handle) -> bytes:
336336
:param handle: Value to be unpacked
337337
:return:
338338
"""
339-
wc_value = WC.get(fstring.lower()) // 2 # type: ignore[operator]
340-
handle = unpack(f"!{wc_value}H", handle)
341-
if self._wordorder == Endian.LITTLE:
342-
handle = list(reversed(handle))
343-
344-
# Repack as unsigned Integer
345-
handle = [pack(self._byteorder + "H", p) for p in handle]
339+
if Endian.LITTLE in {self._byteorder, self._wordorder}:
340+
handle = array("H", handle)
341+
if self._byteorder == Endian.LITTLE:
342+
handle.byteswap()
343+
if self._wordorder == Endian.LITTLE:
344+
handle.reverse()
345+
handle = handle.tobytes()
346346
Log.debug("handle: {}", handle)
347-
handle = b"".join(handle)
348347
return handle
349348

350349
def reset(self):
@@ -377,15 +376,15 @@ def decode_32bit_uint(self):
377376
self._pointer += 4
378377
fstring = "I"
379378
handle = self._payload[self._pointer - 4 : self._pointer]
380-
handle = self._unpack_words(fstring, handle)
379+
handle = self._unpack_words(handle)
381380
return unpack("!" + fstring, handle)[0]
382381

383382
def decode_64bit_uint(self):
384383
"""Decode a 64 bit unsigned int from the buffer."""
385384
self._pointer += 8
386385
fstring = "Q"
387386
handle = self._payload[self._pointer - 8 : self._pointer]
388-
handle = self._unpack_words(fstring, handle)
387+
handle = self._unpack_words(handle)
389388
return unpack("!" + fstring, handle)[0]
390389

391390
def decode_8bit_int(self):
@@ -407,39 +406,39 @@ def decode_32bit_int(self):
407406
self._pointer += 4
408407
fstring = "i"
409408
handle = self._payload[self._pointer - 4 : self._pointer]
410-
handle = self._unpack_words(fstring, handle)
409+
handle = self._unpack_words(handle)
411410
return unpack("!" + fstring, handle)[0]
412411

413412
def decode_64bit_int(self):
414413
"""Decode a 64 bit signed int from the buffer."""
415414
self._pointer += 8
416415
fstring = "q"
417416
handle = self._payload[self._pointer - 8 : self._pointer]
418-
handle = self._unpack_words(fstring, handle)
417+
handle = self._unpack_words(handle)
419418
return unpack("!" + fstring, handle)[0]
420419

421420
def decode_16bit_float(self):
422421
"""Decode a 16 bit float from the buffer."""
423422
self._pointer += 2
424423
fstring = "e"
425424
handle = self._payload[self._pointer - 2 : self._pointer]
426-
handle = self._unpack_words(fstring, handle)
425+
handle = self._unpack_words(handle)
427426
return unpack("!" + fstring, handle)[0]
428427

429428
def decode_32bit_float(self):
430429
"""Decode a 32 bit float from the buffer."""
431430
self._pointer += 4
432431
fstring = "f"
433432
handle = self._payload[self._pointer - 4 : self._pointer]
434-
handle = self._unpack_words(fstring, handle)
433+
handle = self._unpack_words(handle)
435434
return unpack("!" + fstring, handle)[0]
436435

437436
def decode_64bit_float(self):
438437
"""Decode a 64 bit float(double) from the buffer."""
439438
self._pointer += 8
440439
fstring = "d"
441440
handle = self._payload[self._pointer - 8 : self._pointer]
442-
handle = self._unpack_words(fstring, handle)
441+
handle = self._unpack_words(handle)
443442
return unpack("!" + fstring, handle)[0]
444443

445444
def decode_string(self, size=1):

pymodbus/utilities.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111
"unpack_bitstring",
1212
"default",
1313
"rtuFrameSize",
14+
"system_endian",
1415
]
1516

1617
# pylint: disable=missing-type-doc
1718
import struct
19+
import sys
20+
21+
from pymodbus.constants import Endian
1822

1923

2024
class ModbusTransactionState: # pylint: disable=too-few-public-methods
@@ -182,3 +186,11 @@ def hexlify_packets(packet):
182186
if not packet:
183187
return ""
184188
return " ".join([hex(int(x)) for x in packet])
189+
190+
191+
def system_endian():
192+
"""Get system endianness.
193+
194+
:return: Endian of system
195+
"""
196+
return Endian.LITTLE if sys.byteorder == "little" else Endian.BIG

0 commit comments

Comments
 (0)