0
# Data Utilities
1
2
Utility functions for data type conversion, bit manipulation, IEEE 754 floating point encoding/decoding, and protocol validation. These functions are essential for working with Modbus data types and converting between different representations.
3
4
## Capabilities
5
6
### Data Type Conversion
7
8
Functions for converting between different data representations commonly used in industrial automation.
9
10
```python { .api }
11
def word_list_to_long(val_list, big_endian=True, long_long=False):
12
"""
13
Convert word list to long integers.
14
15
Parameters:
16
val_list (list[int]): List of 16-bit words (0-65535)
17
big_endian (bool): Byte order (default: True)
18
long_long (bool): Use 64-bit integers instead of 32-bit (default: False)
19
20
Returns:
21
list[int]: List of 32-bit or 64-bit integers
22
"""
23
24
def long_list_to_word(val_list, big_endian=True, long_long=False):
25
"""
26
Convert long integers to word list.
27
28
Parameters:
29
val_list (list[int]): List of 32-bit or 64-bit integers
30
big_endian (bool): Byte order (default: True)
31
long_long (bool): Input integers are 64-bit (default: False)
32
33
Returns:
34
list[int]: List of 16-bit words (0-65535)
35
"""
36
37
def get_2comp(val_int, val_size=16):
38
"""
39
Convert unsigned integer to two's complement signed integer.
40
41
Parameters:
42
val_int (int): Unsigned integer value
43
val_size (int): Bit size (8, 16, 32, or 64, default: 16)
44
45
Returns:
46
int: Signed integer in two's complement format
47
"""
48
49
def get_list_2comp(val_list, val_size=16):
50
"""
51
Convert list of unsigned integers to two's complement signed integers.
52
53
Parameters:
54
val_list (list[int]): List of unsigned integer values
55
val_size (int): Bit size for each value (default: 16)
56
57
Returns:
58
list[int]: List of signed integers in two's complement format
59
"""
60
```
61
62
### IEEE 754 Floating Point
63
64
Functions for encoding and decoding IEEE 754 floating point numbers commonly used in Modbus applications.
65
66
```python { .api }
67
def decode_ieee(val_int, double=False):
68
"""
69
Decode IEEE 754 floating point from integer representation.
70
71
Parameters:
72
val_int (int): Integer representation of IEEE 754 float
73
double (bool): Use double precision (64-bit) instead of single (32-bit, default: False)
74
75
Returns:
76
float: Decoded floating point value
77
"""
78
79
def encode_ieee(val_float, double=False):
80
"""
81
Encode floating point value as IEEE 754 integer representation.
82
83
Parameters:
84
val_float (float): Floating point value to encode
85
double (bool): Use double precision (64-bit) instead of single (32-bit, default: False)
86
87
Returns:
88
int: IEEE 754 integer representation
89
"""
90
```
91
92
### Bit Manipulation
93
94
Functions for working with individual bits within integers.
95
96
```python { .api }
97
def get_bits_from_int(val_int, val_size=16):
98
"""
99
Extract bit list from integer value.
100
101
Parameters:
102
val_int (int): Integer value
103
val_size (int): Number of bits to extract (default: 16)
104
105
Returns:
106
list[bool]: List of bit values (LSB first)
107
"""
108
109
def test_bit(value, offset):
110
"""
111
Test if a specific bit is set in an integer.
112
113
Parameters:
114
value (int): Integer value to test
115
offset (int): Bit position (0 = LSB)
116
117
Returns:
118
bool: True if bit is set, False otherwise
119
"""
120
121
def set_bit(value, offset):
122
"""
123
Set a specific bit in an integer.
124
125
Parameters:
126
value (int): Original integer value
127
offset (int): Bit position to set (0 = LSB)
128
129
Returns:
130
int: Integer with specified bit set
131
"""
132
133
def reset_bit(value, offset):
134
"""
135
Clear a specific bit in an integer.
136
137
Parameters:
138
value (int): Original integer value
139
offset (int): Bit position to clear (0 = LSB)
140
141
Returns:
142
int: Integer with specified bit cleared
143
"""
144
145
def toggle_bit(value, offset):
146
"""
147
Toggle a specific bit in an integer.
148
149
Parameters:
150
value (int): Original integer value
151
offset (int): Bit position to toggle (0 = LSB)
152
153
Returns:
154
int: Integer with specified bit toggled
155
"""
156
157
def byte_length(bit_length):
158
"""
159
Calculate the number of bytes needed to store a given number of bits.
160
161
Parameters:
162
bit_length (int): Number of bits
163
164
Returns:
165
int: Number of bytes needed
166
"""
167
```
168
169
### Protocol Utilities
170
171
Functions for protocol validation and checksum calculation.
172
173
```python { .api }
174
def crc16(frame):
175
"""
176
Calculate CRC16 checksum for Modbus RTU frames.
177
178
Parameters:
179
frame (bytes): Frame data to calculate checksum for
180
181
Returns:
182
int: CRC16 checksum value
183
"""
184
185
def valid_host(host_str):
186
"""
187
Validate hostname or IP address format.
188
189
Parameters:
190
host_str (str): Hostname or IP address string
191
192
Returns:
193
bool: True if format is valid, False otherwise
194
"""
195
```
196
197
## Usage Examples
198
199
### Working with Floating Point Values
200
201
```python
202
from pyModbusTCP.utils import encode_ieee, decode_ieee, long_list_to_word, word_list_to_long
203
from pyModbusTCP.client import ModbusClient
204
205
client = ModbusClient(host="192.168.1.100", auto_open=True)
206
207
# Write a floating point value
208
temperature = 23.45
209
encoded_temp = encode_ieee(temperature)
210
register_list = long_list_to_word([encoded_temp], big_endian=True)
211
client.write_multiple_registers(100, register_list)
212
213
# Read and decode floating point value
214
registers = client.read_holding_registers(100, 2)
215
if registers:
216
temp_as_int = word_list_to_long(registers, big_endian=True)[0]
217
decoded_temp = decode_ieee(temp_as_int)
218
print(f"Temperature: {decoded_temp}°C")
219
```
220
221
### Bit Manipulation Example
222
223
```python
224
from pyModbusTCP.utils import get_bits_from_int, set_bit, reset_bit, test_bit
225
226
# Extract individual bits from a register value
227
register_value = 0b1101001010110011 # 54451
228
bits = get_bits_from_int(register_value, 16)
229
print(f"Bits: {bits}") # [True, True, False, False, ...]
230
231
# Manipulate individual bits
232
value = 0b00001111 # 15
233
value = set_bit(value, 4) # Set bit 4: 0b00011111 (31)
234
value = reset_bit(value, 0) # Clear bit 0: 0b00011110 (30)
235
is_set = test_bit(value, 1) # Test bit 1: True
236
print(f"Final value: {value}, bit 1 set: {is_set}")
237
```
238
239
### Data Type Conversion Example
240
241
```python
242
from pyModbusTCP.utils import word_list_to_long, long_list_to_word, get_2comp
243
244
# Convert 32-bit integers to register pairs
245
values_32bit = [1234567890, -987654321]
246
registers = long_list_to_word(values_32bit, big_endian=True)
247
print(f"Registers: {registers}") # [18838, 722, 50042, 35023]
248
249
# Convert back to 32-bit integers
250
converted_back = word_list_to_long(registers, big_endian=True)
251
print(f"Converted back: {converted_back}") # [1234567890, 4265312975]
252
253
# Handle signed values with two's complement
254
signed_values = [get_2comp(val, 32) for val in converted_back]
255
print(f"Signed values: {signed_values}") # [1234567890, -987654321]
256
```
257
258
### Working with Double Precision Floats
259
260
```python
261
from pyModbusTCP.utils import encode_ieee, decode_ieee, long_list_to_word, word_list_to_long
262
263
# Encode double precision float (64-bit)
264
precise_value = 3.141592653589793
265
encoded = encode_ieee(precise_value, double=True)
266
print(f"Encoded: {encoded}")
267
268
# Convert to 4 registers for Modbus transmission
269
registers = long_list_to_word([encoded], big_endian=True, long_long=True)
270
print(f"Registers: {registers}") # List of 4 registers
271
272
# Decode back to double precision
273
combined = word_list_to_long(registers, big_endian=True, long_long=True)[0]
274
decoded = decode_ieee(combined, double=True)
275
print(f"Decoded: {decoded}") # 3.141592653589793
276
```
277
278
### Protocol Validation
279
280
```python
281
from pyModbusTCP.utils import valid_host, crc16
282
283
# Validate host addresses
284
hosts = ["192.168.1.1", "localhost", "invalid@host", "::1", "example.com"]
285
for host in hosts:
286
is_valid = valid_host(host)
287
print(f"{host}: {'valid' if is_valid else 'invalid'}")
288
289
# Calculate CRC16 for Modbus RTU frame
290
frame = b'\x01\x03\x00\x00\x00\x0A' # Read holding registers request
291
checksum = crc16(frame)
292
print(f"CRC16: 0x{checksum:04X}")
293
294
# Complete frame with CRC
295
full_frame = frame + checksum.to_bytes(2, 'little')
296
print(f"Complete frame: {full_frame.hex()}")
297
```
298
299
### Bit Pattern Analysis
300
301
```python
302
from pyModbusTCP.utils import get_bits_from_int, byte_length
303
304
# Analyze status register bits
305
status_register = 0b1010001100110101 # 41269
306
status_bits = get_bits_from_int(status_register, 16)
307
308
# Define bit meanings
309
bit_names = [
310
"System Ready", "Error Active", "Manual Mode", "Auto Mode",
311
"Motor 1 Run", "Motor 2 Run", "Pump Active", "Heater On",
312
"Alarm 1", "Alarm 2", "Service Required", "Door Open",
313
"Temperature OK", "Pressure OK", "Flow OK", "Reserved"
314
]
315
316
print("System Status:")
317
for i, (name, state) in enumerate(zip(bit_names, status_bits)):
318
print(f" Bit {i:2d} - {name:15s}: {'ON' if state else 'OFF'}")
319
320
# Calculate storage requirements
321
num_status_bits = 16
322
bytes_needed = byte_length(num_status_bits)
323
print(f"Storage required: {bytes_needed} bytes for {num_status_bits} bits")
324
```
325
326
## Integration with Client/Server
327
328
These utility functions integrate seamlessly with the client and server operations:
329
330
```python
331
from pyModbusTCP.client import ModbusClient
332
from pyModbusTCP.server import ModbusServer, DataBank
333
from pyModbusTCP.utils import encode_ieee, decode_ieee, word_list_to_long, long_list_to_word
334
335
# Server side: Store floating point values
336
data_bank = DataBank()
337
temp_values = [23.45, 24.67, 22.89] # Temperature readings
338
339
for i, temp in enumerate(temp_values):
340
encoded = encode_ieee(temp)
341
registers = long_list_to_word([encoded], big_endian=True)
342
data_bank.set_holding_registers(i * 2, registers) # 2 registers per float
343
344
server = ModbusServer(data_bank=data_bank)
345
server.start()
346
347
# Client side: Read and decode floating point values
348
client = ModbusClient(auto_open=True)
349
temperatures = []
350
351
for i in range(3):
352
registers = client.read_holding_registers(i * 2, 2)
353
if registers:
354
temp_int = word_list_to_long(registers, big_endian=True)[0]
355
temperature = decode_ieee(temp_int)
356
temperatures.append(temperature)
357
358
print(f"Temperatures: {temperatures}")
359
```