python-snap7 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_snap7-3.0.0.dist-info/METADATA +94 -0
- python_snap7-3.0.0.dist-info/RECORD +22 -0
- python_snap7-3.0.0.dist-info/WHEEL +5 -0
- python_snap7-3.0.0.dist-info/entry_points.txt +2 -0
- python_snap7-3.0.0.dist-info/licenses/LICENSE +20 -0
- python_snap7-3.0.0.dist-info/top_level.txt +1 -0
- snap7/__init__.py +34 -0
- snap7/client.py +1923 -0
- snap7/connection.py +456 -0
- snap7/datatypes.py +312 -0
- snap7/error.py +444 -0
- snap7/logo.py +222 -0
- snap7/partner.py +680 -0
- snap7/py.typed +0 -0
- snap7/s7protocol.py +1509 -0
- snap7/server/__init__.py +2681 -0
- snap7/server/__main__.py +41 -0
- snap7/type.py +391 -0
- snap7/util/__init__.py +108 -0
- snap7/util/db.py +789 -0
- snap7/util/getters.py +766 -0
- snap7/util/setters.py +733 -0
snap7/util/getters.py
ADDED
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
from datetime import timedelta, datetime, date
|
|
3
|
+
from typing import NoReturn
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
|
|
6
|
+
logger = getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_bool(bytearray_: bytearray, byte_index: int, bool_index: int) -> bool:
|
|
10
|
+
"""Get the boolean value from location in bytearray
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
bytearray_: buffer data.
|
|
14
|
+
byte_index: byte index to read from.
|
|
15
|
+
bool_index: bit index to read from.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
True if the bit is 1, else 0.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
>>> buffer = bytearray([0b00000001]) # Only one byte length
|
|
22
|
+
>>> get_bool(buffer, 0, 0) # The bit 0 starts at the right.
|
|
23
|
+
True
|
|
24
|
+
"""
|
|
25
|
+
index_value = 1 << bool_index
|
|
26
|
+
byte_value = bytearray_[byte_index]
|
|
27
|
+
current_value = byte_value & index_value
|
|
28
|
+
return current_value == index_value
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_byte(bytearray_: bytearray, byte_index: int) -> bytes:
|
|
32
|
+
"""Get byte value from bytearray.
|
|
33
|
+
|
|
34
|
+
Notes:
|
|
35
|
+
WORD 8bit 1bytes Decimal number unsigned B#(0) to B#(255) => 0 to 255
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
bytearray_: buffer to be read from.
|
|
39
|
+
byte_index: byte index to be read.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
value get from the byte index.
|
|
43
|
+
"""
|
|
44
|
+
data = bytearray_[byte_index : byte_index + 1]
|
|
45
|
+
data[0] = data[0] & 0xFF
|
|
46
|
+
packed = struct.pack("B", *data)
|
|
47
|
+
value: bytes = struct.unpack("B", packed)[0]
|
|
48
|
+
return value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_word(bytearray_: bytearray, byte_index: int) -> bytearray:
|
|
52
|
+
"""Get word value from bytearray.
|
|
53
|
+
|
|
54
|
+
Notes:
|
|
55
|
+
WORD 16bit 2bytes Decimal number unsigned B#(0,0) to B#(255,255) => 0 to 65535
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
bytearray_: buffer to get the word from.
|
|
59
|
+
byte_index: byte index from where start reading from.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Word value.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
>>> get_word(bytearray([0, 100]), 0)
|
|
66
|
+
100
|
|
67
|
+
"""
|
|
68
|
+
data = bytearray_[byte_index : byte_index + 2]
|
|
69
|
+
data[1] = data[1] & 0xFF
|
|
70
|
+
data[0] = data[0] & 0xFF
|
|
71
|
+
packed = struct.pack("2B", *data)
|
|
72
|
+
value: bytearray = struct.unpack(">H", packed)[0]
|
|
73
|
+
return value
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_int(bytearray_: bytearray, byte_index: int) -> int:
|
|
77
|
+
"""Get int value from bytearray.
|
|
78
|
+
|
|
79
|
+
Notes:
|
|
80
|
+
Datatype `int` in the PLC is represented in two bytes
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
bytearray_: buffer to read from.
|
|
84
|
+
byte_index: byte index to start reading from.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Value read.
|
|
88
|
+
|
|
89
|
+
Examples:
|
|
90
|
+
>>> get_int(bytearray([0, 255]), 0)
|
|
91
|
+
255
|
|
92
|
+
"""
|
|
93
|
+
data = bytearray_[byte_index : byte_index + 2]
|
|
94
|
+
data[1] = data[1] & 0xFF
|
|
95
|
+
data[0] = data[0] & 0xFF
|
|
96
|
+
packed = struct.pack("2B", *data)
|
|
97
|
+
value: int = struct.unpack(">h", packed)[0]
|
|
98
|
+
return value
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_uint(bytearray_: bytearray, byte_index: int) -> int:
|
|
102
|
+
"""Get unsigned int value from bytearray.
|
|
103
|
+
|
|
104
|
+
Notes:
|
|
105
|
+
Datatype `uint` in the PLC is represented in two bytes
|
|
106
|
+
Maximum posible value is 65535.
|
|
107
|
+
Lower posible value is 0.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
bytearray_: buffer to read from.
|
|
111
|
+
byte_index: byte index to start reading from.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Value read.
|
|
115
|
+
|
|
116
|
+
Examples:
|
|
117
|
+
>>> data = bytearray([255, 255])
|
|
118
|
+
>>> get_uint(data, 0)
|
|
119
|
+
65535
|
|
120
|
+
"""
|
|
121
|
+
return int(get_word(bytearray_, byte_index))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_real(bytearray_: bytearray, byte_index: int) -> float:
|
|
125
|
+
"""Get real value.
|
|
126
|
+
|
|
127
|
+
Notes:
|
|
128
|
+
Datatype `real` is represented in 4 bytes in the PLC.
|
|
129
|
+
The packed representation uses the `IEEE 754 binary32`.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
bytearray_: buffer to read from.
|
|
133
|
+
byte_index: byte index to reading from.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Real value.
|
|
137
|
+
|
|
138
|
+
Examples:
|
|
139
|
+
>>> data = bytearray(b'B\\xf6\\xa4Z')
|
|
140
|
+
>>> get_real(data, 0)
|
|
141
|
+
123.32099914550781
|
|
142
|
+
"""
|
|
143
|
+
x = bytearray_[byte_index : byte_index + 4]
|
|
144
|
+
real: float = struct.unpack(">f", struct.pack("4B", *x))[0]
|
|
145
|
+
return real
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_fstring(bytearray_: bytearray, byte_index: int, max_length: int, remove_padding: bool = True) -> str:
|
|
149
|
+
"""Parse space-padded fixed-length string from bytearray
|
|
150
|
+
|
|
151
|
+
Notes:
|
|
152
|
+
This function supports fixed-length ASCII strings, right-padded with spaces.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
bytearray_: buffer from where to get the string.
|
|
156
|
+
byte_index: byte index from where to start reading.
|
|
157
|
+
max_length: the maximum length of the string.
|
|
158
|
+
remove_padding: whether to remove the right-padding.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
String value.
|
|
162
|
+
|
|
163
|
+
Examples:
|
|
164
|
+
>>> data = [ord(letter) for letter in "hello world "]
|
|
165
|
+
>>> get_fstring(data, 0, 15)
|
|
166
|
+
'hello world'
|
|
167
|
+
>>> get_fstring(data, 0, 15, remove_padding=False)
|
|
168
|
+
'hello world '
|
|
169
|
+
"""
|
|
170
|
+
data = map(chr, bytearray_[byte_index : byte_index + max_length])
|
|
171
|
+
string = "".join(data)
|
|
172
|
+
|
|
173
|
+
if remove_padding:
|
|
174
|
+
return string.rstrip(" ")
|
|
175
|
+
else:
|
|
176
|
+
return string
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def get_string(bytearray_: bytearray, byte_index: int) -> str:
|
|
180
|
+
"""Parse string from bytearray
|
|
181
|
+
|
|
182
|
+
Notes:
|
|
183
|
+
The first byte of the buffer will contain the max size posible for a string.
|
|
184
|
+
The second byte contains the length of the string that contains.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
bytearray_: buffer from where to get the string.
|
|
188
|
+
byte_index: byte index from where to start reading.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
String value.
|
|
192
|
+
|
|
193
|
+
Examples:
|
|
194
|
+
>>> data = bytearray([254, len("hello world")] + [ord(l) for letter in "hello world"])
|
|
195
|
+
>>> get_string(data, 0)
|
|
196
|
+
'hello world'
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
str_length = int(bytearray_[byte_index + 1])
|
|
200
|
+
max_string_size = int(bytearray_[byte_index])
|
|
201
|
+
|
|
202
|
+
if str_length > max_string_size or max_string_size > 254:
|
|
203
|
+
logger.error("The string is too big for the size encountered in specification")
|
|
204
|
+
logger.error("WRONG SIZED STRING ENCOUNTERED")
|
|
205
|
+
raise TypeError(
|
|
206
|
+
"String contains {str_length} chars, but max. {max_string_size} chars are expected or is "
|
|
207
|
+
"larger than 254. Bytearray doesn't seem to be a valid string."
|
|
208
|
+
)
|
|
209
|
+
data = map(chr, bytearray_[byte_index + 2 : byte_index + 2 + str_length])
|
|
210
|
+
return "".join(data)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_dword(bytearray_: bytearray, byte_index: int) -> int:
|
|
214
|
+
"""Gets the dword from the buffer.
|
|
215
|
+
|
|
216
|
+
Notes:
|
|
217
|
+
Datatype `dword` consists in 8 bytes in the PLC.
|
|
218
|
+
The maximum value posible is `4294967295`
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
bytearray_: buffer to read.
|
|
222
|
+
byte_index: byte index from where to start reading.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Value read.
|
|
226
|
+
|
|
227
|
+
Examples:
|
|
228
|
+
>>> data = bytearray(8)
|
|
229
|
+
>>> data[:] = b"\\x12\\x34\\xAB\\xCD"
|
|
230
|
+
>>> get_dword(data, 0)
|
|
231
|
+
4294967295
|
|
232
|
+
"""
|
|
233
|
+
data = bytearray_[byte_index : byte_index + 4]
|
|
234
|
+
dword: int = struct.unpack(">I", struct.pack("4B", *data))[0]
|
|
235
|
+
return dword
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def get_dint(bytearray_: bytearray, byte_index: int) -> int:
|
|
239
|
+
"""Get dint value from bytearray.
|
|
240
|
+
|
|
241
|
+
Notes:
|
|
242
|
+
Datatype `dint` consists in 4 bytes in the PLC.
|
|
243
|
+
Maximum possible value is 2147483647.
|
|
244
|
+
Lower posible value is -2147483648.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
bytearray_: buffer to read.
|
|
248
|
+
byte_index: byte index from where to start reading.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Value read.
|
|
252
|
+
|
|
253
|
+
Examples:
|
|
254
|
+
>>> import struct
|
|
255
|
+
>>> data = bytearray(4)
|
|
256
|
+
>>> data[:] = struct.pack(">i", 2147483647)
|
|
257
|
+
>>> get_dint(data, 0)
|
|
258
|
+
2147483647
|
|
259
|
+
"""
|
|
260
|
+
data = bytearray_[byte_index : byte_index + 4]
|
|
261
|
+
dint: int = struct.unpack(">i", struct.pack("4B", *data))[0]
|
|
262
|
+
return dint
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def get_udint(bytearray_: bytearray, byte_index: int) -> int:
|
|
266
|
+
"""Get unsigned dint value from bytearray.
|
|
267
|
+
|
|
268
|
+
Notes:
|
|
269
|
+
Datatype `udint` consists in 4 bytes in the PLC.
|
|
270
|
+
Maximum possible value is 4294967295.
|
|
271
|
+
Minimum posible value is 0.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
bytearray_: buffer to read.
|
|
275
|
+
byte_index: byte index from where to start reading.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Value read.
|
|
279
|
+
|
|
280
|
+
Examples:
|
|
281
|
+
>>> import struct
|
|
282
|
+
>>> data = bytearray(4)
|
|
283
|
+
>>> data[:] = struct.pack(">I", 4294967295)
|
|
284
|
+
>>> get_udint(data, 0)
|
|
285
|
+
4294967295
|
|
286
|
+
"""
|
|
287
|
+
data = bytearray_[byte_index : byte_index + 4]
|
|
288
|
+
dint: int = struct.unpack(">I", struct.pack("4B", *data))[0]
|
|
289
|
+
return dint
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_s5time(bytearray_: bytearray, byte_index: int) -> str:
|
|
293
|
+
micro_to_milli = 1000
|
|
294
|
+
data_bytearray = bytearray_[byte_index : byte_index + 2]
|
|
295
|
+
s5time_data_int_like = list(data_bytearray.hex())
|
|
296
|
+
if s5time_data_int_like[0] == "0":
|
|
297
|
+
# 10ms
|
|
298
|
+
time_base = 10
|
|
299
|
+
elif s5time_data_int_like[0] == "1":
|
|
300
|
+
# 100ms
|
|
301
|
+
time_base = 100
|
|
302
|
+
elif s5time_data_int_like[0] == "2":
|
|
303
|
+
# 1s
|
|
304
|
+
time_base = 1000
|
|
305
|
+
elif s5time_data_int_like[0] == "3":
|
|
306
|
+
# 10s
|
|
307
|
+
time_base = 10000
|
|
308
|
+
else:
|
|
309
|
+
raise ValueError("This value should not be greater than 3")
|
|
310
|
+
|
|
311
|
+
s5time_bcd = int(s5time_data_int_like[1]) * 100 + int(s5time_data_int_like[2]) * 10 + int(s5time_data_int_like[3])
|
|
312
|
+
s5time_microseconds = time_base * s5time_bcd
|
|
313
|
+
s5time = timedelta(microseconds=s5time_microseconds * micro_to_milli)
|
|
314
|
+
# here we must return a string like variable, otherwise nothing will return
|
|
315
|
+
return "".join(str(s5time))
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def get_dt(bytearray_: bytearray, byte_index: int) -> str:
|
|
319
|
+
"""Get DATE_AND_TIME Value from bytearray as ISO 8601 formatted Date String
|
|
320
|
+
Notes:
|
|
321
|
+
Datatype `DATE_AND_TIME` consists in 8 bytes in the PLC.
|
|
322
|
+
Args:
|
|
323
|
+
bytearray_: buffer to read.
|
|
324
|
+
byte_index: byte index from where to start writing.
|
|
325
|
+
Examples:
|
|
326
|
+
>>> data = bytearray(8)
|
|
327
|
+
>>> data[:] = [32, 7, 18, 23, 50, 2, 133, 65] #'2020-07-12T17:32:02.854000'
|
|
328
|
+
>>> get_dt(data,0)
|
|
329
|
+
'2020-07-12T17:32:02.854000'
|
|
330
|
+
"""
|
|
331
|
+
return get_date_time_object(bytearray_, byte_index).isoformat(timespec="microseconds")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def get_date_time_object(bytearray_: bytearray, byte_index: int) -> datetime:
|
|
335
|
+
"""Get DATE_AND_TIME Value from bytearray as python datetime object
|
|
336
|
+
Notes:
|
|
337
|
+
Datatype `DATE_AND_TIME` consists in 8 bytes in the PLC.
|
|
338
|
+
Args:
|
|
339
|
+
bytearray_: buffer to read.
|
|
340
|
+
byte_index: byte index from where to start writing.
|
|
341
|
+
Examples:
|
|
342
|
+
>>> data = bytearray(8)
|
|
343
|
+
>>> data[:] = [32, 7, 18, 23, 50, 2, 133, 65] #date '2020-07-12 17:32:02.854'
|
|
344
|
+
>>> get_date_time_object(data,0)
|
|
345
|
+
datetime.datetime(2020, 7, 12, 17, 32, 2, 854000)
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
def bcd_to_byte(byte: int) -> int:
|
|
349
|
+
return (byte >> 4) * 10 + (byte & 0xF)
|
|
350
|
+
|
|
351
|
+
year = bcd_to_byte(bytearray_[byte_index])
|
|
352
|
+
# between 1990 and 2089, only last two digits are saved in DB 90 - 89
|
|
353
|
+
year = 2000 + year if year < 90 else 1900 + year
|
|
354
|
+
month = bcd_to_byte(bytearray_[byte_index + 1])
|
|
355
|
+
day = bcd_to_byte(bytearray_[byte_index + 2])
|
|
356
|
+
hour = bcd_to_byte(bytearray_[byte_index + 3])
|
|
357
|
+
min_ = bcd_to_byte(bytearray_[byte_index + 4])
|
|
358
|
+
sec = bcd_to_byte(bytearray_[byte_index + 5])
|
|
359
|
+
# plc save miliseconds in two bytes with the most signifanct byte used only
|
|
360
|
+
# in the last byte for microseconds the other for weekday
|
|
361
|
+
# * 1000 because pythoin datetime needs microseconds not milli
|
|
362
|
+
microsec = (bcd_to_byte(bytearray_[byte_index + 6]) * 10 + bcd_to_byte(bytearray_[byte_index + 7] >> 4)) * 1000
|
|
363
|
+
|
|
364
|
+
return datetime(year, month, day, hour, min_, sec, microsec)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def get_time(bytearray_: bytearray, byte_index: int) -> str:
|
|
368
|
+
"""Get time value from bytearray.
|
|
369
|
+
|
|
370
|
+
Notes:
|
|
371
|
+
Datatype `time` consists in 4 bytes in the PLC.
|
|
372
|
+
Maximum possible value is T#24D_20H_31M_23S_647MS(2147483647).
|
|
373
|
+
Lower posible value is T#-24D_20H_31M_23S_648MS(-2147483648).
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
bytearray_: buffer to read.
|
|
377
|
+
byte_index: byte index from where to start reading.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Value read.
|
|
381
|
+
|
|
382
|
+
Examples:
|
|
383
|
+
>>> import struct
|
|
384
|
+
>>> data = bytearray(4)
|
|
385
|
+
>>> data[:] = struct.pack(">i", 2147483647)
|
|
386
|
+
>>> get_time(data, 0)
|
|
387
|
+
'24:20:31:23:647'
|
|
388
|
+
"""
|
|
389
|
+
data_bytearray = bytearray_[byte_index : byte_index + 4]
|
|
390
|
+
bits = 32
|
|
391
|
+
sign = 1
|
|
392
|
+
byte_str = data_bytearray.hex()
|
|
393
|
+
val = int(byte_str, 16)
|
|
394
|
+
if (val & (1 << (bits - 1))) != 0:
|
|
395
|
+
sign = -1 # if sign bit is set e.g., 8bit: 128-255
|
|
396
|
+
val -= 1 << bits # compute negative value
|
|
397
|
+
val *= sign
|
|
398
|
+
|
|
399
|
+
milli_seconds = val % 1000
|
|
400
|
+
seconds = val // 1000
|
|
401
|
+
minutes = seconds // 60
|
|
402
|
+
hours = minutes // 60
|
|
403
|
+
days = hours // 24
|
|
404
|
+
|
|
405
|
+
sign_str = "" if sign >= 0 else "-"
|
|
406
|
+
time_str = f"{sign_str}{days!s}:{hours % 24!s}:{minutes % 60!s}:{seconds % 60!s}.{milli_seconds!s}"
|
|
407
|
+
|
|
408
|
+
return time_str
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def get_usint(bytearray_: bytearray, byte_index: int) -> int:
|
|
412
|
+
"""Get the unsigned small int from the bytearray
|
|
413
|
+
|
|
414
|
+
Notes:
|
|
415
|
+
Datatype `usint` (Unsigned small int) consists on 1 byte in the PLC.
|
|
416
|
+
Maximum posible value is 255.
|
|
417
|
+
Lower posible value is 0.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
bytearray_: buffer to read from.
|
|
421
|
+
byte_index: byte index from where to start reading.
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
Value read.
|
|
425
|
+
|
|
426
|
+
Examples:
|
|
427
|
+
>>> data = bytearray([255])
|
|
428
|
+
>>> get_usint(data, 0)
|
|
429
|
+
255
|
|
430
|
+
"""
|
|
431
|
+
data = bytearray_[byte_index] & 0xFF
|
|
432
|
+
packed = struct.pack("B", data)
|
|
433
|
+
value: int = struct.unpack(">B", packed)[0]
|
|
434
|
+
return value
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def get_sint(bytearray_: bytearray, byte_index: int) -> int:
|
|
438
|
+
"""Get the small int
|
|
439
|
+
|
|
440
|
+
Notes:
|
|
441
|
+
Datatype `sint` (Small int) consists in 1 byte in the PLC.
|
|
442
|
+
Maximum value posible is 127.
|
|
443
|
+
Lowest value posible is -128.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
bytearray_: buffer to read from.
|
|
447
|
+
byte_index: byte index from where to start reading.
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
Value read.
|
|
451
|
+
|
|
452
|
+
Examples:
|
|
453
|
+
>>> data = bytearray([127])
|
|
454
|
+
>>> get_sint(data, 0)
|
|
455
|
+
127
|
|
456
|
+
"""
|
|
457
|
+
data = bytearray_[byte_index]
|
|
458
|
+
packed = struct.pack("B", data)
|
|
459
|
+
value: int = struct.unpack(">b", packed)[0]
|
|
460
|
+
return value
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def get_lint(bytearray_: bytearray, byte_index: int) -> int:
|
|
464
|
+
"""Get the long int
|
|
465
|
+
|
|
466
|
+
THIS VALUE IS NEITHER TESTED NOR VERIFIED BY A REAL PLC AT THE MOMENT
|
|
467
|
+
|
|
468
|
+
Notes:
|
|
469
|
+
Datatype `lint` (long int) consists in 8 bytes in the PLC.
|
|
470
|
+
Maximum value posible is +9223372036854775807
|
|
471
|
+
Lowest value posible is -9223372036854775808
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
bytearray_: buffer to read from.
|
|
475
|
+
byte_index: byte index from where to start reading.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
Value read.
|
|
479
|
+
|
|
480
|
+
Examples:
|
|
481
|
+
read lint value (here as example 12345) from DB1.10 of a PLC
|
|
482
|
+
>>> from snap7 import Client
|
|
483
|
+
>>> data = Client().db_read(db_number=1, start=10, size=8)
|
|
484
|
+
>>> get_lint(data, 0)
|
|
485
|
+
12345
|
|
486
|
+
"""
|
|
487
|
+
|
|
488
|
+
raw_lint = bytearray_[byte_index : byte_index + 8]
|
|
489
|
+
lint = struct.unpack(">q", struct.pack("8B", *raw_lint))[0]
|
|
490
|
+
return int(lint)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def get_lreal(bytearray_: bytearray, byte_index: int) -> float:
|
|
494
|
+
"""Get the long real
|
|
495
|
+
|
|
496
|
+
Datatype `lreal` (long real) consists in 8 bytes in the PLC.
|
|
497
|
+
Negative Range: -1.7976931348623158e+308 to -2.2250738585072014e-308
|
|
498
|
+
Positive Range: +2.2250738585072014e-308 to +1.7976931348623158e+308
|
|
499
|
+
Zero: ±0
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
bytearray_: buffer to read from.
|
|
503
|
+
byte_index: byte index from where to start reading.
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
The real value.
|
|
507
|
+
|
|
508
|
+
Examples:
|
|
509
|
+
read lreal value (here as example 12345.12345) from DB1.10 of a PLC
|
|
510
|
+
>>> from snap7 import Client
|
|
511
|
+
>>> data = Client().db_read(db_number=1, start=10, size=8)
|
|
512
|
+
>>> get_lreal(data, 0)
|
|
513
|
+
12345.12345
|
|
514
|
+
"""
|
|
515
|
+
return float(struct.unpack_from(">d", bytearray_, offset=byte_index)[0])
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def get_lword(bytearray_: bytearray, byte_index: int) -> int:
|
|
519
|
+
"""Get the long word
|
|
520
|
+
|
|
521
|
+
Notes:
|
|
522
|
+
Datatype `lword` (long word) consists in 8 bytes in the PLC.
|
|
523
|
+
Maximum value is 18446744073709551615 (0xFFFFFFFFFFFFFFFF).
|
|
524
|
+
Minimum value is 0.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
bytearray_: buffer to read from.
|
|
528
|
+
byte_index: byte index from where to start reading.
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Value read.
|
|
532
|
+
|
|
533
|
+
Examples:
|
|
534
|
+
>>> data = bytearray(b"\\x00\\x00\\x00\\x00\\x00\\x00\\xAB\\xCD")
|
|
535
|
+
>>> get_lword(data, 0)
|
|
536
|
+
43981
|
|
537
|
+
"""
|
|
538
|
+
data = bytearray_[byte_index : byte_index + 8]
|
|
539
|
+
lword: int = struct.unpack(">Q", struct.pack("8B", *data))[0]
|
|
540
|
+
return lword
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def get_ulint(bytearray_: bytearray, byte_index: int) -> int:
|
|
544
|
+
"""Get ulint value from bytearray.
|
|
545
|
+
|
|
546
|
+
Notes:
|
|
547
|
+
Datatype `int` in the PLC is represented in 8 bytes
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
bytearray_: buffer to read from.
|
|
551
|
+
byte_index: byte index to start reading from.
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
Value read.
|
|
555
|
+
|
|
556
|
+
Examples:
|
|
557
|
+
Read 8 Bytes raw from DB1.10, where an ulint value is stored. Return Python compatible value.
|
|
558
|
+
>>> from snap7 import Client
|
|
559
|
+
>>> data = Client().db_read(db_number=1, start=10, size=8)
|
|
560
|
+
>>> get_ulint(data, 0)
|
|
561
|
+
12345
|
|
562
|
+
"""
|
|
563
|
+
raw_ulint = bytearray_[byte_index : byte_index + 8]
|
|
564
|
+
lint: int = struct.unpack(">Q", struct.pack("8B", *raw_ulint))[0]
|
|
565
|
+
return lint
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def get_tod(bytearray_: bytearray, byte_index: int) -> timedelta:
|
|
569
|
+
len_bytearray_ = len(bytearray_)
|
|
570
|
+
byte_range = byte_index + 4
|
|
571
|
+
if len_bytearray_ < byte_range:
|
|
572
|
+
raise ValueError("Date can't be extracted from bytearray. bytearray_[Index:Index+16] would cause overflow.")
|
|
573
|
+
time_val = timedelta(milliseconds=int.from_bytes(bytearray_[byte_index:byte_range], byteorder="big"))
|
|
574
|
+
if time_val.days >= 1:
|
|
575
|
+
raise ValueError("Time_Of_Date can't be extracted from bytearray. Bytearray contains unexpected values.")
|
|
576
|
+
return time_val
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def get_date(bytearray_: bytearray, byte_index: int = 0) -> date:
|
|
580
|
+
len_bytearray_ = len(bytearray_)
|
|
581
|
+
byte_range = byte_index + 2
|
|
582
|
+
if len_bytearray_ < byte_range:
|
|
583
|
+
raise ValueError("Date can't be extracted from bytearray. bytearray_[Index:Index+16] would cause overflow.")
|
|
584
|
+
date_val = date(1990, 1, 1) + timedelta(days=int.from_bytes(bytearray_[byte_index:byte_range], byteorder="big"))
|
|
585
|
+
if date_val > date(2168, 12, 31):
|
|
586
|
+
raise ValueError("date_val is higher than specification allows.")
|
|
587
|
+
return date_val
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def get_ltime(bytearray_: bytearray, byte_index: int) -> timedelta:
|
|
591
|
+
"""Get LTIME value from bytearray.
|
|
592
|
+
|
|
593
|
+
Notes:
|
|
594
|
+
Datatype `LTIME` consists of 8 bytes (64-bit signed integer) representing
|
|
595
|
+
nanoseconds. Used in S7-1500 PLCs.
|
|
596
|
+
|
|
597
|
+
Args:
|
|
598
|
+
bytearray_: buffer to read from.
|
|
599
|
+
byte_index: byte index from where to start reading.
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
timedelta value.
|
|
603
|
+
|
|
604
|
+
Examples:
|
|
605
|
+
>>> data = bytearray(8)
|
|
606
|
+
>>> data[:] = b'\\x00\\x00\\x00\\x00\\x3b\\x9a\\xca\\x00' # 1 second in nanoseconds
|
|
607
|
+
>>> get_ltime(data, 0)
|
|
608
|
+
datetime.timedelta(seconds=1)
|
|
609
|
+
"""
|
|
610
|
+
raw = bytearray_[byte_index : byte_index + 8]
|
|
611
|
+
nanoseconds: int = struct.unpack(">q", struct.pack("8B", *raw))[0]
|
|
612
|
+
return timedelta(microseconds=nanoseconds // 1000)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def get_ltod(bytearray_: bytearray, byte_index: int) -> timedelta:
|
|
616
|
+
"""Get LTOD (Long Time of Day) value from bytearray.
|
|
617
|
+
|
|
618
|
+
Notes:
|
|
619
|
+
Datatype `LTOD` consists of 8 bytes (64-bit unsigned integer) representing
|
|
620
|
+
nanoseconds since midnight. Used in S7-1500 PLCs.
|
|
621
|
+
Range: 0 to 86399999999999 ns.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
bytearray_: buffer to read from.
|
|
625
|
+
byte_index: byte index from where to start reading.
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
timedelta value representing time of day.
|
|
629
|
+
"""
|
|
630
|
+
raw = bytearray_[byte_index : byte_index + 8]
|
|
631
|
+
nanoseconds: int = struct.unpack(">Q", struct.pack("8B", *raw))[0]
|
|
632
|
+
result = timedelta(microseconds=nanoseconds // 1000)
|
|
633
|
+
if result.days >= 1:
|
|
634
|
+
raise ValueError("LTOD value exceeds 24 hours")
|
|
635
|
+
return result
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def get_ldt(bytearray_: bytearray, byte_index: int) -> datetime:
|
|
639
|
+
"""Get LDT (Long Date and Time) value from bytearray.
|
|
640
|
+
|
|
641
|
+
Notes:
|
|
642
|
+
Datatype `LDT` consists of 8 bytes (64-bit unsigned integer) representing
|
|
643
|
+
nanoseconds since 1970-01-01 00:00:00 UTC. Used in S7-1500 PLCs.
|
|
644
|
+
|
|
645
|
+
Args:
|
|
646
|
+
bytearray_: buffer to read from.
|
|
647
|
+
byte_index: byte index from where to start reading.
|
|
648
|
+
|
|
649
|
+
Returns:
|
|
650
|
+
datetime value.
|
|
651
|
+
"""
|
|
652
|
+
raw = bytearray_[byte_index : byte_index + 8]
|
|
653
|
+
nanoseconds: int = struct.unpack(">Q", struct.pack("8B", *raw))[0]
|
|
654
|
+
epoch = datetime(1970, 1, 1)
|
|
655
|
+
return epoch + timedelta(microseconds=nanoseconds // 1000)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def get_dtl(bytearray_: bytearray, byte_index: int) -> datetime:
|
|
659
|
+
time_to_datetime = datetime(
|
|
660
|
+
year=int.from_bytes(bytearray_[byte_index : byte_index + 2], byteorder="big"),
|
|
661
|
+
month=int(bytearray_[byte_index + 2]),
|
|
662
|
+
day=int(bytearray_[byte_index + 3]),
|
|
663
|
+
hour=int(bytearray_[byte_index + 5]),
|
|
664
|
+
minute=int(bytearray_[byte_index + 6]),
|
|
665
|
+
second=int(bytearray_[byte_index + 7]),
|
|
666
|
+
microsecond=int(bytearray_[byte_index + 8]),
|
|
667
|
+
) # --- ? noch nicht genau genug
|
|
668
|
+
if time_to_datetime > datetime(2554, 12, 31, 23, 59, 59):
|
|
669
|
+
raise ValueError("date_val is higher than specification allows.")
|
|
670
|
+
return time_to_datetime
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
def get_char(bytearray_: bytearray, byte_index: int) -> str:
|
|
674
|
+
"""Get char value from bytearray.
|
|
675
|
+
|
|
676
|
+
Notes:
|
|
677
|
+
Datatype `char` in the PLC is represented in 1 byte. It has to be in ASCII-format.
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
bytearray_: buffer to read from.
|
|
681
|
+
byte_index: byte index to start reading from.
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
Value read.
|
|
685
|
+
|
|
686
|
+
Examples:
|
|
687
|
+
Read 1 Byte raw from DB1.10, where a char value is stored. Return Python compatible value.
|
|
688
|
+
>>> from snap7 import Client
|
|
689
|
+
>>> data = Client().db_read(db_number=1, start=10, size=1)
|
|
690
|
+
>>> get_char(data, 0)
|
|
691
|
+
'C'
|
|
692
|
+
"""
|
|
693
|
+
char = chr(bytearray_[byte_index])
|
|
694
|
+
return char
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def get_wchar(bytearray_: bytearray, byte_index: int) -> str:
|
|
698
|
+
"""Get wchar value from bytearray.
|
|
699
|
+
|
|
700
|
+
Datatype `wchar` in the PLC is represented in 2 bytes. It has to be in utf-16-be format.
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
bytearray_: buffer to read from.
|
|
704
|
+
byte_index: byte index to start reading from.
|
|
705
|
+
|
|
706
|
+
Returns:
|
|
707
|
+
Value read.
|
|
708
|
+
|
|
709
|
+
Examples:
|
|
710
|
+
Read 2 Bytes raw from DB1.10, where a wchar value is stored. Return Python compatible value.
|
|
711
|
+
>>> from snap7 import Client
|
|
712
|
+
>>> data = Client().db_read(db_number=1, start=10, size=2)
|
|
713
|
+
>>> get_wchar(data, 0)
|
|
714
|
+
'C'
|
|
715
|
+
"""
|
|
716
|
+
if bytearray_[byte_index] == 0:
|
|
717
|
+
return chr(bytearray_[byte_index + 1])
|
|
718
|
+
return bytearray_[byte_index : byte_index + 2].decode("utf-16-be")
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def get_wstring(bytearray_: bytearray, byte_index: int) -> str:
|
|
722
|
+
"""Parse wstring from bytearray
|
|
723
|
+
|
|
724
|
+
Notes:
|
|
725
|
+
Byte 0 and 1 contains the max size posible for a string (2 Byte value).
|
|
726
|
+
byte 2 and 3 contains the length of the string that contains (2 Byte value).
|
|
727
|
+
The other bytes contain WCHARs (2Byte) in utf-16-be style.
|
|
728
|
+
|
|
729
|
+
Args:
|
|
730
|
+
bytearray_: buffer from where to get the string.
|
|
731
|
+
byte_index: byte index from where to start reading.
|
|
732
|
+
|
|
733
|
+
Returns:
|
|
734
|
+
String value.
|
|
735
|
+
|
|
736
|
+
Examples:
|
|
737
|
+
Read from DB1.10 22, where the WSTRING is stored, the raw 22 Bytes and convert them to a python string
|
|
738
|
+
>>> from snap7 import Client
|
|
739
|
+
>>> data = Client().db_read(db_number=1, start=10, size=22)
|
|
740
|
+
>>> get_wstring(data, 0)
|
|
741
|
+
'hello world'
|
|
742
|
+
"""
|
|
743
|
+
# Byte 0 + 1 --> total length of wstring, should be bytearray_ - 4
|
|
744
|
+
# Byte 2, 3 --> used length of wstring
|
|
745
|
+
wstring_start = byte_index + 4
|
|
746
|
+
|
|
747
|
+
max_wstring_size = bytearray_[byte_index : byte_index + 2]
|
|
748
|
+
packed = struct.pack("2B", *max_wstring_size)
|
|
749
|
+
max_wstring_symbols = struct.unpack(">H", packed)[0] * 2
|
|
750
|
+
|
|
751
|
+
wstr_length_raw = bytearray_[byte_index + 2 : byte_index + 4]
|
|
752
|
+
wstr_symbols_amount = struct.unpack(">H", struct.pack("2B", *wstr_length_raw))[0] * 2
|
|
753
|
+
|
|
754
|
+
if wstr_symbols_amount > max_wstring_symbols or max_wstring_symbols > 16382:
|
|
755
|
+
logger.error("The wstring is too big for the size encountered in specification")
|
|
756
|
+
logger.error("WRONG SIZED STRING ENCOUNTERED")
|
|
757
|
+
raise TypeError(
|
|
758
|
+
f"WString contains {wstr_symbols_amount} chars, but max {max_wstring_symbols} chars are "
|
|
759
|
+
f"expected or is larger than 16382. Bytearray doesn't seem to be a valid string."
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
return bytearray_[wstring_start : wstring_start + wstr_symbols_amount].decode("utf-16-be")
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def get_array(bytearray_: bytearray, byte_index: int) -> NoReturn:
|
|
766
|
+
raise NotImplementedError
|