Skip to content

Commit 010283c

Browse files
Breinajaniversen
authored andcommitted
Parameterize string encoding in convert_to_registers and convert_from_registers (#2558)
1 parent f9f0299 commit 010283c

File tree

2 files changed

+37
-21
lines changed

2 files changed

+37
-21
lines changed

pymodbus/client/mixin.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -695,15 +695,17 @@ class DATATYPE(Enum):
695695

696696
@classmethod
697697
def convert_from_registers(
698-
cls, registers: list[int], data_type: DATATYPE, word_order: Literal["big", "little"] = "big"
698+
cls, registers: list[int], data_type: DATATYPE, word_order: Literal["big", "little"] = "big", string_encoding: str = "utf-8"
699699
) -> int | float | str | list[bool] | list[int] | list[float]:
700700
"""Convert registers to int/float/str.
701701
702702
:param registers: list of registers received from e.g. read_holding_registers()
703703
:param data_type: data type to convert to
704704
:param word_order: "big"/"little" order of words/registers
705+
:param string_encoding: The encoding with which to decode the bytearray, only used when data_type=DATATYPE.STRING
705706
:returns: scalar or array of "data_type"
706707
:raises ModbusException: when size of registers is not a multiple of data_type
708+
:raises ParameterException: when the specified string encoding is not supported
707709
"""
708710
if not (data_len := data_type.value[1]):
709711
byte_list = bytearray()
@@ -716,7 +718,7 @@ def convert_from_registers(
716718
while trailing_nulls_begin > 0 and not byte_list[trailing_nulls_begin - 1]:
717719
trailing_nulls_begin -= 1
718720
byte_list = byte_list[:trailing_nulls_begin]
719-
return byte_list.decode("utf-8")
721+
return byte_list.decode(string_encoding)
720722
return unpack_bitstring(byte_list)
721723
if (reg_len := len(registers)) % data_len:
722724
raise ModbusException(
@@ -736,15 +738,17 @@ def convert_from_registers(
736738

737739
@classmethod
738740
def convert_to_registers(
739-
cls, value: int | float | str | list[bool] | list[int] | list[float] , data_type: DATATYPE, word_order: Literal["big", "little"] = "big"
741+
cls, value: int | float | str | list[bool] | list[int] | list[float], data_type: DATATYPE, word_order: Literal["big", "little"] = "big", string_encoding: str = "utf-8"
740742
) -> list[int]:
741743
"""Convert int/float/str to registers (16/32/64 bit).
742744
743745
:param value: value to be converted
744746
:param data_type: data type to convert from
745747
:param word_order: "big"/"little" order of words/registers
748+
:param string_encoding: The encoding with which to encode the bytearray, only used when data_type=DATATYPE.STRING
746749
:returns: List of registers, can be used directly in e.g. write_registers()
747750
:raises TypeError: when there is a mismatch between data_type and value
751+
:raises ParameterException: when the specified string encoding is not supported
748752
"""
749753
if data_type == cls.DATATYPE.BITS:
750754
if not isinstance(value, list):
@@ -755,7 +759,7 @@ def convert_to_registers(
755759
elif data_type == cls.DATATYPE.STRING:
756760
if not isinstance(value, str):
757761
raise TypeError(f"Value should be string but is {type(value)}.")
758-
byte_list = value.encode()
762+
byte_list = value.encode(string_encoding)
759763
if len(byte_list) % 2:
760764
byte_list += b"\x00"
761765
else:

test/client/test_client.py

+29-17
Original file line numberDiff line numberDiff line change
@@ -97,73 +97,85 @@ def fake_execute(_self, _no_response_expected, request):
9797

9898
@pytest.mark.parametrize(("word_order"), ["big", "little", None])
9999
@pytest.mark.parametrize(
100-
("datatype", "value", "registers"),
100+
("datatype", "value", "registers", "string_encoding"),
101101
[
102-
(ModbusClientMixin.DATATYPE.STRING, "abcd", [0x6162, 0x6364]),
103-
(ModbusClientMixin.DATATYPE.STRING, "a", [0x6100]),
104-
(ModbusClientMixin.DATATYPE.UINT16, 27123, [0x69F3]),
105-
(ModbusClientMixin.DATATYPE.INT16, -27123, [0x960D]),
106-
(ModbusClientMixin.DATATYPE.UINT32, 27123, [0x0000, 0x69F3]),
107-
(ModbusClientMixin.DATATYPE.UINT32, 32145678, [0x01EA, 0x810E]),
108-
(ModbusClientMixin.DATATYPE.INT32, -32145678, [0xFE15, 0x7EF2]),
102+
(ModbusClientMixin.DATATYPE.STRING, "abcdÇ", [0x6162, 0x6364, 0xc387], "utf-8"),
103+
(ModbusClientMixin.DATATYPE.STRING, "abcdÇ", [0x6162, 0x6364, 0xc387], None),
104+
(ModbusClientMixin.DATATYPE.STRING, "abcdÇ", [0x6162, 0x6364, 0x8000], "cp437"),
105+
(ModbusClientMixin.DATATYPE.STRING, "a", [0x6100], None),
106+
(ModbusClientMixin.DATATYPE.UINT16, 27123, [0x69F3], None),
107+
(ModbusClientMixin.DATATYPE.INT16, -27123, [0x960D], None),
108+
(ModbusClientMixin.DATATYPE.UINT32, 27123, [0x0000, 0x69F3], None),
109+
(ModbusClientMixin.DATATYPE.UINT32, 32145678, [0x01EA, 0x810E], None),
110+
(ModbusClientMixin.DATATYPE.INT32, -32145678, [0xFE15, 0x7EF2], None),
109111
(
110112
ModbusClientMixin.DATATYPE.UINT64,
111113
1234567890123456789,
112114
[0x1122, 0x10F4, 0x7DE9, 0x8115],
115+
None,
113116
),
114117
(
115118
ModbusClientMixin.DATATYPE.INT64,
116119
-1234567890123456789,
117120
[0xEEDD, 0xEF0B, 0x8216, 0x7EEB],
121+
None,
118122
),
119-
(ModbusClientMixin.DATATYPE.FLOAT32, 27123.5, [0x46D3, 0xE700]),
120-
(ModbusClientMixin.DATATYPE.FLOAT32, 3.141592, [0x4049, 0x0FD8]),
121-
(ModbusClientMixin.DATATYPE.FLOAT32, -3.141592, [0xC049, 0x0FD8]),
122-
(ModbusClientMixin.DATATYPE.FLOAT64, 27123.5, [0x40DA, 0x7CE0, 0x0000, 0x0000]),
123+
(ModbusClientMixin.DATATYPE.FLOAT32, 27123.5, [0x46D3, 0xE700], None),
124+
(ModbusClientMixin.DATATYPE.FLOAT32, 3.141592, [0x4049, 0x0FD8], None),
125+
(ModbusClientMixin.DATATYPE.FLOAT32, -3.141592, [0xC049, 0x0FD8], None),
126+
(ModbusClientMixin.DATATYPE.FLOAT64, 27123.5, [0x40DA, 0x7CE0, 0x0000, 0x0000], None),
123127
(
124128
ModbusClientMixin.DATATYPE.FLOAT64,
125129
3.14159265358979,
126130
[0x4009, 0x21FB, 0x5444, 0x2D11],
131+
None,
127132
),
128133
(
129134
ModbusClientMixin.DATATYPE.FLOAT64,
130135
-3.14159265358979,
131136
[0xC009, 0x21FB, 0x5444, 0x2D11],
137+
None,
132138
),
133139
(
134140
ModbusClientMixin.DATATYPE.BITS,
135141
[True],
136142
[256],
143+
None,
137144
),
138145
(
139146
ModbusClientMixin.DATATYPE.BITS,
140147
[True, False, True],
141148
[1280],
149+
None,
142150
),
143151
(
144152
ModbusClientMixin.DATATYPE.BITS,
145153
[True, False, True] + [False] * 5 + [True],
146154
[1281],
155+
None,
147156
),
148157
(
149158
ModbusClientMixin.DATATYPE.BITS,
150159
[True, False, True] + [False] * 5 + [True] + [False] * 6 + [True],
151160
[1409],
161+
None,
152162
),
153163
(
154164
ModbusClientMixin.DATATYPE.BITS,
155165
[True, False, True] + [False] * 5 + [True] + [False] * 6 + [True] * 2,
156166
[1409, 256],
167+
None,
157168
),
158169
],
159170
)
160-
def test_client_mixin_convert(self, datatype, word_order, registers, value):
171+
def test_client_mixin_convert(self, datatype, word_order, registers, value, string_encoding):
161172
"""Test converter methods."""
162173
if word_order == "little":
163-
x = registers.copy()
164-
x.reverse()
165-
registers = x
166-
kwargs = {"word_order": word_order} if word_order else {}
174+
registers = list(reversed(registers))
175+
176+
kwargs = {**({"word_order": word_order} if word_order else {}),
177+
**({"string_encoding": string_encoding} if string_encoding else {})}
178+
167179
regs = ModbusClientMixin.convert_to_registers(value, datatype, **kwargs)
168180
assert regs == registers
169181
result = ModbusClientMixin.convert_from_registers(registers, datatype, **kwargs)

0 commit comments

Comments
 (0)