Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert endianness #2506

Merged
merged 10 commits into from
Dec 18, 2024
24 changes: 19 additions & 5 deletions pymodbus/client/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import struct
from abc import abstractmethod
from enum import Enum
from typing import Generic, TypeVar
from typing import Generic, TypeVar, Literal

import pymodbus.pdu.bit_message as pdu_bit
import pymodbus.pdu.diag_message as pdu_diag
Expand Down Expand Up @@ -695,18 +695,25 @@ class DATATYPE(Enum):

@classmethod
def convert_from_registers(
cls, registers: list[int], data_type: DATATYPE
cls, registers: list[int], data_type: DATATYPE, byte_order: Literal["big", "little"] = "big",
word_order: Literal["big", "little"] = "big"
) -> int | float | str | list[bool]:
"""Convert registers to int/float/str.

:param registers: list of registers received from e.g. read_holding_registers()
:param data_type: data type to convert to
:param byte_order: Literal[big, small] order of to bytes in 16 bit register encoding (ALMOST always big)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Byte order is fixed by the standard.

(default is big)
:param word_order: Literal[big, small] order of words (registers) when encoding in multiple registers
(default is big)
:returns: int, float, str or list[bool] depending on "data_type"
:raises ModbusException: when size of registers is not 1, 2 or 4
"""
byte_list = bytearray()
for x in registers:
byte_list.extend(int.to_bytes(x, 2, "big"))
byte_list.extend(int.to_bytes(x, 2, byte_order))
if word_order == "little":
byte_list = byte_list[::-1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not convert the order of all registers.

if data_type == cls.DATATYPE.STRING:
# remove trailing null bytes
trailing_nulls_begin = len(byte_list)
Expand All @@ -725,15 +732,22 @@ def convert_from_registers(

@classmethod
def convert_to_registers(
cls, value: int | float | str | list[bool], data_type: DATATYPE
cls, value: int | float | str | list[bool], data_type: DATATYPE, byte_order: Literal["big", "little"] = "big",
word_order: Literal["big", "little"] = "big"
) -> list[int]:
"""Convert int/float/str to registers (16/32/64 bit).

:param value: value to be converted
:param data_type: data type to be encoded as registers
:param byte_order: Literal[big, small] order of to bytes in 16 bit register encoding (ALMOST always big)
(default is big)
:param word_order: Literal[big, small] order of words (registers) when encoding in multiple registers
(default is big)
:returns: List of registers, can be used directly in e.g. write_registers()
:raises TypeError: when there is a mismatch between data_type and value
"""
if word_order == "little" and isinstance(value, list):
value = value[::-1]
if data_type == cls.DATATYPE.BITS:
if not isinstance(value, list):
raise TypeError(f"Value should be string but is {type(value)}.")
Expand All @@ -749,7 +763,7 @@ def convert_to_registers(
else:
byte_list = struct.pack(f">{data_type.value[0]}", value)
regs = [
int.from_bytes(byte_list[x : x + 2], "big")
int.from_bytes(byte_list[x : x + 2], byte_order)
for x in range(0, len(byte_list), 2)
]
return regs
Loading