DLMS-SPODES-client 0.19.22__py3-none-any.whl → 0.19.24__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.
Files changed (60) hide show
  1. DLMS_SPODES_client/FCS16.py +39 -39
  2. DLMS_SPODES_client/__init__.py +12 -12
  3. DLMS_SPODES_client/client.py +2091 -2091
  4. DLMS_SPODES_client/gurux_common/enums/TraceLevel.py +21 -21
  5. DLMS_SPODES_client/gurux_dlms/AesGcmParameter.py +37 -37
  6. DLMS_SPODES_client/gurux_dlms/CountType.py +16 -16
  7. DLMS_SPODES_client/gurux_dlms/GXByteBuffer.py +545 -545
  8. DLMS_SPODES_client/gurux_dlms/GXCiphering.py +196 -196
  9. DLMS_SPODES_client/gurux_dlms/GXDLMS.py +426 -426
  10. DLMS_SPODES_client/gurux_dlms/GXDLMSChippering.py +237 -237
  11. DLMS_SPODES_client/gurux_dlms/GXDLMSChipperingStream.py +977 -977
  12. DLMS_SPODES_client/gurux_dlms/GXDLMSConfirmedServiceError.py +90 -90
  13. DLMS_SPODES_client/gurux_dlms/GXDLMSException.py +139 -139
  14. DLMS_SPODES_client/gurux_dlms/GXDLMSLNParameters.py +33 -33
  15. DLMS_SPODES_client/gurux_dlms/GXDLMSSNParameters.py +21 -21
  16. DLMS_SPODES_client/gurux_dlms/GXDLMSSettings.py +254 -254
  17. DLMS_SPODES_client/gurux_dlms/GXReplyData.py +87 -87
  18. DLMS_SPODES_client/gurux_dlms/HdlcControlFrame.py +9 -9
  19. DLMS_SPODES_client/gurux_dlms/MBusCommand.py +8 -8
  20. DLMS_SPODES_client/gurux_dlms/MBusEncryptionMode.py +27 -27
  21. DLMS_SPODES_client/gurux_dlms/ResponseType.py +8 -8
  22. DLMS_SPODES_client/gurux_dlms/SetResponseType.py +29 -29
  23. DLMS_SPODES_client/gurux_dlms/_HDLCInfo.py +9 -9
  24. DLMS_SPODES_client/gurux_dlms/__init__.py +75 -75
  25. DLMS_SPODES_client/gurux_dlms/enums/Access.py +12 -12
  26. DLMS_SPODES_client/gurux_dlms/enums/ApplicationReference.py +14 -14
  27. DLMS_SPODES_client/gurux_dlms/enums/Authentication.py +41 -41
  28. DLMS_SPODES_client/gurux_dlms/enums/BerType.py +35 -35
  29. DLMS_SPODES_client/gurux_dlms/enums/Command.py +285 -285
  30. DLMS_SPODES_client/gurux_dlms/enums/Definition.py +9 -9
  31. DLMS_SPODES_client/gurux_dlms/enums/ErrorCode.py +46 -46
  32. DLMS_SPODES_client/gurux_dlms/enums/ExceptionServiceError.py +12 -12
  33. DLMS_SPODES_client/gurux_dlms/enums/HardwareResource.py +10 -10
  34. DLMS_SPODES_client/gurux_dlms/enums/HdlcFrameType.py +9 -9
  35. DLMS_SPODES_client/gurux_dlms/enums/Initiate.py +10 -10
  36. DLMS_SPODES_client/gurux_dlms/enums/LoadDataSet.py +13 -13
  37. DLMS_SPODES_client/gurux_dlms/enums/ObjectType.py +306 -306
  38. DLMS_SPODES_client/gurux_dlms/enums/Priority.py +7 -7
  39. DLMS_SPODES_client/gurux_dlms/enums/RequestTypes.py +9 -9
  40. DLMS_SPODES_client/gurux_dlms/enums/Security.py +14 -14
  41. DLMS_SPODES_client/gurux_dlms/enums/Service.py +16 -16
  42. DLMS_SPODES_client/gurux_dlms/enums/ServiceClass.py +9 -9
  43. DLMS_SPODES_client/gurux_dlms/enums/ServiceError.py +8 -8
  44. DLMS_SPODES_client/gurux_dlms/enums/Standard.py +18 -18
  45. DLMS_SPODES_client/gurux_dlms/enums/StateError.py +7 -7
  46. DLMS_SPODES_client/gurux_dlms/enums/Task.py +10 -10
  47. DLMS_SPODES_client/gurux_dlms/enums/VdeStateError.py +10 -10
  48. DLMS_SPODES_client/gurux_dlms/enums/__init__.py +33 -33
  49. DLMS_SPODES_client/gurux_dlms/internal/_GXCommon.py +1673 -1673
  50. DLMS_SPODES_client/logger.py +56 -56
  51. DLMS_SPODES_client/services.py +97 -97
  52. DLMS_SPODES_client/session.py +365 -365
  53. DLMS_SPODES_client/settings.py +48 -48
  54. DLMS_SPODES_client/task.py +1841 -1842
  55. {dlms_spodes_client-0.19.22.dist-info → dlms_spodes_client-0.19.24.dist-info}/METADATA +29 -27
  56. dlms_spodes_client-0.19.24.dist-info/RECORD +61 -0
  57. dlms_spodes_client-0.19.22.dist-info/RECORD +0 -61
  58. {dlms_spodes_client-0.19.22.dist-info → dlms_spodes_client-0.19.24.dist-info}/WHEEL +0 -0
  59. {dlms_spodes_client-0.19.22.dist-info → dlms_spodes_client-0.19.24.dist-info}/entry_points.txt +0 -0
  60. {dlms_spodes_client-0.19.22.dist-info → dlms_spodes_client-0.19.24.dist-info}/top_level.txt +0 -0
@@ -1,1673 +1,1673 @@
1
- from datetime import datetime
2
- from DLMS_SPODES.types import cdt
3
-
4
- # from ..GXTimeZone import GXTimeZone
5
- from ..GXByteBuffer import GXByteBuffer
6
- from ..enums.Standard import Standard
7
- # from ._GXDataInfo import _GXDataInfo
8
- # from ..GXBitString import GXBitString
9
- # from ..enums import DataType
10
- # from ..enums import DateTimeSkips, DateTimeExtraInfo, ClockStatus
11
- # from ..TranslatorTags import TranslatorTags
12
- # from ..TranslatorOutputType import TranslatorOutputType
13
- # from ..GXArray import GXArray
14
- # from ..GXStructure import GXStructure
15
- # from ..GXEnum import GXEnum
16
- # from ..GXInt8 import GXInt8
17
- # from ..GXInt16 import GXInt16
18
- # from ..GXInt32 import GXInt32
19
- # from ..GXInt64 import GXInt64
20
- # from ..GXUInt8 import GXUInt8
21
- # from ..GXUInt16 import GXUInt16
22
- # from ..GXUInt32 import GXUInt32
23
- # from ..GXUInt64 import GXUInt64
24
- # from ..GXDateTime import GXDateTime
25
- # from ..GXDate import GXDate
26
- # from ..GXTime import GXTime
27
- # from ..GXFloat32 import GXFloat32
28
- # from ..GXFloat64 import GXFloat64
29
-
30
-
31
- class _GXCommon:
32
- """This class is for internal use only and is subject to changes or removal in future versions of the API. Don't use it."""
33
-
34
- # HDLC frame start and end character.
35
- HDLC_FRAME_START_END = 0x7E
36
- LLC_SEND_BYTES = bytearray([0xE6, 0xE6, 0x00])
37
- LLC_REPLY_BYTES = bytearray([0xE6, 0xE7, 0x00])
38
- DATA_TYPE_OFFSET = 0xFF0000
39
- zeroes = "00000000000000000000000000000000"
40
-
41
-
42
- @classmethod
43
- def getBytes(cls, value):
44
- """
45
- Convert string to byte array.
46
- value: String value.
47
- returns String as bytes.
48
- """
49
- if not value:
50
- return None
51
- return value.encode()
52
-
53
- #
54
- # Is string hex string.
55
- #
56
- # value: String value.
57
- # Return true, if string is hex string.
58
- #
59
- @classmethod
60
- def isHexString(cls, value):
61
- # pylint: disable=chained-comparison
62
- if not value:
63
- return False
64
- ch = str()
65
- pos = 0
66
- while pos != len(value):
67
- ch = value.charAt(pos)
68
- if ch != ' ':
69
- if not ((ch > 0x40 and ch < 'G') or (ch > 0x60 and ch < 'g') or (ch > '/' and ch < ':')):
70
- return False
71
- pos += 1
72
- return True
73
-
74
- #
75
- # Get object count. If first byte is 0x80 or higger it will tell
76
- # bytes
77
- # count.
78
- # data received data.
79
- # Object count.
80
- #
81
- @classmethod
82
- def getObjectCount(cls, data):
83
- cnt = data.getUInt8()
84
- if cnt > 0x80:
85
- if cnt == 0x81:
86
- cnt = data.getUInt8()
87
- elif cnt == 0x82:
88
- cnt = data.getUInt16()
89
- elif cnt == 0x84:
90
- cnt = int(data.getUInt32())
91
- else:
92
- raise ValueError("Invalid count.")
93
- return cnt
94
-
95
- #
96
- # Return how many bytes object count takes.
97
- #
98
- # count
99
- # Value
100
- # Value size in bytes.
101
- #
102
- @classmethod
103
- def getObjectCountSizeInBytes(cls, count):
104
- if count < 0x80:
105
- ret = 1
106
- elif count < 0x100:
107
- ret = 2
108
- elif count < 0x10000:
109
- ret = 3
110
- else:
111
- ret = 5
112
- return ret
113
-
114
- #
115
- # Add string to byte buffer.
116
- #
117
- # value
118
- # String to add.
119
- # bb
120
- # Byte buffer where string is added.
121
- #
122
- @classmethod
123
- def addString(cls, value, bb):
124
- bb.set(cdt.OctetString.TAG)
125
- if not value:
126
- _GXCommon.setObjectCount(0, bb)
127
- else:
128
- _GXCommon.setObjectCount(len(value), bb)
129
- bb.set(value.encode())
130
-
131
- #
132
- # Set item count.
133
- # count
134
- # buff
135
- #
136
- @classmethod
137
- def setObjectCount(cls, count, buff):
138
- if count < 0x80:
139
- buff.setUInt8(count)
140
- ret = 1
141
- elif count < 0x100:
142
- buff.setUInt8(0x81)
143
- buff.setUInt8(count)
144
- ret = 2
145
- elif count < 0x10000:
146
- buff.setUInt8(0x82)
147
- buff.setUInt16(count)
148
- ret = 3
149
- else:
150
- buff.setUInt8(0x84)
151
- buff.setUInt32(count)
152
- ret = 5
153
- return ret
154
-
155
- #
156
- # Reserved for internal use.
157
- #
158
- @classmethod
159
- def toBitString(cls, value, count):
160
- count2 = count
161
- sb = ""
162
- if count2 > 0:
163
- if count2 > 8:
164
- count2 = 8
165
- pos = 7
166
- while pos != 8 - count2 - 1:
167
- if (value & (1 << pos)) != 0:
168
- sb += '1'
169
- else:
170
- sb += '0'
171
- pos -= 1
172
- return sb
173
-
174
- @classmethod
175
- def changeType(cls, settings, value, type_):
176
- #pylint: disable=import-outside-toplevel
177
- if value is None:
178
- ret = None
179
- elif type_ == DataType.NONE:
180
- ret = GXByteBuffer.hex(value, True)
181
- elif type_ in (DataType.STRING, DataType.OCTET_STRING) and not value:
182
- ret = ""
183
- elif type_ == DataType.OCTET_STRING:
184
- ret = GXByteBuffer(value)
185
- # elif type_ == DataType.STRING and not GXByteBuffer.isAsciiString(value):
186
- # ret = GXByteBuffer(value)
187
- elif type_ == DataType.STRING:
188
- ret = cdt.OctetString(value)
189
- elif type_ == DataType.DATETIME and not value:
190
- ret = GXDateTime(None)
191
- elif type_ == DataType.DATE and not value:
192
- ret = GXDate(None)
193
- elif type_ == DataType.TIME and not value:
194
- ret = GXTime(None)
195
- else:
196
- info = _GXDataInfo()
197
- info.type_ = type_
198
- ret = _GXCommon.getData(settings, GXByteBuffer(value), info)
199
- if not info.complete:
200
- raise ValueError("Change type failed. Not enought data.")
201
- if type_ == DataType.OCTET_STRING and isinstance(ret, bytes):
202
- ret = GXByteBuffer.hex(ret)
203
- return ret
204
-
205
- #
206
- # Get data from DLMS frame.
207
- #
208
- # data
209
- # received data.
210
- # info
211
- # Data info.
212
- # Received data.
213
- #
214
- @classmethod
215
- def getData(cls, settings, data: GXByteBuffer, info): # info: _GXDataInfo
216
- value = None
217
- startIndex = data.position
218
- if data.position == len(data):
219
- info.complete = False
220
- return None
221
- info.complete = True
222
- knownType = info.type_ != cdt.NullData
223
- # Get data type if it is unknown.
224
- if not knownType:
225
- info.type_ = cdt.get_common_data_type_from(data.getUInt8().to_bytes(1, "big"))
226
- if info.type_ == cdt.NullData:
227
- return value
228
- if data.position == len(data):
229
- info.complete = False
230
- return None
231
- if info.type_ == cdt.Array or info.type_ == cdt.Structure:
232
- value = cls.getArray(settings, data, info, startIndex)
233
- elif info.type_ == DataType.BOOLEAN:
234
- value = cls.getBoolean(data, info)
235
- elif info.type_ == DataType.BITSTRING:
236
- value = cls.getBitString(data, info)
237
- elif info.type_ == DataType.INT32:
238
- value = cls.getInt32(data, info)
239
- elif info.type_ == DataType.UINT32:
240
- value = cls.getUInt32(data, info)
241
- elif info.type_ == DataType.STRING:
242
- value = cls.getString(data, info, knownType)
243
- elif info.type_ == DataType.STRING_UTF8:
244
- value = cls.getUtfString(data, info, knownType)
245
- elif info.type_ == DataType.OCTET_STRING:
246
- value = cls.getOctetString(settings, data, info, knownType)
247
- elif info.type_ == DataType.BCD:
248
- value = cls.getBcd(data, info)
249
- elif info.type_ == DataType.INT8:
250
- value = cls.getInt8(data, info)
251
- elif info.type_ == DataType.INT16:
252
- value = cls.getInt16(data, info)
253
- elif info.type_ == DataType.UINT8:
254
- value = cls.getUInt8(data, info)
255
- elif info.type_ == DataType.UINT16:
256
- value = cls.getUInt16(data, info)
257
- elif info.type_ == DataType.COMPACT_ARRAY:
258
- value = cls.getCompactArray(settings, data, info)
259
- elif info.type_ == DataType.INT64:
260
- value = cls.getInt64(data, info)
261
- elif info.type_ == DataType.UINT64:
262
- value = cls.getUInt64(data, info)
263
- elif info.type_ == DataType.ENUM:
264
- value = cls.getEnum(data, info)
265
- elif info.type_ == DataType.FLOAT32:
266
- value = cls.getFloat(settings, data, info)
267
- elif info.type_ == DataType.FLOAT64:
268
- value = cls.getDouble(settings, data, info)
269
- elif info.type_ == DataType.DATETIME:
270
- value = cls.getDateTime(settings, data, info)
271
- elif info.type_ == DataType.DATE:
272
- value = cls.getDate(data, info)
273
- elif info.type_ == DataType.TIME:
274
- value = cls.getTime(data, info)
275
- else:
276
- raise ValueError("Invalid data type.")
277
- return value
278
-
279
- #
280
- # Convert value to hex string.
281
- # value value to convert.
282
- # desimals Amount of decimals.
283
- # @return
284
- #
285
- @classmethod
286
- def integerToHex(cls, value, desimals):
287
- if desimals:
288
- nbits = desimals * 4
289
- str_ = hex((value + (1 << nbits)) % (1 << nbits))[2:].upper()
290
- else:
291
- str_ = hex(value)[2:].upper()
292
- if not desimals or desimals == len(str_):
293
- return str_
294
- return _GXCommon.zeroes[0: desimals - len(str_)] + str_.upper()
295
-
296
- #
297
- # Convert value to hex string.
298
- # value value to convert.
299
- # desimals Amount of decimals.
300
- # @return
301
- #
302
- @classmethod
303
- def integerString(cls, value, desimals):
304
- str_ = str(value)
305
- if desimals == 0 or len(_GXCommon.zeroes) == len(str_):
306
- return str_
307
- return _GXCommon.zeroes[0: desimals - len(str_)] + str_
308
-
309
- #
310
- # Get array from DLMS data.
311
- #
312
- # buff
313
- # Received DLMS data.
314
- # info
315
- # Data info.
316
- # index
317
- # starting index.
318
- # Object array.
319
- #
320
- @classmethod
321
- def getArray(cls, settings, buff, info, index):
322
- value = None
323
- if info.count == 0:
324
- info.count = _GXCommon.getObjectCount(buff)
325
- size = len(buff) - buff.position
326
- if info.count != 0 and size < 1:
327
- info.complete = False
328
- return None
329
- startIndex = index
330
- if info.type_ == DataType.ARRAY:
331
- value = GXArray()
332
- else:
333
- value = GXStructure()
334
- # Position where last row was found. Cache uses this info.
335
- pos = info.index
336
- while pos != info.count:
337
- info2 = _GXDataInfo()
338
- tmp = cls.getData(settings, buff, info2)
339
- if not info2.complete:
340
- buff.position = startIndex
341
- info.complete = False
342
- break
343
- if info2.count == info2.index:
344
- startIndex = buff.position
345
- value.append(tmp)
346
- pos += 1
347
- info.index = pos
348
- return value
349
-
350
- #
351
- # Get time from DLMS data.
352
- #
353
- # buff
354
- # Received DLMS data.
355
- # info
356
- # Data info.
357
- # Parsed time.
358
- #
359
- @classmethod
360
- def getTime(cls, buff, info):
361
- # pylint: disable=broad-except
362
- value = None
363
- if len(buff) - buff.position < 4:
364
- # If there is not enough data available.
365
- info.complete = False
366
- return None
367
- str_ = None
368
- value = GXTime(None)
369
- # Get time.
370
- hour = buff.getUInt8()
371
- if hour == 0xFF:
372
- hour = 0
373
- value.skip |= DateTimeSkips.HOUR
374
- minute = buff.getUInt8()
375
- if minute == 0xFF:
376
- minute = 0
377
- value.skip |= DateTimeSkips.MINUTE
378
- second = buff.getUInt8()
379
- if second == 0xFF:
380
- second = 0
381
- value.skip |= DateTimeSkips.SECOND
382
- ms = buff.getUInt8()
383
- if ms != 0xFF:
384
- ms *= 10
385
- else:
386
- ms = 0
387
- value.skip |= DateTimeSkips.MILLISECOND
388
- value.value = datetime(1900, 1, 1, hour, minute, second, ms)
389
- return value
390
-
391
- #
392
- # Get date from DLMS data.
393
- #
394
- # buff
395
- # Received DLMS data.
396
- # info
397
- # Data info.
398
- # Parsed date.
399
- #
400
- @classmethod
401
- def getDate(cls, buff, info):
402
- # pylint: disable=broad-except
403
- value = None
404
- if len(buff) - buff.position < 5:
405
- # If there is not enough data available.
406
- info.complete = False
407
- return None
408
- str_ = None
409
- dt = GXDate()
410
- # Get year.
411
- year = buff.getUInt16()
412
- if year < 1900 or year == 0xFFFF:
413
- dt.skip |= DateTimeSkips.YEAR
414
- year = 2000
415
- # Get month
416
- month = buff.getUInt8()
417
- if month == 0xFE:
418
- dt.extra |= DateTimeExtraInfo.DST_BEGIN
419
- month = 1
420
- elif month == 0xFD:
421
- dt.extra |= DateTimeExtraInfo.DST_END
422
- month = 1
423
- else:
424
- if month < 1 or month > 12:
425
- dt.skip |= DateTimeSkips.MONTH
426
- month = 1
427
- # Get day
428
- day = buff.getUInt8()
429
- if day == 0xFE:
430
- dt.extra |= DateTimeExtraInfo.LAST_DAY
431
- day = 1
432
- elif day == 0xFD:
433
- dt.extra |= DateTimeExtraInfo.LAST_DAY2
434
- day = 1
435
- else:
436
- if day < 1 or day > 31:
437
- dt.skip |= DateTimeSkips.DAY
438
- day = 1
439
- dt.value = datetime(year, month, day, 0, 0, 0, 0)
440
- value = dt
441
- # Skip week day
442
- if buff.getUInt8() == 0xFF:
443
- dt.skip |= DateTimeSkips.DAY_OF_WEEK
444
- return value
445
-
446
- #
447
- # Get date and time from DLMS data.
448
- #
449
- # buff
450
- # Received DLMS data.
451
- # info
452
- # Data info.
453
- # Parsed date and time.
454
- #
455
- @classmethod
456
- def getDateTime(cls, settings, buff, info):
457
- # pylint: disable=too-many-locals, broad-except
458
- value = None
459
- skip = DateTimeSkips.NONE
460
- extra = DateTimeExtraInfo.NONE
461
- # If there is not enough data available.
462
- if len(buff) - buff.position < 12:
463
- info.complete = False
464
- return None
465
- str_ = None
466
- dt = GXDateTime()
467
- # Get year.
468
- year = buff.getUInt16()
469
- # Get month
470
- month = buff.getUInt8()
471
- # Get day
472
- day = buff.getUInt8()
473
- # Skip week day
474
- dayOfWeek = buff.getUInt8()
475
- if dayOfWeek == 0xFF:
476
- skip |= DateTimeSkips.DAY_OF_WEEK
477
- else:
478
- dt.dayOfWeek = dayOfWeek
479
- # Get time.
480
- hour = buff.getUInt8()
481
- minute = buff.getUInt8()
482
- second = buff.getUInt8()
483
- ms = buff.getUInt8() & 0xFF
484
- if ms != 0xFF:
485
- ms *= 10
486
- else:
487
- ms = -1
488
- deviation = buff.getInt16()
489
- if deviation == -32768:
490
- deviation = 0x8000
491
- skip |= DateTimeSkips.DEVITATION
492
- status = buff.getUInt8()
493
- dt.status = status
494
- if year < 1900 or year == 0xFFFF:
495
- skip |= DateTimeSkips.YEAR
496
- year = 2000
497
- if month == 0xFE:
498
- extra |= DateTimeExtraInfo.DST_BEGIN
499
- month = 1
500
- elif month == 0xFD:
501
- extra |= DateTimeExtraInfo.DST_END
502
- month = 1
503
- else:
504
- if month < 1 or month > 12:
505
- skip |= DateTimeSkips.MONTH
506
- month = 1
507
-
508
- if day == 0xFE:
509
- extra |= DateTimeExtraInfo.LAST_DAY
510
- day = 1
511
- elif day == 0xFD:
512
- extra |= DateTimeExtraInfo.LAST_DAY2
513
- day = 1
514
- else:
515
- if day == -1 or day == 0 or day > 31:
516
- skip |= DateTimeSkips.DAY
517
- day = 1
518
-
519
- if hour < 0 or hour > 24:
520
- skip |= DateTimeSkips.HOUR
521
- hour = 0
522
- if minute < 0 or minute > 60:
523
- skip |= DateTimeSkips.MINUTE
524
- minute = 0
525
- if second < 0 or second > 60:
526
- skip |= DateTimeSkips.SECOND
527
- second = 0
528
- # If ms is Zero it's skipped.
529
- if ms < 0 or ms > 100:
530
- skip |= DateTimeSkips.MILLISECOND
531
- ms = 0
532
- tz = None
533
- if deviation != 0x8000:
534
- if settings.useUtc2NormalTime:
535
- tz = deviation
536
- else:
537
- tz = -deviation
538
- if tz is None:
539
- dt.value = datetime(year, month, day, hour, minute, second, ms)
540
- else:
541
- dt.value = datetime(year, month, day, hour, minute, second, ms, tzinfo=GXTimeZone(tz))
542
- dt.skip = skip
543
- value = dt
544
- return value
545
-
546
- #
547
- # Get double value from DLMS data.
548
- #
549
- # buff
550
- # Received DLMS data.
551
- # info
552
- # Data info.
553
- # Parsed double value.
554
- #
555
- @classmethod
556
- def getDouble(cls, settings, buff, info):
557
- value = None
558
- # If there is not enough data available.
559
- if len(buff) - buff.position < 8:
560
- info.complete = False
561
- return None
562
- value = buff.getDouble()
563
- return GXFloat64(value)
564
-
565
- #
566
- # Get float value from DLMS data.
567
- #
568
- # buff
569
- # Received DLMS data.
570
- # info
571
- # Data info.
572
- # Parsed float value.
573
- #
574
- @classmethod
575
- def getFloat(cls, settings, buff, info):
576
- value = None
577
- # If there is not enough data available.
578
- if len(buff) - buff.position < 4:
579
- info.complete = False
580
- return None
581
- value = buff.getFloat()
582
- return GXFloat32(value)
583
-
584
- #
585
- # Get enumeration value from DLMS data.
586
- #
587
- # buff
588
- # Received DLMS data.
589
- # info
590
- # Data info.
591
- # parsed enumeration value.
592
- #
593
- @classmethod
594
- def getEnum(cls, buff, info):
595
- value = None
596
- # If there is not enough data available.
597
- if len(buff) - buff.position < 1:
598
- info.complete = False
599
- return None
600
- value = buff.getUInt8()
601
- return GXEnum(value)
602
-
603
- #
604
- # Get UInt64 value from DLMS data.
605
- #
606
- # buff
607
- # Received DLMS data.
608
- # info
609
- # Data info.
610
- # parsed UInt64 value.
611
- #
612
- @classmethod
613
- def getUInt64(cls, buff, info):
614
- value = None
615
- # If there is not enough data available.
616
- if len(buff) - buff.position < 8:
617
- info.complete = False
618
- return None
619
- value = buff.getUInt64()
620
- return GXUInt64(value)
621
-
622
- #
623
- # Get Int64 value from DLMS data.
624
- #
625
- # buff
626
- # Received DLMS data.
627
- # info
628
- # Data info.
629
- # parsed Int64 value.
630
- #
631
- @classmethod
632
- def getInt64(cls, buff, info):
633
- value = None
634
- # If there is not enough data available.
635
- if len(buff) - buff.position < 8:
636
- info.complete = False
637
- return None
638
- value = buff.getInt64()
639
- return value
640
-
641
- #
642
- # Get UInt16 value from DLMS data.
643
- #
644
- # buff
645
- # Received DLMS data.
646
- # info
647
- # Data info.
648
- # parsed UInt16 value.
649
- #
650
- @classmethod
651
- def getUInt16(cls, buff, info):
652
- value = None
653
- # If there is not enough data available.
654
- if len(buff) - buff.position < 2:
655
- info.complete = False
656
- return None
657
- value = buff.getUInt16()
658
- return GXUInt16(value)
659
-
660
- #pylint: disable=too-many-arguments
661
- @classmethod
662
- def getCompactArrayItem(cls, settings, buff, dt, list_, len_):
663
- if isinstance(dt, list):
664
- tmp2 = list()
665
- for it in dt:
666
- if isinstance(it, DataType):
667
- cls.getCompactArrayItem(settings, buff, it, tmp2, 1)
668
- else:
669
- cls.getCompactArrayItem(settings, buff, it, tmp2, 1)
670
- list_.append(tmp2)
671
- return
672
-
673
- tmp = _GXDataInfo()
674
- tmp.type_ = dt
675
- start = buff.position
676
- if dt == DataType.STRING:
677
- while buff.position - start < len_:
678
- tmp.clear()
679
- tmp.type = dt
680
- list_.append(cls.getString(buff, tmp, False))
681
- if not tmp.complete:
682
- break
683
- elif dt == DataType.OCTET_STRING:
684
- while buff.position - start < len_:
685
- tmp.clear()
686
- tmp.type = dt
687
- list_.append(cls.getOctetString(settings, buff, tmp, False))
688
- if not tmp.complete:
689
- break
690
- else:
691
- while buff.position - start < len_:
692
- tmp.clear()
693
- tmp.type_ = dt
694
- list_.append(cls.getData(settings, buff, tmp))
695
- if not tmp.complete:
696
- break
697
-
698
- @classmethod
699
- def getDataTypes(cls, buff, cols, len_):
700
- dt = None
701
- pos = 0
702
- while pos != len_:
703
- dt = buff.getUInt8()
704
- if dt == DataType.ARRAY:
705
- cnt = buff.getUInt16()
706
- tmp = list()
707
- tmp2 = list()
708
- cls.getDataTypes(buff, tmp, 1)
709
- i = 0
710
- while i != cnt:
711
- tmp2.append(tmp)
712
- i += 1
713
- cols.append(tmp2)
714
- elif dt == DataType.STRUCTURE:
715
- tmp = list()
716
- cls.getDataTypes(buff, tmp, buff.getUInt8())
717
- cols.append(tmp)
718
- else:
719
- cols.append(dt)
720
- pos += 1
721
-
722
- #
723
- # Get compact array value from DLMS data.
724
- #
725
- # buff
726
- # Received DLMS data.
727
- # info
728
- # Data info.
729
- # parsed UInt16 value.
730
- #
731
- @classmethod
732
- def getCompactArray(cls, settings, buff, info):
733
- # pylint: disable=too-many-nested-blocks
734
-
735
- # If there is not enough data available.
736
- if len(buff) - buff.position < 2:
737
- info.complete = False
738
- return None
739
- dt = buff.getUInt8()
740
- if dt == DataType.ARRAY:
741
- raise ValueError("Invalid compact array data.")
742
- len_ = _GXCommon.getObjectCount(buff)
743
- list_ = list()
744
- if dt == DataType.STRUCTURE:
745
- # Get data types.
746
- cols = list()
747
- cls.getDataTypes(buff, cols, len_)
748
- len_ = _GXCommon.getObjectCount(buff)
749
- start = buff.position
750
- while buff.position - start < len_:
751
- row = list()
752
- pos = 0
753
- while pos != len(cols):
754
- if isinstance(cols[pos], GXArray):
755
- cls.getCompactArrayItem(settings, buff, cols[pos], row, 1)
756
- elif isinstance(cols[pos], GXStructure):
757
- tmp2 = list()
758
- cls.getCompactArrayItem(settings, buff, cols[pos], tmp2, 1)
759
- row.append(tmp2[0])
760
- else:
761
- cls.getCompactArrayItem(settings, buff, cols[pos], row, 1)
762
- if buff.position == len(buff):
763
- break
764
- pos += 1
765
- # If all columns are read.
766
- if len(row) >= len(cols):
767
- list_.append(row)
768
- else:
769
- break
770
- else:
771
- cls.getCompactArrayItem(settings, buff, dt, list_, len_)
772
- return list_
773
-
774
- #
775
- # Get UInt8 value from DLMS data.
776
- #
777
- # buff
778
- # Received DLMS data.
779
- # info
780
- # Data info.
781
- # parsed UInt8 value.
782
- #
783
- @classmethod
784
- def getUInt8(cls, buff, info):
785
- value = None
786
- # If there is not enough data available.
787
- if len(buff) - buff.position < 1:
788
- info.complete = False
789
- return None
790
- value = buff.getUInt8() & 0xFF
791
- return GXUInt8(value)
792
-
793
- #
794
- # Get Int16 value from DLMS data.
795
- #
796
- # buff
797
- # Received DLMS data.
798
- # info
799
- # Data info.
800
- # parsed Int16 value.
801
- #
802
- @classmethod
803
- def getInt16(cls, buff, info):
804
- value = None
805
- # If there is not enough data available.
806
- if len(buff) - buff.position < 2:
807
- info.complete = False
808
- return None
809
- value = int(buff.getInt16())
810
- return value
811
-
812
- #
813
- # Get Int8 value from DLMS data.
814
- #
815
- # buff
816
- # Received DLMS data.
817
- # info
818
- # Data info.
819
- # parsed Int8 value.
820
- #
821
- @classmethod
822
- def getInt8(cls, buff, info):
823
- value = None
824
- # If there is not enough data available.
825
- if len(buff) - buff.position < 1:
826
- info.complete = False
827
- return None
828
- value = int(buff.getInt8())
829
- return GXInt8(value)
830
-
831
- #
832
- # Get BCD value from DLMS data.
833
- #
834
- # buff: Received DLMS data.
835
- # info: Data info.
836
- # Returns parsed BCD value.
837
- #
838
- @classmethod
839
- def getBcd(cls, buff, info):
840
- # If there is not enough data available.
841
- if len(buff) - buff.position < 1:
842
- info.complete = False
843
- return None
844
- value = buff.getUInt8()
845
- return value
846
-
847
- #
848
- # Get UTF string value from DLMS data.
849
- #
850
- # buff
851
- # Received DLMS data.
852
- # info
853
- # Data info.
854
- # parsed UTF string value.
855
- #
856
- @classmethod
857
- def getUtfString(cls, buff, info, knownType):
858
- if knownType:
859
- len_ = len(buff)
860
- else:
861
- len_ = _GXCommon.getObjectCount(buff)
862
- # If there is not enough data available.
863
- if len(buff) - buff.position < len_:
864
- info.complete = False
865
- return None
866
- if len_ > 0:
867
- value = buff.getString(buff.position, len_)
868
- buff.position += len_
869
- else:
870
- value = ""
871
- return value
872
-
873
- #
874
- # Get octet string value from DLMS data.
875
- #
876
- # buff
877
- # Received DLMS data.
878
- # info
879
- # Data info.
880
- # parsed octet string value.
881
- #
882
- @classmethod
883
- def getOctetString(cls, settings, buff, info, knownType):
884
- # pylint: disable=too-many-nested-blocks,broad-except
885
- value = None
886
- if knownType:
887
- len_ = len(buff)
888
- else:
889
- len_ = _GXCommon.getObjectCount(buff)
890
- # If there is not enough data available.
891
- if len(buff) - buff.position < len_:
892
- info.complete = False
893
- return None
894
- tmp = bytearray(len_)
895
- buff.get(tmp)
896
- value = tmp
897
- return value
898
-
899
- #
900
- # Get string value from DLMS data.
901
- #
902
- # buff
903
- # Received DLMS data.
904
- # info
905
- # Data info.
906
- # parsed string value.
907
- #
908
- @classmethod
909
- def getString(cls, buff, info, knownType):
910
- value = None
911
- if knownType:
912
- len_ = len(buff)
913
- else:
914
- len_ = _GXCommon.getObjectCount(buff)
915
- # If there is not enough data available.
916
- if len(buff) - buff.position < len_:
917
- info.complete = False
918
- return None
919
- if len_ > 0:
920
- value = buff.getString(buff.position, len_)
921
- buff.position += len_
922
- else:
923
- value = ""
924
- return value
925
-
926
- #
927
- # Get UInt32 value from DLMS data.
928
- #
929
- # buff
930
- # Received DLMS data.
931
- # info
932
- # Data info.
933
- # parsed UInt32 value.
934
- #
935
- @classmethod
936
- def getUInt32(cls, buff, info):
937
- # If there is not enough data available.
938
- if len(buff) - buff.position < 4:
939
- info.complete = False
940
- return None
941
- value = buff.getUInt32()
942
- return GXUInt32(value)
943
-
944
- #
945
- # Get Int32 value from DLMS data.
946
- #
947
- # buff
948
- # Received DLMS data.
949
- # info
950
- # Data info.
951
- # parsed Int32 value.
952
- #
953
- @classmethod
954
- def getInt32(cls, buff, info):
955
- # If there is not enough data available.
956
- if len(buff) - buff.position < 4:
957
- info.complete = False
958
- return None
959
- value = int(buff.getInt32())
960
- return value
961
-
962
- #
963
- # Get bit string value from DLMS data.
964
- #
965
- # buff
966
- # Received DLMS data.
967
- # info
968
- # Data info.
969
- # parsed bit string value.
970
- #
971
- @classmethod
972
- def getBitString(cls, buff, info):
973
- cnt = cls.getObjectCount(buff)
974
- t = cnt
975
- t /= 8
976
- if cnt % 8 != 0:
977
- t += 1
978
- byteCnt = int(t)
979
- # If there is not enough data available.
980
- if len(buff) - buff.position < byteCnt:
981
- info.complete = False
982
- return None
983
- sb = ""
984
- while cnt > 0:
985
- sb += cls.toBitString(buff.getInt8(), cnt)
986
- cnt -= 8
987
- return GXBitString(sb)
988
-
989
- #
990
- # Get boolean value from DLMS data.
991
- #
992
- # buff
993
- # Received DLMS data.
994
- # info
995
- # Data info.
996
- # parsed boolean value.
997
- #
998
- @classmethod
999
- def getBoolean(cls, buff, info):
1000
- # If there is not enough data available.
1001
- if len(buff) - buff.position < 1:
1002
- info.complete = False
1003
- return None
1004
- value = bool(buff.getUInt8() != 0)
1005
- return value
1006
-
1007
- #
1008
- # Get HDLC address from byte array.
1009
- #
1010
- # buff
1011
- # byte array.
1012
- # HDLC address.
1013
- #
1014
- @classmethod
1015
- def getHDLCAddress(cls, buff):
1016
- size = 0
1017
- pos = buff.position
1018
- while pos != len(buff):
1019
- size += 1
1020
- if buff.getUInt8(pos) & 0x1 == 1:
1021
- break
1022
- pos += 1
1023
- if size == 1:
1024
- ret = (buff.getUInt8() & 0xFE) >> 1
1025
- elif size == 2:
1026
- ret = buff.getUInt16()
1027
- ret = ((ret & 0xFE) >> 1) | ((ret & 0xFE00) >> 2)
1028
- elif size == 4:
1029
- ret = buff.getUInt32()
1030
- ret = ((ret & 0xFE) >> 1) | ((ret & 0xFE00) >> 2) | ((ret & 0xFE0000) >> 3) | ((ret & 0xFE000000) >> 4)
1031
- else:
1032
- raise ValueError("Wrong size.")
1033
- return ret
1034
-
1035
- #
1036
- # Convert object to DLMS bytes.
1037
- #
1038
- # settings: DLMS settings.
1039
- # buff: Byte buffer where data is write.
1040
- # dataType: Data type.
1041
- # value: Added Value.
1042
- #
1043
- @classmethod
1044
- def setData(cls, settings, buff, dataType, value):
1045
- if dataType in (DataType.ARRAY, DataType.STRUCTURE) and isinstance(value, (GXByteBuffer, bytearray, bytes)):
1046
- # If byte array is added do not add type.
1047
- buff.set(value)
1048
- return
1049
-
1050
- if isinstance(value, cdt.CommonDataType):
1051
- buff.set(value.encoding)
1052
- else:
1053
- buff.setUInt8(dataType)
1054
- if dataType == DataType.NONE:
1055
- pass
1056
- elif dataType == DataType.BOOLEAN:
1057
- if value:
1058
- buff.setUInt8(1)
1059
- else:
1060
- buff.setUInt8(0)
1061
- elif dataType == DataType.UINT8:
1062
- buff.setUInt8(value)
1063
- elif dataType in (DataType.INT8, DataType.ENUM):
1064
- buff.setInt8(value)
1065
- elif dataType in (DataType.UINT16, DataType.INT16):
1066
- buff.setUInt16(value)
1067
- elif dataType in (DataType.UINT32, DataType.INT32):
1068
- buff.setUInt32(value)
1069
- elif dataType in (DataType.UINT64, DataType.INT64):
1070
- buff.setUInt64(value)
1071
- elif dataType == DataType.FLOAT32:
1072
- buff.setFloat(value)
1073
- elif dataType == DataType.FLOAT64:
1074
- buff.setDouble(value)
1075
- elif dataType == DataType.BITSTRING:
1076
- cls.setBitString(buff, value, True)
1077
- elif dataType == DataType.STRING:
1078
- cls.setString(buff, value)
1079
- elif dataType == DataType.STRING_UTF8:
1080
- cls.setUtfString(buff, value)
1081
- elif dataType == DataType.OCTET_STRING:
1082
- if isinstance(value, GXDate):
1083
- # Add size
1084
- buff.setUInt8(5)
1085
- cls.setDate(buff, value)
1086
- elif isinstance(value, GXTime):
1087
- # Add size
1088
- buff.setUInt8(4)
1089
- cls.setTime(buff, value)
1090
- elif isinstance(value, (GXDateTime, datetime)):
1091
- buff.setUInt8(12)
1092
- cls.setDateTime(settings, buff, value)
1093
- else:
1094
- cls.setOctetString(buff, value)
1095
- elif dataType in (DataType.ARRAY, DataType.STRUCTURE):
1096
- cls.setArray(settings, buff, value)
1097
- elif dataType == DataType.BCD:
1098
- cls.setBcd(buff, value)
1099
- elif dataType == DataType.COMPACT_ARRAY:
1100
- # Compact array is not work with python because we don't know data
1101
- # types of each element.
1102
- raise ValueError("Invalid data type.")
1103
- elif dataType == DataType.DATETIME:
1104
- cls.setDateTime(settings, buff, value)
1105
- elif dataType == DataType.DATE:
1106
- cls.setDate(buff, value)
1107
- elif dataType == DataType.TIME:
1108
- cls.setTime(buff, value)
1109
- else:
1110
- raise ValueError("Invalid data type.")
1111
-
1112
- #
1113
- # Convert time to DLMS bytes.
1114
- #
1115
- # buff
1116
- # Byte buffer where data is write.
1117
- # value
1118
- # Added value.
1119
- #
1120
- @classmethod
1121
- def setTime(cls, buff, value):
1122
- dt = cls.__getDateTime(value)
1123
- # Add time.
1124
- if dt.skip & DateTimeSkips.HOUR != DateTimeSkips.NONE:
1125
- buff.setUInt8(0xFF)
1126
- else:
1127
- buff.setUInt8(dt.value.hour)
1128
- if dt.skip & DateTimeSkips.MINUTE != DateTimeSkips.NONE:
1129
- buff.setUInt8(0xFF)
1130
- else:
1131
- buff.setUInt8(dt.value.minute)
1132
- if dt.skip & DateTimeSkips.SECOND != DateTimeSkips.NONE:
1133
- buff.setUInt8(0xFF)
1134
- else:
1135
- buff.setUInt8(dt.value.second)
1136
- if dt.skip & DateTimeSkips.MILLISECOND != DateTimeSkips.NONE:
1137
- # Hundredth of seconds is not used.
1138
- buff.setUInt8(0xFF)
1139
- else:
1140
- ms = dt.value.microsecond
1141
- if ms != 0:
1142
- ms /= 10000
1143
- buff.setUInt8(int(ms))
1144
-
1145
- #
1146
- # Convert date to DLMS bytes.
1147
- #
1148
- # buff
1149
- # Byte buffer where data is write.
1150
- # value
1151
- # Added value.
1152
- #
1153
- @classmethod
1154
- def setDate(cls, buff, value):
1155
- dt = cls.__getDateTime(value)
1156
- # Add year.
1157
- if dt.skip & DateTimeSkips.YEAR != DateTimeSkips.NONE:
1158
- buff.setUInt16(0xFFFF)
1159
- else:
1160
- buff.setUInt16(dt.value.year)
1161
- # Add month
1162
- if dt.extra & DateTimeExtraInfo.DST_BEGIN != 0:
1163
- buff.setUInt8(0xFE)
1164
- elif dt.extra & DateTimeExtraInfo.DST_END != 0:
1165
- buff.setUInt8(0xFD)
1166
- elif dt.skip & DateTimeSkips.MONTH != 0:
1167
- buff.setUInt8(0xFF)
1168
- else:
1169
- buff.setUInt8(dt.value.month)
1170
- # Add day
1171
- if dt.extra & DateTimeExtraInfo.LAST_DAY2 != DateTimeSkips.NONE:
1172
- buff.setUInt8(0xFD)
1173
- elif dt.extra & DateTimeExtraInfo.LAST_DAY != DateTimeSkips.NONE:
1174
- buff.setUInt8(0xFE)
1175
- elif dt.skip & DateTimeSkips.DAY != DateTimeSkips.NONE:
1176
- buff.setUInt8(0xFF)
1177
- else:
1178
- buff.setUInt8(dt.value.day)
1179
-
1180
- # Day of week.
1181
- if dt.skip & DateTimeSkips.DAY_OF_WEEK != DateTimeSkips.NONE:
1182
- buff.setUInt8(0xFF)
1183
- else:
1184
- if dt.dayOfWeek == 0:
1185
- buff.setUInt8(dt.value.weekday() + 1)
1186
- else:
1187
- buff.setUInt8(dt.dayOfWeek)
1188
-
1189
- @classmethod
1190
- def __getDateTime(cls, value):
1191
- dt = None
1192
- if isinstance(value, (GXDateTime)):
1193
- dt = value
1194
- elif isinstance(value, (datetime, str)):
1195
- dt = GXDateTime(value)
1196
- dt.skip |= DateTimeSkips.MILLISECOND
1197
- else:
1198
- raise ValueError("Invalid date format.")
1199
- return dt
1200
-
1201
- #
1202
- # Convert date time to DLMS bytes.
1203
- #
1204
- # buff
1205
- # Byte buffer where data is write.
1206
- # value
1207
- # Added value.
1208
- #
1209
- @classmethod
1210
- def setDateTime(cls, settings, buff, value):
1211
- dt = cls.__getDateTime(value)
1212
- # Add year.
1213
- if dt.skip & DateTimeSkips.YEAR != DateTimeSkips.NONE:
1214
- buff.setUInt16(0xFFFF)
1215
- else:
1216
- buff.setUInt16(dt.value.year)
1217
- # Add month
1218
- if dt.extra & DateTimeExtraInfo.DST_BEGIN != 0:
1219
- buff.setUInt8(0xFD)
1220
- elif dt.extra & DateTimeExtraInfo.DST_END != 0:
1221
- buff.setUInt8(0xFE)
1222
- elif dt.skip & DateTimeSkips.MONTH != DateTimeSkips.NONE:
1223
- buff.setUInt8(0xFF)
1224
- else:
1225
- buff.setUInt8(dt.value.month)
1226
-
1227
- # Add day
1228
- if dt.extra & DateTimeExtraInfo.LAST_DAY2 != DateTimeSkips.NONE:
1229
- buff.setUInt8(0xFD)
1230
- elif dt.extra & DateTimeExtraInfo.LAST_DAY != DateTimeSkips.NONE:
1231
- buff.setUInt8(0xFE)
1232
- elif dt.skip & DateTimeSkips.DAY != DateTimeSkips.NONE:
1233
- buff.setUInt8(0xFF)
1234
- else:
1235
- buff.setUInt8(dt.value.day)
1236
- # Day of week.
1237
- if dt.skip & DateTimeSkips.DAY_OF_WEEK != DateTimeSkips.NONE:
1238
- buff.setUInt8(0xFF)
1239
- else:
1240
- if dt.dayOfWeek == 0:
1241
- buff.setUInt8(dt.value.weekday() + 1)
1242
- else:
1243
- buff.setUInt8(dt.dayOfWeek)
1244
- # Add time.
1245
- if dt.skip & DateTimeSkips.HOUR != DateTimeSkips.NONE:
1246
- buff.setUInt8(0xFF)
1247
- else:
1248
- buff.setUInt8(dt.value.hour)
1249
- if dt.skip & DateTimeSkips.MINUTE != DateTimeSkips.NONE:
1250
- buff.setUInt8(0xFF)
1251
- else:
1252
- buff.setUInt8(dt.value.minute)
1253
- if dt.skip & DateTimeSkips.SECOND != DateTimeSkips.NONE:
1254
- buff.setUInt8(0xFF)
1255
- else:
1256
- buff.setUInt8(dt.value.second)
1257
- if dt.skip & DateTimeSkips.MILLISECOND != DateTimeSkips.NONE:
1258
- # Hundredth of seconds is not used.
1259
- buff.setUInt8(0xFF)
1260
- else:
1261
- ms = dt.value.microsecond
1262
- if ms != 0:
1263
- ms /= 10000
1264
- buff.setUInt8(int(ms))
1265
- # devitation not used.
1266
- if dt.skip & DateTimeSkips.DEVITATION != DateTimeSkips.NONE:
1267
- buff.setUInt16(0x8000)
1268
- else:
1269
- # Add devitation.
1270
- try:
1271
- d = int(dt.value.utcoffset().total_seconds() / 60)
1272
- except AttributeError:
1273
- d = 0
1274
- if not (settings and settings.useUtc2NormalTime):
1275
- d = -d
1276
- buff.setUInt16(d)
1277
- # Add clock_status
1278
- if dt.skip & DateTimeSkips.STATUS == DateTimeSkips.NONE:
1279
- if dt.value.dst() or dt.status & ClockStatus.DAYLIGHT_SAVE_ACTIVE != ClockStatus.OK:
1280
- buff.setUInt8(dt.status | ClockStatus.DAYLIGHT_SAVE_ACTIVE)
1281
- else:
1282
- buff.setUInt8(dt.status)
1283
- else:
1284
- buff.setUInt8(0xFF)
1285
-
1286
- @classmethod
1287
- def setBcd(cls, buff, value):
1288
- buff.setUInt8(value)
1289
-
1290
- @classmethod
1291
- def setArray(cls, settings, buff, value):
1292
- if value:
1293
- _GXCommon.setObjectCount(len(value), buff)
1294
- for it in value:
1295
- cls.setData(settings, buff, cls.getDLMSDataType(it), it)
1296
- else:
1297
- _GXCommon.setObjectCount(0, buff)
1298
-
1299
- @classmethod
1300
- def setOctetString(cls, buff, value):
1301
- if isinstance(value, str):
1302
- tmp = GXByteBuffer.hexToBytes(value)
1303
- _GXCommon.setObjectCount(len(tmp), buff)
1304
- buff.set(tmp)
1305
- elif isinstance(value, GXByteBuffer):
1306
- cls.setObjectCount(len(value), buff)
1307
- buff.set(value)
1308
- elif isinstance(value, (bytearray, bytes)):
1309
- cls.setObjectCount(len(value), buff)
1310
- buff.set(value)
1311
- elif value is None:
1312
- cls.setObjectCount(0, buff)
1313
- else:
1314
- raise ValueError("Invalid data type.")
1315
-
1316
- @classmethod
1317
- def setUtfString(cls, buff, value):
1318
- if value:
1319
- tmp = value.encode()
1320
- _GXCommon.setObjectCount(len(tmp), buff)
1321
- buff.set(tmp)
1322
- else:
1323
- buff.setUInt8(0)
1324
-
1325
- @classmethod
1326
- def setString(cls, buff, value):
1327
- if value:
1328
- _GXCommon.setObjectCount(len(value), buff)
1329
- buff.set(_GXCommon.getBytes(value))
1330
- else:
1331
- buff.setUInt8(0)
1332
-
1333
- @classmethod
1334
- def setBitString(cls, buff, val1, addCount):
1335
- value = val1
1336
- if isinstance(value, GXBitString):
1337
- value = value.value
1338
- if isinstance(value, str):
1339
- val = 0
1340
- str_ = str(value)
1341
- if addCount:
1342
- _GXCommon.setObjectCount(len(str_), buff)
1343
- index = 7
1344
- pos = 0
1345
- while pos != len(str_):
1346
- it = str_[pos]
1347
- if it == '1':
1348
- val |= (1 << index)
1349
- elif it != '0':
1350
- raise ValueError("Not a bit string.")
1351
- index -= 1
1352
- if index == -1:
1353
- index = 7
1354
- buff.setUInt8(val)
1355
- val = 0
1356
- pos += 1
1357
- if index != 7:
1358
- buff.setUInt8(val)
1359
- elif isinstance(value, (bytearray, bytes)):
1360
- _GXCommon.setObjectCount(8 * len(value), buff)
1361
- buff.set(value)
1362
- elif isinstance(value, int):
1363
- _GXCommon.setObjectCount(8, buff)
1364
- buff.setUInt8(value)
1365
- elif value is None:
1366
- buff.setUInt8(0)
1367
- else:
1368
- raise ValueError("BitString must give as string.")
1369
-
1370
- @classmethod
1371
- def getDataType(cls, value):
1372
- if value == DataType.NONE:
1373
- ret = None
1374
- elif value == DataType.OCTET_STRING:
1375
- ret = bytes.__class__
1376
- elif value == DataType.ENUM:
1377
- ret = GXEnum.__class__
1378
- elif value == DataType.INT8:
1379
- ret = int.__class__
1380
- elif value == DataType.INT16:
1381
- ret = int.__class__
1382
- elif value == DataType.INT32:
1383
- ret = int.__class__
1384
- elif value == DataType.INT64:
1385
- ret = int.__class__
1386
- elif value == DataType.UINT8:
1387
- ret = GXUInt8.__class__
1388
- elif value == DataType.UINT16:
1389
- ret = GXUInt16.__class__
1390
- elif value == DataType.UINT32:
1391
- ret = GXUInt32.__class__
1392
- elif value == DataType.UINT64:
1393
- ret = GXUInt64.__class__
1394
- elif value == DataType.TIME:
1395
- ret = GXTime.__class__
1396
- elif value == DataType.DATE:
1397
- ret = GXDate.__class__
1398
- elif value == DataType.DATETIME:
1399
- ret = GXDateTime.__class__
1400
- elif value == DataType.ARRAY:
1401
- ret = list.__class__
1402
- elif value == DataType.STRING:
1403
- ret = str.__class__
1404
- elif value == DataType.BOOLEAN:
1405
- ret = bool.__class__
1406
- elif value == DataType.FLOAT32:
1407
- ret = GXFloat32.__class__
1408
- elif value == DataType.FLOAT64:
1409
- ret = GXFloat64.__class__
1410
- elif value == DataType.BITSTRING:
1411
- ret = GXBitString.__class__
1412
- else:
1413
- raise ValueError("Invalid value.")
1414
- return ret
1415
-
1416
- @classmethod
1417
- def getDLMSDataType(cls, value):
1418
- # pylint: disable=undefined-variable
1419
- if value is None:
1420
- ret = DataType.NONE
1421
- elif isinstance(value, (bytes, bytearray, GXByteBuffer)):
1422
- ret = DataType.OCTET_STRING
1423
- elif isinstance(value, (GXEnum)):
1424
- ret = DataType.ENUM
1425
- elif isinstance(value, (GXInt8)):
1426
- ret = DataType.INT8
1427
- elif isinstance(value, (GXInt16)):
1428
- ret = DataType.INT16
1429
- elif isinstance(value, (GXInt64)):
1430
- ret = DataType.INT64
1431
- elif isinstance(value, (GXUInt8)):
1432
- ret = DataType.UINT8
1433
- elif isinstance(value, (GXUInt16)):
1434
- ret = DataType.UINT16
1435
- elif isinstance(value, (GXUInt32)):
1436
- ret = DataType.UINT32
1437
- elif isinstance(value, (GXUInt64)):
1438
- ret = DataType.UINT64
1439
- elif isinstance(value, (bool)):
1440
- ret = DataType.BOOLEAN
1441
- elif isinstance(value, (GXInt32, int)):
1442
- ret = DataType.INT32
1443
- elif isinstance(value, (GXTime)):
1444
- ret = DataType.TIME
1445
- elif isinstance(value, (GXDate)):
1446
- ret = DataType.DATE
1447
- elif isinstance(value, (datetime, GXDateTime)):
1448
- ret = DataType.DATETIME
1449
- elif isinstance(value, (GXStructure)):
1450
- ret = DataType.STRUCTURE
1451
- elif isinstance(value, (GXArray, list)):
1452
- ret = DataType.ARRAY
1453
- elif isinstance(value, (str)):
1454
- ret = DataType.STRING
1455
- elif isinstance(value, (GXFloat64)):
1456
- ret = DataType.FLOAT64
1457
- elif isinstance(value, (GXFloat32, complex, float)):
1458
- ret = DataType.FLOAT32
1459
- elif isinstance(value, (GXBitString)):
1460
- ret = DataType.BITSTRING
1461
- else:
1462
- ret = None
1463
- if ret is None:
1464
- #Python 2.7 uses unicode.
1465
- try:
1466
- if isinstance(value, (unicode)):
1467
- ret = DataType.STRING
1468
- except Exception:
1469
- ret = None
1470
- if ret is None:
1471
- raise ValueError("Invalid datatype " + type(value) + ".")
1472
- return ret
1473
-
1474
- @classmethod
1475
- def getDataTypeSize(cls, type_):
1476
- size = -1
1477
- if type_ == DataType.BCD:
1478
- size = 1
1479
- elif type_ == DataType.BOOLEAN:
1480
- size = 1
1481
- elif type_ == DataType.DATE:
1482
- size = 5
1483
- elif type_ == DataType.DATETIME:
1484
- size = 12
1485
- elif type_ == DataType.ENUM:
1486
- size = 1
1487
- elif type_ == DataType.FLOAT32:
1488
- size = 4
1489
- elif type_ == DataType.FLOAT64:
1490
- size = 8
1491
- elif type_ == DataType.INT16:
1492
- size = 2
1493
- elif type_ == DataType.INT32:
1494
- size = 4
1495
- elif type_ == DataType.INT64:
1496
- size = 8
1497
- elif type_ == DataType.INT8:
1498
- size = 1
1499
- elif type_ == DataType.NONE:
1500
- size = 0
1501
- elif type_ == DataType.TIME:
1502
- size = 4
1503
- elif type_ == DataType.UINT16:
1504
- size = 2
1505
- elif type_ == DataType.UINT32:
1506
- size = 4
1507
- elif type_ == DataType.UINT64:
1508
- size = 8
1509
- elif type_ == DataType.UINT8:
1510
- size = 1
1511
- return size
1512
-
1513
- @classmethod
1514
- def toLogicalName(cls, value):
1515
- if isinstance(value, bytearray):
1516
- if not value:
1517
- value = bytearray(6)
1518
- if len(value) == 6:
1519
- return str(value[0]) + "." + str(value[1]) + "." + str(value[2]) + "." + str(value[3]) + "." + str(value[4]) + "." + str(value[5])
1520
- raise ValueError("Invalid Logical name.")
1521
- return str(value)
1522
-
1523
- @classmethod
1524
- def logicalNameToBytes(cls, value):
1525
- if not value:
1526
- return bytearray(6)
1527
- items = value.split('.')
1528
- if len(items) != 6:
1529
- raise ValueError("Invalid Logical name.")
1530
- buff = bytearray(6)
1531
- pos = 0
1532
- for it in items:
1533
- v = int(it)
1534
- if v < 0 or v > 255:
1535
- raise ValueError("Invalid Logical name.")
1536
- buff[pos] = int(v)
1537
- pos += 1
1538
- return buff
1539
-
1540
- @classmethod
1541
- def getGeneralizedTime(cls, dateString):
1542
- year = int(dateString[0:4])
1543
- month = int(dateString[4:6])
1544
- day = int(dateString[6:8])
1545
- hour = int(dateString[8:10])
1546
- minute = int(dateString[10:12])
1547
- #If UTC time.
1548
- if dateString.endsWith("Z"):
1549
- if len(dateString) > 13:
1550
- second = int(dateString[12:14])
1551
- return datetime(year, month, day, hour, minute, second, 0, tzinfo=GXTimeZone(0))
1552
-
1553
- if len(dateString) > 17:
1554
- second = int(dateString.substring(12, 14))
1555
- tz = dateString[dateString.length() - 4:]
1556
- return datetime(year, month, day, hour, minute, second, 0, tzinfo=GXTimeZone(tz))
1557
-
1558
- @classmethod
1559
- def generalizedTime(cls, value):
1560
- #Convert to UTC time.
1561
- if isinstance(value, (GXDateTime)):
1562
- value = value.value
1563
- value = value.utctimetuple()
1564
- sb = cls.integerString(value.tm_year, 4)
1565
- sb += cls.integerString(value.tm_mon, 2)
1566
- sb += cls.integerString(value.tm_mday, 2)
1567
- sb += cls.integerString(value.tm_hour, 2)
1568
- sb += cls.integerString(value.tm_min, 2)
1569
- sb += cls.integerString(value.tm_sec, 2)
1570
- #UTC time.
1571
- sb += "Z"
1572
- return sb
1573
-
1574
- @classmethod
1575
- def encryptManufacturer(cls, flagName):
1576
- if len(flagName) != 3:
1577
- raise ValueError("Invalid Flag name.")
1578
- value = ((flagName.charAt(0) - 0x40) & 0x1f)
1579
- value <<= 5
1580
- value += ((flagName.charAt(0) - 0x40) & 0x1f)
1581
- value <<= 5
1582
- value += ((flagName.charAt(0) - 0x40) & 0x1f)
1583
- return value
1584
-
1585
- @classmethod
1586
- def decryptManufacturer(cls, value):
1587
- tmp = (value >> 8 | value << 8)
1588
- c = str(((tmp & 0x1f) + 0x40))
1589
- tmp = (tmp >> 5)
1590
- c1 = str(((tmp & 0x1f) + 0x40))
1591
- tmp = (tmp >> 5)
1592
- c2 = str(((tmp & 0x1f) + 0x40))
1593
- return str(c2, c1, c)
1594
-
1595
- @classmethod
1596
- def idisSystemTitleToString(cls, st):
1597
- sb = '\n'
1598
- sb += "IDIS system title:\n"
1599
- sb += "Manufacturer Code: "
1600
- sb += cls.__getChar(st[0]) + cls.__getChar(st[1]) + cls.__getChar(st[2])
1601
- sb += "\nFunction type: "
1602
- ft = st[4] >> 4
1603
- add = False
1604
- if (ft & 0x1) != 0:
1605
- sb += "Disconnector extension"
1606
- add = True
1607
- if (ft & 0x2) != 0:
1608
- if add:
1609
- sb += ", "
1610
- add = True
1611
- sb += "Load Management extension"
1612
-
1613
- if (ft & 0x4) != 0:
1614
- if add:
1615
- sb += ", "
1616
- sb += "Multi Utility extension"
1617
- #Serial number
1618
- sn = (st[4] & 0xF) << 24
1619
- sn |= st[5] << 16
1620
- sn |= st[6] << 8
1621
- sn |= st[7]
1622
- sb += '\n'
1623
- sb += "Serial number: "
1624
- sb += str(sn) + '\n'
1625
- return sb
1626
-
1627
- @classmethod
1628
- def dlmsSystemTitleToString(cls, st):
1629
- sb = '\n'
1630
- sb += "IDIS system title:\n"
1631
- sb += "Manufacturer Code: "
1632
- sb += cls.__getChar(st[0]) + cls.__getChar(st[1]) + cls.__getChar(st[2])
1633
- sb += "Serial number: "
1634
- sb += cls.__getChar(st[3]) + cls.__getChar(st[4]) + cls.__getChar(st[5]) + cls.__getChar(st[6]) + cls.__getChar(st[7])
1635
- return sb
1636
-
1637
- @classmethod
1638
- def uniSystemTitleToString(cls, st):
1639
- sb = '\n'
1640
- sb += "UNI/TS system title:\n"
1641
- sb += "Manufacturer: "
1642
- m = st[0] << 8 | st[1]
1643
- sb += cls.decryptManufacturer(m)
1644
- sb += "\nSerial number: "
1645
- sb += GXByteBuffer.toHex((st[7], st[6], st[5], st[4], st[3], st[2]), False)
1646
- return sb
1647
-
1648
- @classmethod
1649
- def __getChar(cls, ch):
1650
- try:
1651
- return str(chr(ch))
1652
- except Exception:
1653
- #If python 2.7 is used.
1654
- #pylint: disable=undefined-variable
1655
- return str(unichr(ch))
1656
-
1657
- @classmethod
1658
- def systemTitleToString(cls, standard, st):
1659
- ###Conver system title to string.
1660
- #pylint: disable=too-many-boolean-expressions
1661
- if standard == Standard.ITALY or not cls.__getChar(st[0]).isalpha() or \
1662
- not cls.__getChar(st[1]).isalpha() or not cls.__getChar(st[2]).isalpha():
1663
- return cls.uniSystemTitleToString(st)
1664
- if standard == Standard.IDIS or not cls.__getChar(st[3]).isdigit() or \
1665
- not cls.__getChar(st[4]).isdigit() or not cls.__getChar(st[5]).isdigit() or \
1666
- not cls.__getChar(st[6]).isdigit() or not cls.__getChar(st[7]).isdigit():
1667
- return cls.idisSystemTitleToString(st)
1668
- return cls.dlmsSystemTitleToString(st)
1669
-
1670
- #Reserved for internal use.
1671
- @classmethod
1672
- def swapBits(cls, value):
1673
- return int(F'{value:08b}'[::-1], 2)
1
+ from datetime import datetime
2
+ from DLMS_SPODES.types import cdt
3
+
4
+ # from ..GXTimeZone import GXTimeZone
5
+ from ..GXByteBuffer import GXByteBuffer
6
+ from ..enums.Standard import Standard
7
+ # from ._GXDataInfo import _GXDataInfo
8
+ # from ..GXBitString import GXBitString
9
+ # from ..enums import DataType
10
+ # from ..enums import DateTimeSkips, DateTimeExtraInfo, ClockStatus
11
+ # from ..TranslatorTags import TranslatorTags
12
+ # from ..TranslatorOutputType import TranslatorOutputType
13
+ # from ..GXArray import GXArray
14
+ # from ..GXStructure import GXStructure
15
+ # from ..GXEnum import GXEnum
16
+ # from ..GXInt8 import GXInt8
17
+ # from ..GXInt16 import GXInt16
18
+ # from ..GXInt32 import GXInt32
19
+ # from ..GXInt64 import GXInt64
20
+ # from ..GXUInt8 import GXUInt8
21
+ # from ..GXUInt16 import GXUInt16
22
+ # from ..GXUInt32 import GXUInt32
23
+ # from ..GXUInt64 import GXUInt64
24
+ # from ..GXDateTime import GXDateTime
25
+ # from ..GXDate import GXDate
26
+ # from ..GXTime import GXTime
27
+ # from ..GXFloat32 import GXFloat32
28
+ # from ..GXFloat64 import GXFloat64
29
+
30
+
31
+ class _GXCommon:
32
+ """This class is for internal use only and is subject to changes or removal in future versions of the API. Don't use it."""
33
+
34
+ # HDLC frame start and end character.
35
+ HDLC_FRAME_START_END = 0x7E
36
+ LLC_SEND_BYTES = bytearray([0xE6, 0xE6, 0x00])
37
+ LLC_REPLY_BYTES = bytearray([0xE6, 0xE7, 0x00])
38
+ DATA_TYPE_OFFSET = 0xFF0000
39
+ zeroes = "00000000000000000000000000000000"
40
+
41
+
42
+ @classmethod
43
+ def getBytes(cls, value):
44
+ """
45
+ Convert string to byte array.
46
+ value: String value.
47
+ returns String as bytes.
48
+ """
49
+ if not value:
50
+ return None
51
+ return value.encode()
52
+
53
+ #
54
+ # Is string hex string.
55
+ #
56
+ # value: String value.
57
+ # Return true, if string is hex string.
58
+ #
59
+ @classmethod
60
+ def isHexString(cls, value):
61
+ # pylint: disable=chained-comparison
62
+ if not value:
63
+ return False
64
+ ch = str()
65
+ pos = 0
66
+ while pos != len(value):
67
+ ch = value.charAt(pos)
68
+ if ch != ' ':
69
+ if not ((ch > 0x40 and ch < 'G') or (ch > 0x60 and ch < 'g') or (ch > '/' and ch < ':')):
70
+ return False
71
+ pos += 1
72
+ return True
73
+
74
+ #
75
+ # Get object count. If first byte is 0x80 or higger it will tell
76
+ # bytes
77
+ # count.
78
+ # data received data.
79
+ # Object count.
80
+ #
81
+ @classmethod
82
+ def getObjectCount(cls, data):
83
+ cnt = data.getUInt8()
84
+ if cnt > 0x80:
85
+ if cnt == 0x81:
86
+ cnt = data.getUInt8()
87
+ elif cnt == 0x82:
88
+ cnt = data.getUInt16()
89
+ elif cnt == 0x84:
90
+ cnt = int(data.getUInt32())
91
+ else:
92
+ raise ValueError("Invalid count.")
93
+ return cnt
94
+
95
+ #
96
+ # Return how many bytes object count takes.
97
+ #
98
+ # count
99
+ # Value
100
+ # Value size in bytes.
101
+ #
102
+ @classmethod
103
+ def getObjectCountSizeInBytes(cls, count):
104
+ if count < 0x80:
105
+ ret = 1
106
+ elif count < 0x100:
107
+ ret = 2
108
+ elif count < 0x10000:
109
+ ret = 3
110
+ else:
111
+ ret = 5
112
+ return ret
113
+
114
+ #
115
+ # Add string to byte buffer.
116
+ #
117
+ # value
118
+ # String to add.
119
+ # bb
120
+ # Byte buffer where string is added.
121
+ #
122
+ @classmethod
123
+ def addString(cls, value, bb):
124
+ bb.set(cdt.OctetString.TAG)
125
+ if not value:
126
+ _GXCommon.setObjectCount(0, bb)
127
+ else:
128
+ _GXCommon.setObjectCount(len(value), bb)
129
+ bb.set(value.encode())
130
+
131
+ #
132
+ # Set item count.
133
+ # count
134
+ # buff
135
+ #
136
+ @classmethod
137
+ def setObjectCount(cls, count, buff):
138
+ if count < 0x80:
139
+ buff.setUInt8(count)
140
+ ret = 1
141
+ elif count < 0x100:
142
+ buff.setUInt8(0x81)
143
+ buff.setUInt8(count)
144
+ ret = 2
145
+ elif count < 0x10000:
146
+ buff.setUInt8(0x82)
147
+ buff.setUInt16(count)
148
+ ret = 3
149
+ else:
150
+ buff.setUInt8(0x84)
151
+ buff.setUInt32(count)
152
+ ret = 5
153
+ return ret
154
+
155
+ #
156
+ # Reserved for internal use.
157
+ #
158
+ @classmethod
159
+ def toBitString(cls, value, count):
160
+ count2 = count
161
+ sb = ""
162
+ if count2 > 0:
163
+ if count2 > 8:
164
+ count2 = 8
165
+ pos = 7
166
+ while pos != 8 - count2 - 1:
167
+ if (value & (1 << pos)) != 0:
168
+ sb += '1'
169
+ else:
170
+ sb += '0'
171
+ pos -= 1
172
+ return sb
173
+
174
+ @classmethod
175
+ def changeType(cls, settings, value, type_):
176
+ #pylint: disable=import-outside-toplevel
177
+ if value is None:
178
+ ret = None
179
+ elif type_ == DataType.NONE:
180
+ ret = GXByteBuffer.hex(value, True)
181
+ elif type_ in (DataType.STRING, DataType.OCTET_STRING) and not value:
182
+ ret = ""
183
+ elif type_ == DataType.OCTET_STRING:
184
+ ret = GXByteBuffer(value)
185
+ # elif type_ == DataType.STRING and not GXByteBuffer.isAsciiString(value):
186
+ # ret = GXByteBuffer(value)
187
+ elif type_ == DataType.STRING:
188
+ ret = cdt.OctetString(value)
189
+ elif type_ == DataType.DATETIME and not value:
190
+ ret = GXDateTime(None)
191
+ elif type_ == DataType.DATE and not value:
192
+ ret = GXDate(None)
193
+ elif type_ == DataType.TIME and not value:
194
+ ret = GXTime(None)
195
+ else:
196
+ info = _GXDataInfo()
197
+ info.type_ = type_
198
+ ret = _GXCommon.getData(settings, GXByteBuffer(value), info)
199
+ if not info.complete:
200
+ raise ValueError("Change type failed. Not enought data.")
201
+ if type_ == DataType.OCTET_STRING and isinstance(ret, bytes):
202
+ ret = GXByteBuffer.hex(ret)
203
+ return ret
204
+
205
+ #
206
+ # Get data from DLMS frame.
207
+ #
208
+ # data
209
+ # received data.
210
+ # info
211
+ # Data info.
212
+ # Received data.
213
+ #
214
+ @classmethod
215
+ def getData(cls, settings, data: GXByteBuffer, info): # info: _GXDataInfo
216
+ value = None
217
+ startIndex = data.position
218
+ if data.position == len(data):
219
+ info.complete = False
220
+ return None
221
+ info.complete = True
222
+ knownType = info.type_ != cdt.NullData
223
+ # Get data type if it is unknown.
224
+ if not knownType:
225
+ info.type_ = cdt.get_common_data_type_from(data.getUInt8().to_bytes(1, "big"))
226
+ if info.type_ == cdt.NullData:
227
+ return value
228
+ if data.position == len(data):
229
+ info.complete = False
230
+ return None
231
+ if info.type_ == cdt.Array or info.type_ == cdt.Structure:
232
+ value = cls.getArray(settings, data, info, startIndex)
233
+ elif info.type_ == DataType.BOOLEAN:
234
+ value = cls.getBoolean(data, info)
235
+ elif info.type_ == DataType.BITSTRING:
236
+ value = cls.getBitString(data, info)
237
+ elif info.type_ == DataType.INT32:
238
+ value = cls.getInt32(data, info)
239
+ elif info.type_ == DataType.UINT32:
240
+ value = cls.getUInt32(data, info)
241
+ elif info.type_ == DataType.STRING:
242
+ value = cls.getString(data, info, knownType)
243
+ elif info.type_ == DataType.STRING_UTF8:
244
+ value = cls.getUtfString(data, info, knownType)
245
+ elif info.type_ == DataType.OCTET_STRING:
246
+ value = cls.getOctetString(settings, data, info, knownType)
247
+ elif info.type_ == DataType.BCD:
248
+ value = cls.getBcd(data, info)
249
+ elif info.type_ == DataType.INT8:
250
+ value = cls.getInt8(data, info)
251
+ elif info.type_ == DataType.INT16:
252
+ value = cls.getInt16(data, info)
253
+ elif info.type_ == DataType.UINT8:
254
+ value = cls.getUInt8(data, info)
255
+ elif info.type_ == DataType.UINT16:
256
+ value = cls.getUInt16(data, info)
257
+ elif info.type_ == DataType.COMPACT_ARRAY:
258
+ value = cls.getCompactArray(settings, data, info)
259
+ elif info.type_ == DataType.INT64:
260
+ value = cls.getInt64(data, info)
261
+ elif info.type_ == DataType.UINT64:
262
+ value = cls.getUInt64(data, info)
263
+ elif info.type_ == DataType.ENUM:
264
+ value = cls.getEnum(data, info)
265
+ elif info.type_ == DataType.FLOAT32:
266
+ value = cls.getFloat(settings, data, info)
267
+ elif info.type_ == DataType.FLOAT64:
268
+ value = cls.getDouble(settings, data, info)
269
+ elif info.type_ == DataType.DATETIME:
270
+ value = cls.getDateTime(settings, data, info)
271
+ elif info.type_ == DataType.DATE:
272
+ value = cls.getDate(data, info)
273
+ elif info.type_ == DataType.TIME:
274
+ value = cls.getTime(data, info)
275
+ else:
276
+ raise ValueError("Invalid data type.")
277
+ return value
278
+
279
+ #
280
+ # Convert value to hex string.
281
+ # value value to convert.
282
+ # desimals Amount of decimals.
283
+ # @return
284
+ #
285
+ @classmethod
286
+ def integerToHex(cls, value, desimals):
287
+ if desimals:
288
+ nbits = desimals * 4
289
+ str_ = hex((value + (1 << nbits)) % (1 << nbits))[2:].upper()
290
+ else:
291
+ str_ = hex(value)[2:].upper()
292
+ if not desimals or desimals == len(str_):
293
+ return str_
294
+ return _GXCommon.zeroes[0: desimals - len(str_)] + str_.upper()
295
+
296
+ #
297
+ # Convert value to hex string.
298
+ # value value to convert.
299
+ # desimals Amount of decimals.
300
+ # @return
301
+ #
302
+ @classmethod
303
+ def integerString(cls, value, desimals):
304
+ str_ = str(value)
305
+ if desimals == 0 or len(_GXCommon.zeroes) == len(str_):
306
+ return str_
307
+ return _GXCommon.zeroes[0: desimals - len(str_)] + str_
308
+
309
+ #
310
+ # Get array from DLMS data.
311
+ #
312
+ # buff
313
+ # Received DLMS data.
314
+ # info
315
+ # Data info.
316
+ # index
317
+ # starting index.
318
+ # Object array.
319
+ #
320
+ @classmethod
321
+ def getArray(cls, settings, buff, info, index):
322
+ value = None
323
+ if info.count == 0:
324
+ info.count = _GXCommon.getObjectCount(buff)
325
+ size = len(buff) - buff.position
326
+ if info.count != 0 and size < 1:
327
+ info.complete = False
328
+ return None
329
+ startIndex = index
330
+ if info.type_ == DataType.ARRAY:
331
+ value = GXArray()
332
+ else:
333
+ value = GXStructure()
334
+ # Position where last row was found. Cache uses this info.
335
+ pos = info.index
336
+ while pos != info.count:
337
+ info2 = _GXDataInfo()
338
+ tmp = cls.getData(settings, buff, info2)
339
+ if not info2.complete:
340
+ buff.position = startIndex
341
+ info.complete = False
342
+ break
343
+ if info2.count == info2.index:
344
+ startIndex = buff.position
345
+ value.append(tmp)
346
+ pos += 1
347
+ info.index = pos
348
+ return value
349
+
350
+ #
351
+ # Get time from DLMS data.
352
+ #
353
+ # buff
354
+ # Received DLMS data.
355
+ # info
356
+ # Data info.
357
+ # Parsed time.
358
+ #
359
+ @classmethod
360
+ def getTime(cls, buff, info):
361
+ # pylint: disable=broad-except
362
+ value = None
363
+ if len(buff) - buff.position < 4:
364
+ # If there is not enough data available.
365
+ info.complete = False
366
+ return None
367
+ str_ = None
368
+ value = GXTime(None)
369
+ # Get time.
370
+ hour = buff.getUInt8()
371
+ if hour == 0xFF:
372
+ hour = 0
373
+ value.skip |= DateTimeSkips.HOUR
374
+ minute = buff.getUInt8()
375
+ if minute == 0xFF:
376
+ minute = 0
377
+ value.skip |= DateTimeSkips.MINUTE
378
+ second = buff.getUInt8()
379
+ if second == 0xFF:
380
+ second = 0
381
+ value.skip |= DateTimeSkips.SECOND
382
+ ms = buff.getUInt8()
383
+ if ms != 0xFF:
384
+ ms *= 10
385
+ else:
386
+ ms = 0
387
+ value.skip |= DateTimeSkips.MILLISECOND
388
+ value.value = datetime(1900, 1, 1, hour, minute, second, ms)
389
+ return value
390
+
391
+ #
392
+ # Get date from DLMS data.
393
+ #
394
+ # buff
395
+ # Received DLMS data.
396
+ # info
397
+ # Data info.
398
+ # Parsed date.
399
+ #
400
+ @classmethod
401
+ def getDate(cls, buff, info):
402
+ # pylint: disable=broad-except
403
+ value = None
404
+ if len(buff) - buff.position < 5:
405
+ # If there is not enough data available.
406
+ info.complete = False
407
+ return None
408
+ str_ = None
409
+ dt = GXDate()
410
+ # Get year.
411
+ year = buff.getUInt16()
412
+ if year < 1900 or year == 0xFFFF:
413
+ dt.skip |= DateTimeSkips.YEAR
414
+ year = 2000
415
+ # Get month
416
+ month = buff.getUInt8()
417
+ if month == 0xFE:
418
+ dt.extra |= DateTimeExtraInfo.DST_BEGIN
419
+ month = 1
420
+ elif month == 0xFD:
421
+ dt.extra |= DateTimeExtraInfo.DST_END
422
+ month = 1
423
+ else:
424
+ if month < 1 or month > 12:
425
+ dt.skip |= DateTimeSkips.MONTH
426
+ month = 1
427
+ # Get day
428
+ day = buff.getUInt8()
429
+ if day == 0xFE:
430
+ dt.extra |= DateTimeExtraInfo.LAST_DAY
431
+ day = 1
432
+ elif day == 0xFD:
433
+ dt.extra |= DateTimeExtraInfo.LAST_DAY2
434
+ day = 1
435
+ else:
436
+ if day < 1 or day > 31:
437
+ dt.skip |= DateTimeSkips.DAY
438
+ day = 1
439
+ dt.value = datetime(year, month, day, 0, 0, 0, 0)
440
+ value = dt
441
+ # Skip week day
442
+ if buff.getUInt8() == 0xFF:
443
+ dt.skip |= DateTimeSkips.DAY_OF_WEEK
444
+ return value
445
+
446
+ #
447
+ # Get date and time from DLMS data.
448
+ #
449
+ # buff
450
+ # Received DLMS data.
451
+ # info
452
+ # Data info.
453
+ # Parsed date and time.
454
+ #
455
+ @classmethod
456
+ def getDateTime(cls, settings, buff, info):
457
+ # pylint: disable=too-many-locals, broad-except
458
+ value = None
459
+ skip = DateTimeSkips.NONE
460
+ extra = DateTimeExtraInfo.NONE
461
+ # If there is not enough data available.
462
+ if len(buff) - buff.position < 12:
463
+ info.complete = False
464
+ return None
465
+ str_ = None
466
+ dt = GXDateTime()
467
+ # Get year.
468
+ year = buff.getUInt16()
469
+ # Get month
470
+ month = buff.getUInt8()
471
+ # Get day
472
+ day = buff.getUInt8()
473
+ # Skip week day
474
+ dayOfWeek = buff.getUInt8()
475
+ if dayOfWeek == 0xFF:
476
+ skip |= DateTimeSkips.DAY_OF_WEEK
477
+ else:
478
+ dt.dayOfWeek = dayOfWeek
479
+ # Get time.
480
+ hour = buff.getUInt8()
481
+ minute = buff.getUInt8()
482
+ second = buff.getUInt8()
483
+ ms = buff.getUInt8() & 0xFF
484
+ if ms != 0xFF:
485
+ ms *= 10
486
+ else:
487
+ ms = -1
488
+ deviation = buff.getInt16()
489
+ if deviation == -32768:
490
+ deviation = 0x8000
491
+ skip |= DateTimeSkips.DEVITATION
492
+ status = buff.getUInt8()
493
+ dt.status = status
494
+ if year < 1900 or year == 0xFFFF:
495
+ skip |= DateTimeSkips.YEAR
496
+ year = 2000
497
+ if month == 0xFE:
498
+ extra |= DateTimeExtraInfo.DST_BEGIN
499
+ month = 1
500
+ elif month == 0xFD:
501
+ extra |= DateTimeExtraInfo.DST_END
502
+ month = 1
503
+ else:
504
+ if month < 1 or month > 12:
505
+ skip |= DateTimeSkips.MONTH
506
+ month = 1
507
+
508
+ if day == 0xFE:
509
+ extra |= DateTimeExtraInfo.LAST_DAY
510
+ day = 1
511
+ elif day == 0xFD:
512
+ extra |= DateTimeExtraInfo.LAST_DAY2
513
+ day = 1
514
+ else:
515
+ if day == -1 or day == 0 or day > 31:
516
+ skip |= DateTimeSkips.DAY
517
+ day = 1
518
+
519
+ if hour < 0 or hour > 24:
520
+ skip |= DateTimeSkips.HOUR
521
+ hour = 0
522
+ if minute < 0 or minute > 60:
523
+ skip |= DateTimeSkips.MINUTE
524
+ minute = 0
525
+ if second < 0 or second > 60:
526
+ skip |= DateTimeSkips.SECOND
527
+ second = 0
528
+ # If ms is Zero it's skipped.
529
+ if ms < 0 or ms > 100:
530
+ skip |= DateTimeSkips.MILLISECOND
531
+ ms = 0
532
+ tz = None
533
+ if deviation != 0x8000:
534
+ if settings.useUtc2NormalTime:
535
+ tz = deviation
536
+ else:
537
+ tz = -deviation
538
+ if tz is None:
539
+ dt.value = datetime(year, month, day, hour, minute, second, ms)
540
+ else:
541
+ dt.value = datetime(year, month, day, hour, minute, second, ms, tzinfo=GXTimeZone(tz))
542
+ dt.skip = skip
543
+ value = dt
544
+ return value
545
+
546
+ #
547
+ # Get double value from DLMS data.
548
+ #
549
+ # buff
550
+ # Received DLMS data.
551
+ # info
552
+ # Data info.
553
+ # Parsed double value.
554
+ #
555
+ @classmethod
556
+ def getDouble(cls, settings, buff, info):
557
+ value = None
558
+ # If there is not enough data available.
559
+ if len(buff) - buff.position < 8:
560
+ info.complete = False
561
+ return None
562
+ value = buff.getDouble()
563
+ return GXFloat64(value)
564
+
565
+ #
566
+ # Get float value from DLMS data.
567
+ #
568
+ # buff
569
+ # Received DLMS data.
570
+ # info
571
+ # Data info.
572
+ # Parsed float value.
573
+ #
574
+ @classmethod
575
+ def getFloat(cls, settings, buff, info):
576
+ value = None
577
+ # If there is not enough data available.
578
+ if len(buff) - buff.position < 4:
579
+ info.complete = False
580
+ return None
581
+ value = buff.getFloat()
582
+ return GXFloat32(value)
583
+
584
+ #
585
+ # Get enumeration value from DLMS data.
586
+ #
587
+ # buff
588
+ # Received DLMS data.
589
+ # info
590
+ # Data info.
591
+ # parsed enumeration value.
592
+ #
593
+ @classmethod
594
+ def getEnum(cls, buff, info):
595
+ value = None
596
+ # If there is not enough data available.
597
+ if len(buff) - buff.position < 1:
598
+ info.complete = False
599
+ return None
600
+ value = buff.getUInt8()
601
+ return GXEnum(value)
602
+
603
+ #
604
+ # Get UInt64 value from DLMS data.
605
+ #
606
+ # buff
607
+ # Received DLMS data.
608
+ # info
609
+ # Data info.
610
+ # parsed UInt64 value.
611
+ #
612
+ @classmethod
613
+ def getUInt64(cls, buff, info):
614
+ value = None
615
+ # If there is not enough data available.
616
+ if len(buff) - buff.position < 8:
617
+ info.complete = False
618
+ return None
619
+ value = buff.getUInt64()
620
+ return GXUInt64(value)
621
+
622
+ #
623
+ # Get Int64 value from DLMS data.
624
+ #
625
+ # buff
626
+ # Received DLMS data.
627
+ # info
628
+ # Data info.
629
+ # parsed Int64 value.
630
+ #
631
+ @classmethod
632
+ def getInt64(cls, buff, info):
633
+ value = None
634
+ # If there is not enough data available.
635
+ if len(buff) - buff.position < 8:
636
+ info.complete = False
637
+ return None
638
+ value = buff.getInt64()
639
+ return value
640
+
641
+ #
642
+ # Get UInt16 value from DLMS data.
643
+ #
644
+ # buff
645
+ # Received DLMS data.
646
+ # info
647
+ # Data info.
648
+ # parsed UInt16 value.
649
+ #
650
+ @classmethod
651
+ def getUInt16(cls, buff, info):
652
+ value = None
653
+ # If there is not enough data available.
654
+ if len(buff) - buff.position < 2:
655
+ info.complete = False
656
+ return None
657
+ value = buff.getUInt16()
658
+ return GXUInt16(value)
659
+
660
+ #pylint: disable=too-many-arguments
661
+ @classmethod
662
+ def getCompactArrayItem(cls, settings, buff, dt, list_, len_):
663
+ if isinstance(dt, list):
664
+ tmp2 = list()
665
+ for it in dt:
666
+ if isinstance(it, DataType):
667
+ cls.getCompactArrayItem(settings, buff, it, tmp2, 1)
668
+ else:
669
+ cls.getCompactArrayItem(settings, buff, it, tmp2, 1)
670
+ list_.append(tmp2)
671
+ return
672
+
673
+ tmp = _GXDataInfo()
674
+ tmp.type_ = dt
675
+ start = buff.position
676
+ if dt == DataType.STRING:
677
+ while buff.position - start < len_:
678
+ tmp.clear()
679
+ tmp.type = dt
680
+ list_.append(cls.getString(buff, tmp, False))
681
+ if not tmp.complete:
682
+ break
683
+ elif dt == DataType.OCTET_STRING:
684
+ while buff.position - start < len_:
685
+ tmp.clear()
686
+ tmp.type = dt
687
+ list_.append(cls.getOctetString(settings, buff, tmp, False))
688
+ if not tmp.complete:
689
+ break
690
+ else:
691
+ while buff.position - start < len_:
692
+ tmp.clear()
693
+ tmp.type_ = dt
694
+ list_.append(cls.getData(settings, buff, tmp))
695
+ if not tmp.complete:
696
+ break
697
+
698
+ @classmethod
699
+ def getDataTypes(cls, buff, cols, len_):
700
+ dt = None
701
+ pos = 0
702
+ while pos != len_:
703
+ dt = buff.getUInt8()
704
+ if dt == DataType.ARRAY:
705
+ cnt = buff.getUInt16()
706
+ tmp = list()
707
+ tmp2 = list()
708
+ cls.getDataTypes(buff, tmp, 1)
709
+ i = 0
710
+ while i != cnt:
711
+ tmp2.append(tmp)
712
+ i += 1
713
+ cols.append(tmp2)
714
+ elif dt == DataType.STRUCTURE:
715
+ tmp = list()
716
+ cls.getDataTypes(buff, tmp, buff.getUInt8())
717
+ cols.append(tmp)
718
+ else:
719
+ cols.append(dt)
720
+ pos += 1
721
+
722
+ #
723
+ # Get compact array value from DLMS data.
724
+ #
725
+ # buff
726
+ # Received DLMS data.
727
+ # info
728
+ # Data info.
729
+ # parsed UInt16 value.
730
+ #
731
+ @classmethod
732
+ def getCompactArray(cls, settings, buff, info):
733
+ # pylint: disable=too-many-nested-blocks
734
+
735
+ # If there is not enough data available.
736
+ if len(buff) - buff.position < 2:
737
+ info.complete = False
738
+ return None
739
+ dt = buff.getUInt8()
740
+ if dt == DataType.ARRAY:
741
+ raise ValueError("Invalid compact array data.")
742
+ len_ = _GXCommon.getObjectCount(buff)
743
+ list_ = list()
744
+ if dt == DataType.STRUCTURE:
745
+ # Get data types.
746
+ cols = list()
747
+ cls.getDataTypes(buff, cols, len_)
748
+ len_ = _GXCommon.getObjectCount(buff)
749
+ start = buff.position
750
+ while buff.position - start < len_:
751
+ row = list()
752
+ pos = 0
753
+ while pos != len(cols):
754
+ if isinstance(cols[pos], GXArray):
755
+ cls.getCompactArrayItem(settings, buff, cols[pos], row, 1)
756
+ elif isinstance(cols[pos], GXStructure):
757
+ tmp2 = list()
758
+ cls.getCompactArrayItem(settings, buff, cols[pos], tmp2, 1)
759
+ row.append(tmp2[0])
760
+ else:
761
+ cls.getCompactArrayItem(settings, buff, cols[pos], row, 1)
762
+ if buff.position == len(buff):
763
+ break
764
+ pos += 1
765
+ # If all columns are read.
766
+ if len(row) >= len(cols):
767
+ list_.append(row)
768
+ else:
769
+ break
770
+ else:
771
+ cls.getCompactArrayItem(settings, buff, dt, list_, len_)
772
+ return list_
773
+
774
+ #
775
+ # Get UInt8 value from DLMS data.
776
+ #
777
+ # buff
778
+ # Received DLMS data.
779
+ # info
780
+ # Data info.
781
+ # parsed UInt8 value.
782
+ #
783
+ @classmethod
784
+ def getUInt8(cls, buff, info):
785
+ value = None
786
+ # If there is not enough data available.
787
+ if len(buff) - buff.position < 1:
788
+ info.complete = False
789
+ return None
790
+ value = buff.getUInt8() & 0xFF
791
+ return GXUInt8(value)
792
+
793
+ #
794
+ # Get Int16 value from DLMS data.
795
+ #
796
+ # buff
797
+ # Received DLMS data.
798
+ # info
799
+ # Data info.
800
+ # parsed Int16 value.
801
+ #
802
+ @classmethod
803
+ def getInt16(cls, buff, info):
804
+ value = None
805
+ # If there is not enough data available.
806
+ if len(buff) - buff.position < 2:
807
+ info.complete = False
808
+ return None
809
+ value = int(buff.getInt16())
810
+ return value
811
+
812
+ #
813
+ # Get Int8 value from DLMS data.
814
+ #
815
+ # buff
816
+ # Received DLMS data.
817
+ # info
818
+ # Data info.
819
+ # parsed Int8 value.
820
+ #
821
+ @classmethod
822
+ def getInt8(cls, buff, info):
823
+ value = None
824
+ # If there is not enough data available.
825
+ if len(buff) - buff.position < 1:
826
+ info.complete = False
827
+ return None
828
+ value = int(buff.getInt8())
829
+ return GXInt8(value)
830
+
831
+ #
832
+ # Get BCD value from DLMS data.
833
+ #
834
+ # buff: Received DLMS data.
835
+ # info: Data info.
836
+ # Returns parsed BCD value.
837
+ #
838
+ @classmethod
839
+ def getBcd(cls, buff, info):
840
+ # If there is not enough data available.
841
+ if len(buff) - buff.position < 1:
842
+ info.complete = False
843
+ return None
844
+ value = buff.getUInt8()
845
+ return value
846
+
847
+ #
848
+ # Get UTF string value from DLMS data.
849
+ #
850
+ # buff
851
+ # Received DLMS data.
852
+ # info
853
+ # Data info.
854
+ # parsed UTF string value.
855
+ #
856
+ @classmethod
857
+ def getUtfString(cls, buff, info, knownType):
858
+ if knownType:
859
+ len_ = len(buff)
860
+ else:
861
+ len_ = _GXCommon.getObjectCount(buff)
862
+ # If there is not enough data available.
863
+ if len(buff) - buff.position < len_:
864
+ info.complete = False
865
+ return None
866
+ if len_ > 0:
867
+ value = buff.getString(buff.position, len_)
868
+ buff.position += len_
869
+ else:
870
+ value = ""
871
+ return value
872
+
873
+ #
874
+ # Get octet string value from DLMS data.
875
+ #
876
+ # buff
877
+ # Received DLMS data.
878
+ # info
879
+ # Data info.
880
+ # parsed octet string value.
881
+ #
882
+ @classmethod
883
+ def getOctetString(cls, settings, buff, info, knownType):
884
+ # pylint: disable=too-many-nested-blocks,broad-except
885
+ value = None
886
+ if knownType:
887
+ len_ = len(buff)
888
+ else:
889
+ len_ = _GXCommon.getObjectCount(buff)
890
+ # If there is not enough data available.
891
+ if len(buff) - buff.position < len_:
892
+ info.complete = False
893
+ return None
894
+ tmp = bytearray(len_)
895
+ buff.get(tmp)
896
+ value = tmp
897
+ return value
898
+
899
+ #
900
+ # Get string value from DLMS data.
901
+ #
902
+ # buff
903
+ # Received DLMS data.
904
+ # info
905
+ # Data info.
906
+ # parsed string value.
907
+ #
908
+ @classmethod
909
+ def getString(cls, buff, info, knownType):
910
+ value = None
911
+ if knownType:
912
+ len_ = len(buff)
913
+ else:
914
+ len_ = _GXCommon.getObjectCount(buff)
915
+ # If there is not enough data available.
916
+ if len(buff) - buff.position < len_:
917
+ info.complete = False
918
+ return None
919
+ if len_ > 0:
920
+ value = buff.getString(buff.position, len_)
921
+ buff.position += len_
922
+ else:
923
+ value = ""
924
+ return value
925
+
926
+ #
927
+ # Get UInt32 value from DLMS data.
928
+ #
929
+ # buff
930
+ # Received DLMS data.
931
+ # info
932
+ # Data info.
933
+ # parsed UInt32 value.
934
+ #
935
+ @classmethod
936
+ def getUInt32(cls, buff, info):
937
+ # If there is not enough data available.
938
+ if len(buff) - buff.position < 4:
939
+ info.complete = False
940
+ return None
941
+ value = buff.getUInt32()
942
+ return GXUInt32(value)
943
+
944
+ #
945
+ # Get Int32 value from DLMS data.
946
+ #
947
+ # buff
948
+ # Received DLMS data.
949
+ # info
950
+ # Data info.
951
+ # parsed Int32 value.
952
+ #
953
+ @classmethod
954
+ def getInt32(cls, buff, info):
955
+ # If there is not enough data available.
956
+ if len(buff) - buff.position < 4:
957
+ info.complete = False
958
+ return None
959
+ value = int(buff.getInt32())
960
+ return value
961
+
962
+ #
963
+ # Get bit string value from DLMS data.
964
+ #
965
+ # buff
966
+ # Received DLMS data.
967
+ # info
968
+ # Data info.
969
+ # parsed bit string value.
970
+ #
971
+ @classmethod
972
+ def getBitString(cls, buff, info):
973
+ cnt = cls.getObjectCount(buff)
974
+ t = cnt
975
+ t /= 8
976
+ if cnt % 8 != 0:
977
+ t += 1
978
+ byteCnt = int(t)
979
+ # If there is not enough data available.
980
+ if len(buff) - buff.position < byteCnt:
981
+ info.complete = False
982
+ return None
983
+ sb = ""
984
+ while cnt > 0:
985
+ sb += cls.toBitString(buff.getInt8(), cnt)
986
+ cnt -= 8
987
+ return GXBitString(sb)
988
+
989
+ #
990
+ # Get boolean value from DLMS data.
991
+ #
992
+ # buff
993
+ # Received DLMS data.
994
+ # info
995
+ # Data info.
996
+ # parsed boolean value.
997
+ #
998
+ @classmethod
999
+ def getBoolean(cls, buff, info):
1000
+ # If there is not enough data available.
1001
+ if len(buff) - buff.position < 1:
1002
+ info.complete = False
1003
+ return None
1004
+ value = bool(buff.getUInt8() != 0)
1005
+ return value
1006
+
1007
+ #
1008
+ # Get HDLC address from byte array.
1009
+ #
1010
+ # buff
1011
+ # byte array.
1012
+ # HDLC address.
1013
+ #
1014
+ @classmethod
1015
+ def getHDLCAddress(cls, buff):
1016
+ size = 0
1017
+ pos = buff.position
1018
+ while pos != len(buff):
1019
+ size += 1
1020
+ if buff.getUInt8(pos) & 0x1 == 1:
1021
+ break
1022
+ pos += 1
1023
+ if size == 1:
1024
+ ret = (buff.getUInt8() & 0xFE) >> 1
1025
+ elif size == 2:
1026
+ ret = buff.getUInt16()
1027
+ ret = ((ret & 0xFE) >> 1) | ((ret & 0xFE00) >> 2)
1028
+ elif size == 4:
1029
+ ret = buff.getUInt32()
1030
+ ret = ((ret & 0xFE) >> 1) | ((ret & 0xFE00) >> 2) | ((ret & 0xFE0000) >> 3) | ((ret & 0xFE000000) >> 4)
1031
+ else:
1032
+ raise ValueError("Wrong size.")
1033
+ return ret
1034
+
1035
+ #
1036
+ # Convert object to DLMS bytes.
1037
+ #
1038
+ # settings: DLMS settings.
1039
+ # buff: Byte buffer where data is write.
1040
+ # dataType: Data type.
1041
+ # value: Added Value.
1042
+ #
1043
+ @classmethod
1044
+ def setData(cls, settings, buff, dataType, value):
1045
+ if dataType in (DataType.ARRAY, DataType.STRUCTURE) and isinstance(value, (GXByteBuffer, bytearray, bytes)):
1046
+ # If byte array is added do not add type.
1047
+ buff.set(value)
1048
+ return
1049
+
1050
+ if isinstance(value, cdt.CommonDataType):
1051
+ buff.set(value.encoding)
1052
+ else:
1053
+ buff.setUInt8(dataType)
1054
+ if dataType == DataType.NONE:
1055
+ pass
1056
+ elif dataType == DataType.BOOLEAN:
1057
+ if value:
1058
+ buff.setUInt8(1)
1059
+ else:
1060
+ buff.setUInt8(0)
1061
+ elif dataType == DataType.UINT8:
1062
+ buff.setUInt8(value)
1063
+ elif dataType in (DataType.INT8, DataType.ENUM):
1064
+ buff.setInt8(value)
1065
+ elif dataType in (DataType.UINT16, DataType.INT16):
1066
+ buff.setUInt16(value)
1067
+ elif dataType in (DataType.UINT32, DataType.INT32):
1068
+ buff.setUInt32(value)
1069
+ elif dataType in (DataType.UINT64, DataType.INT64):
1070
+ buff.setUInt64(value)
1071
+ elif dataType == DataType.FLOAT32:
1072
+ buff.setFloat(value)
1073
+ elif dataType == DataType.FLOAT64:
1074
+ buff.setDouble(value)
1075
+ elif dataType == DataType.BITSTRING:
1076
+ cls.setBitString(buff, value, True)
1077
+ elif dataType == DataType.STRING:
1078
+ cls.setString(buff, value)
1079
+ elif dataType == DataType.STRING_UTF8:
1080
+ cls.setUtfString(buff, value)
1081
+ elif dataType == DataType.OCTET_STRING:
1082
+ if isinstance(value, GXDate):
1083
+ # Add size
1084
+ buff.setUInt8(5)
1085
+ cls.setDate(buff, value)
1086
+ elif isinstance(value, GXTime):
1087
+ # Add size
1088
+ buff.setUInt8(4)
1089
+ cls.setTime(buff, value)
1090
+ elif isinstance(value, (GXDateTime, datetime)):
1091
+ buff.setUInt8(12)
1092
+ cls.setDateTime(settings, buff, value)
1093
+ else:
1094
+ cls.setOctetString(buff, value)
1095
+ elif dataType in (DataType.ARRAY, DataType.STRUCTURE):
1096
+ cls.setArray(settings, buff, value)
1097
+ elif dataType == DataType.BCD:
1098
+ cls.setBcd(buff, value)
1099
+ elif dataType == DataType.COMPACT_ARRAY:
1100
+ # Compact array is not work with python because we don't know data
1101
+ # types of each element.
1102
+ raise ValueError("Invalid data type.")
1103
+ elif dataType == DataType.DATETIME:
1104
+ cls.setDateTime(settings, buff, value)
1105
+ elif dataType == DataType.DATE:
1106
+ cls.setDate(buff, value)
1107
+ elif dataType == DataType.TIME:
1108
+ cls.setTime(buff, value)
1109
+ else:
1110
+ raise ValueError("Invalid data type.")
1111
+
1112
+ #
1113
+ # Convert time to DLMS bytes.
1114
+ #
1115
+ # buff
1116
+ # Byte buffer where data is write.
1117
+ # value
1118
+ # Added value.
1119
+ #
1120
+ @classmethod
1121
+ def setTime(cls, buff, value):
1122
+ dt = cls.__getDateTime(value)
1123
+ # Add time.
1124
+ if dt.skip & DateTimeSkips.HOUR != DateTimeSkips.NONE:
1125
+ buff.setUInt8(0xFF)
1126
+ else:
1127
+ buff.setUInt8(dt.value.hour)
1128
+ if dt.skip & DateTimeSkips.MINUTE != DateTimeSkips.NONE:
1129
+ buff.setUInt8(0xFF)
1130
+ else:
1131
+ buff.setUInt8(dt.value.minute)
1132
+ if dt.skip & DateTimeSkips.SECOND != DateTimeSkips.NONE:
1133
+ buff.setUInt8(0xFF)
1134
+ else:
1135
+ buff.setUInt8(dt.value.second)
1136
+ if dt.skip & DateTimeSkips.MILLISECOND != DateTimeSkips.NONE:
1137
+ # Hundredth of seconds is not used.
1138
+ buff.setUInt8(0xFF)
1139
+ else:
1140
+ ms = dt.value.microsecond
1141
+ if ms != 0:
1142
+ ms /= 10000
1143
+ buff.setUInt8(int(ms))
1144
+
1145
+ #
1146
+ # Convert date to DLMS bytes.
1147
+ #
1148
+ # buff
1149
+ # Byte buffer where data is write.
1150
+ # value
1151
+ # Added value.
1152
+ #
1153
+ @classmethod
1154
+ def setDate(cls, buff, value):
1155
+ dt = cls.__getDateTime(value)
1156
+ # Add year.
1157
+ if dt.skip & DateTimeSkips.YEAR != DateTimeSkips.NONE:
1158
+ buff.setUInt16(0xFFFF)
1159
+ else:
1160
+ buff.setUInt16(dt.value.year)
1161
+ # Add month
1162
+ if dt.extra & DateTimeExtraInfo.DST_BEGIN != 0:
1163
+ buff.setUInt8(0xFE)
1164
+ elif dt.extra & DateTimeExtraInfo.DST_END != 0:
1165
+ buff.setUInt8(0xFD)
1166
+ elif dt.skip & DateTimeSkips.MONTH != 0:
1167
+ buff.setUInt8(0xFF)
1168
+ else:
1169
+ buff.setUInt8(dt.value.month)
1170
+ # Add day
1171
+ if dt.extra & DateTimeExtraInfo.LAST_DAY2 != DateTimeSkips.NONE:
1172
+ buff.setUInt8(0xFD)
1173
+ elif dt.extra & DateTimeExtraInfo.LAST_DAY != DateTimeSkips.NONE:
1174
+ buff.setUInt8(0xFE)
1175
+ elif dt.skip & DateTimeSkips.DAY != DateTimeSkips.NONE:
1176
+ buff.setUInt8(0xFF)
1177
+ else:
1178
+ buff.setUInt8(dt.value.day)
1179
+
1180
+ # Day of week.
1181
+ if dt.skip & DateTimeSkips.DAY_OF_WEEK != DateTimeSkips.NONE:
1182
+ buff.setUInt8(0xFF)
1183
+ else:
1184
+ if dt.dayOfWeek == 0:
1185
+ buff.setUInt8(dt.value.weekday() + 1)
1186
+ else:
1187
+ buff.setUInt8(dt.dayOfWeek)
1188
+
1189
+ @classmethod
1190
+ def __getDateTime(cls, value):
1191
+ dt = None
1192
+ if isinstance(value, (GXDateTime)):
1193
+ dt = value
1194
+ elif isinstance(value, (datetime, str)):
1195
+ dt = GXDateTime(value)
1196
+ dt.skip |= DateTimeSkips.MILLISECOND
1197
+ else:
1198
+ raise ValueError("Invalid date format.")
1199
+ return dt
1200
+
1201
+ #
1202
+ # Convert date time to DLMS bytes.
1203
+ #
1204
+ # buff
1205
+ # Byte buffer where data is write.
1206
+ # value
1207
+ # Added value.
1208
+ #
1209
+ @classmethod
1210
+ def setDateTime(cls, settings, buff, value):
1211
+ dt = cls.__getDateTime(value)
1212
+ # Add year.
1213
+ if dt.skip & DateTimeSkips.YEAR != DateTimeSkips.NONE:
1214
+ buff.setUInt16(0xFFFF)
1215
+ else:
1216
+ buff.setUInt16(dt.value.year)
1217
+ # Add month
1218
+ if dt.extra & DateTimeExtraInfo.DST_BEGIN != 0:
1219
+ buff.setUInt8(0xFD)
1220
+ elif dt.extra & DateTimeExtraInfo.DST_END != 0:
1221
+ buff.setUInt8(0xFE)
1222
+ elif dt.skip & DateTimeSkips.MONTH != DateTimeSkips.NONE:
1223
+ buff.setUInt8(0xFF)
1224
+ else:
1225
+ buff.setUInt8(dt.value.month)
1226
+
1227
+ # Add day
1228
+ if dt.extra & DateTimeExtraInfo.LAST_DAY2 != DateTimeSkips.NONE:
1229
+ buff.setUInt8(0xFD)
1230
+ elif dt.extra & DateTimeExtraInfo.LAST_DAY != DateTimeSkips.NONE:
1231
+ buff.setUInt8(0xFE)
1232
+ elif dt.skip & DateTimeSkips.DAY != DateTimeSkips.NONE:
1233
+ buff.setUInt8(0xFF)
1234
+ else:
1235
+ buff.setUInt8(dt.value.day)
1236
+ # Day of week.
1237
+ if dt.skip & DateTimeSkips.DAY_OF_WEEK != DateTimeSkips.NONE:
1238
+ buff.setUInt8(0xFF)
1239
+ else:
1240
+ if dt.dayOfWeek == 0:
1241
+ buff.setUInt8(dt.value.weekday() + 1)
1242
+ else:
1243
+ buff.setUInt8(dt.dayOfWeek)
1244
+ # Add time.
1245
+ if dt.skip & DateTimeSkips.HOUR != DateTimeSkips.NONE:
1246
+ buff.setUInt8(0xFF)
1247
+ else:
1248
+ buff.setUInt8(dt.value.hour)
1249
+ if dt.skip & DateTimeSkips.MINUTE != DateTimeSkips.NONE:
1250
+ buff.setUInt8(0xFF)
1251
+ else:
1252
+ buff.setUInt8(dt.value.minute)
1253
+ if dt.skip & DateTimeSkips.SECOND != DateTimeSkips.NONE:
1254
+ buff.setUInt8(0xFF)
1255
+ else:
1256
+ buff.setUInt8(dt.value.second)
1257
+ if dt.skip & DateTimeSkips.MILLISECOND != DateTimeSkips.NONE:
1258
+ # Hundredth of seconds is not used.
1259
+ buff.setUInt8(0xFF)
1260
+ else:
1261
+ ms = dt.value.microsecond
1262
+ if ms != 0:
1263
+ ms /= 10000
1264
+ buff.setUInt8(int(ms))
1265
+ # devitation not used.
1266
+ if dt.skip & DateTimeSkips.DEVITATION != DateTimeSkips.NONE:
1267
+ buff.setUInt16(0x8000)
1268
+ else:
1269
+ # Add devitation.
1270
+ try:
1271
+ d = int(dt.value.utcoffset().total_seconds() / 60)
1272
+ except AttributeError:
1273
+ d = 0
1274
+ if not (settings and settings.useUtc2NormalTime):
1275
+ d = -d
1276
+ buff.setUInt16(d)
1277
+ # Add clock_status
1278
+ if dt.skip & DateTimeSkips.STATUS == DateTimeSkips.NONE:
1279
+ if dt.value.dst() or dt.status & ClockStatus.DAYLIGHT_SAVE_ACTIVE != ClockStatus.OK:
1280
+ buff.setUInt8(dt.status | ClockStatus.DAYLIGHT_SAVE_ACTIVE)
1281
+ else:
1282
+ buff.setUInt8(dt.status)
1283
+ else:
1284
+ buff.setUInt8(0xFF)
1285
+
1286
+ @classmethod
1287
+ def setBcd(cls, buff, value):
1288
+ buff.setUInt8(value)
1289
+
1290
+ @classmethod
1291
+ def setArray(cls, settings, buff, value):
1292
+ if value:
1293
+ _GXCommon.setObjectCount(len(value), buff)
1294
+ for it in value:
1295
+ cls.setData(settings, buff, cls.getDLMSDataType(it), it)
1296
+ else:
1297
+ _GXCommon.setObjectCount(0, buff)
1298
+
1299
+ @classmethod
1300
+ def setOctetString(cls, buff, value):
1301
+ if isinstance(value, str):
1302
+ tmp = GXByteBuffer.hexToBytes(value)
1303
+ _GXCommon.setObjectCount(len(tmp), buff)
1304
+ buff.set(tmp)
1305
+ elif isinstance(value, GXByteBuffer):
1306
+ cls.setObjectCount(len(value), buff)
1307
+ buff.set(value)
1308
+ elif isinstance(value, (bytearray, bytes)):
1309
+ cls.setObjectCount(len(value), buff)
1310
+ buff.set(value)
1311
+ elif value is None:
1312
+ cls.setObjectCount(0, buff)
1313
+ else:
1314
+ raise ValueError("Invalid data type.")
1315
+
1316
+ @classmethod
1317
+ def setUtfString(cls, buff, value):
1318
+ if value:
1319
+ tmp = value.encode()
1320
+ _GXCommon.setObjectCount(len(tmp), buff)
1321
+ buff.set(tmp)
1322
+ else:
1323
+ buff.setUInt8(0)
1324
+
1325
+ @classmethod
1326
+ def setString(cls, buff, value):
1327
+ if value:
1328
+ _GXCommon.setObjectCount(len(value), buff)
1329
+ buff.set(_GXCommon.getBytes(value))
1330
+ else:
1331
+ buff.setUInt8(0)
1332
+
1333
+ @classmethod
1334
+ def setBitString(cls, buff, val1, addCount):
1335
+ value = val1
1336
+ if isinstance(value, GXBitString):
1337
+ value = value.value
1338
+ if isinstance(value, str):
1339
+ val = 0
1340
+ str_ = str(value)
1341
+ if addCount:
1342
+ _GXCommon.setObjectCount(len(str_), buff)
1343
+ index = 7
1344
+ pos = 0
1345
+ while pos != len(str_):
1346
+ it = str_[pos]
1347
+ if it == '1':
1348
+ val |= (1 << index)
1349
+ elif it != '0':
1350
+ raise ValueError("Not a bit string.")
1351
+ index -= 1
1352
+ if index == -1:
1353
+ index = 7
1354
+ buff.setUInt8(val)
1355
+ val = 0
1356
+ pos += 1
1357
+ if index != 7:
1358
+ buff.setUInt8(val)
1359
+ elif isinstance(value, (bytearray, bytes)):
1360
+ _GXCommon.setObjectCount(8 * len(value), buff)
1361
+ buff.set(value)
1362
+ elif isinstance(value, int):
1363
+ _GXCommon.setObjectCount(8, buff)
1364
+ buff.setUInt8(value)
1365
+ elif value is None:
1366
+ buff.setUInt8(0)
1367
+ else:
1368
+ raise ValueError("BitString must give as string.")
1369
+
1370
+ @classmethod
1371
+ def getDataType(cls, value):
1372
+ if value == DataType.NONE:
1373
+ ret = None
1374
+ elif value == DataType.OCTET_STRING:
1375
+ ret = bytes.__class__
1376
+ elif value == DataType.ENUM:
1377
+ ret = GXEnum.__class__
1378
+ elif value == DataType.INT8:
1379
+ ret = int.__class__
1380
+ elif value == DataType.INT16:
1381
+ ret = int.__class__
1382
+ elif value == DataType.INT32:
1383
+ ret = int.__class__
1384
+ elif value == DataType.INT64:
1385
+ ret = int.__class__
1386
+ elif value == DataType.UINT8:
1387
+ ret = GXUInt8.__class__
1388
+ elif value == DataType.UINT16:
1389
+ ret = GXUInt16.__class__
1390
+ elif value == DataType.UINT32:
1391
+ ret = GXUInt32.__class__
1392
+ elif value == DataType.UINT64:
1393
+ ret = GXUInt64.__class__
1394
+ elif value == DataType.TIME:
1395
+ ret = GXTime.__class__
1396
+ elif value == DataType.DATE:
1397
+ ret = GXDate.__class__
1398
+ elif value == DataType.DATETIME:
1399
+ ret = GXDateTime.__class__
1400
+ elif value == DataType.ARRAY:
1401
+ ret = list.__class__
1402
+ elif value == DataType.STRING:
1403
+ ret = str.__class__
1404
+ elif value == DataType.BOOLEAN:
1405
+ ret = bool.__class__
1406
+ elif value == DataType.FLOAT32:
1407
+ ret = GXFloat32.__class__
1408
+ elif value == DataType.FLOAT64:
1409
+ ret = GXFloat64.__class__
1410
+ elif value == DataType.BITSTRING:
1411
+ ret = GXBitString.__class__
1412
+ else:
1413
+ raise ValueError("Invalid value.")
1414
+ return ret
1415
+
1416
+ @classmethod
1417
+ def getDLMSDataType(cls, value):
1418
+ # pylint: disable=undefined-variable
1419
+ if value is None:
1420
+ ret = DataType.NONE
1421
+ elif isinstance(value, (bytes, bytearray, GXByteBuffer)):
1422
+ ret = DataType.OCTET_STRING
1423
+ elif isinstance(value, (GXEnum)):
1424
+ ret = DataType.ENUM
1425
+ elif isinstance(value, (GXInt8)):
1426
+ ret = DataType.INT8
1427
+ elif isinstance(value, (GXInt16)):
1428
+ ret = DataType.INT16
1429
+ elif isinstance(value, (GXInt64)):
1430
+ ret = DataType.INT64
1431
+ elif isinstance(value, (GXUInt8)):
1432
+ ret = DataType.UINT8
1433
+ elif isinstance(value, (GXUInt16)):
1434
+ ret = DataType.UINT16
1435
+ elif isinstance(value, (GXUInt32)):
1436
+ ret = DataType.UINT32
1437
+ elif isinstance(value, (GXUInt64)):
1438
+ ret = DataType.UINT64
1439
+ elif isinstance(value, (bool)):
1440
+ ret = DataType.BOOLEAN
1441
+ elif isinstance(value, (GXInt32, int)):
1442
+ ret = DataType.INT32
1443
+ elif isinstance(value, (GXTime)):
1444
+ ret = DataType.TIME
1445
+ elif isinstance(value, (GXDate)):
1446
+ ret = DataType.DATE
1447
+ elif isinstance(value, (datetime, GXDateTime)):
1448
+ ret = DataType.DATETIME
1449
+ elif isinstance(value, (GXStructure)):
1450
+ ret = DataType.STRUCTURE
1451
+ elif isinstance(value, (GXArray, list)):
1452
+ ret = DataType.ARRAY
1453
+ elif isinstance(value, (str)):
1454
+ ret = DataType.STRING
1455
+ elif isinstance(value, (GXFloat64)):
1456
+ ret = DataType.FLOAT64
1457
+ elif isinstance(value, (GXFloat32, complex, float)):
1458
+ ret = DataType.FLOAT32
1459
+ elif isinstance(value, (GXBitString)):
1460
+ ret = DataType.BITSTRING
1461
+ else:
1462
+ ret = None
1463
+ if ret is None:
1464
+ #Python 2.7 uses unicode.
1465
+ try:
1466
+ if isinstance(value, (unicode)):
1467
+ ret = DataType.STRING
1468
+ except Exception:
1469
+ ret = None
1470
+ if ret is None:
1471
+ raise ValueError("Invalid datatype " + type(value) + ".")
1472
+ return ret
1473
+
1474
+ @classmethod
1475
+ def getDataTypeSize(cls, type_):
1476
+ size = -1
1477
+ if type_ == DataType.BCD:
1478
+ size = 1
1479
+ elif type_ == DataType.BOOLEAN:
1480
+ size = 1
1481
+ elif type_ == DataType.DATE:
1482
+ size = 5
1483
+ elif type_ == DataType.DATETIME:
1484
+ size = 12
1485
+ elif type_ == DataType.ENUM:
1486
+ size = 1
1487
+ elif type_ == DataType.FLOAT32:
1488
+ size = 4
1489
+ elif type_ == DataType.FLOAT64:
1490
+ size = 8
1491
+ elif type_ == DataType.INT16:
1492
+ size = 2
1493
+ elif type_ == DataType.INT32:
1494
+ size = 4
1495
+ elif type_ == DataType.INT64:
1496
+ size = 8
1497
+ elif type_ == DataType.INT8:
1498
+ size = 1
1499
+ elif type_ == DataType.NONE:
1500
+ size = 0
1501
+ elif type_ == DataType.TIME:
1502
+ size = 4
1503
+ elif type_ == DataType.UINT16:
1504
+ size = 2
1505
+ elif type_ == DataType.UINT32:
1506
+ size = 4
1507
+ elif type_ == DataType.UINT64:
1508
+ size = 8
1509
+ elif type_ == DataType.UINT8:
1510
+ size = 1
1511
+ return size
1512
+
1513
+ @classmethod
1514
+ def toLogicalName(cls, value):
1515
+ if isinstance(value, bytearray):
1516
+ if not value:
1517
+ value = bytearray(6)
1518
+ if len(value) == 6:
1519
+ return str(value[0]) + "." + str(value[1]) + "." + str(value[2]) + "." + str(value[3]) + "." + str(value[4]) + "." + str(value[5])
1520
+ raise ValueError("Invalid Logical name.")
1521
+ return str(value)
1522
+
1523
+ @classmethod
1524
+ def logicalNameToBytes(cls, value):
1525
+ if not value:
1526
+ return bytearray(6)
1527
+ items = value.split('.')
1528
+ if len(items) != 6:
1529
+ raise ValueError("Invalid Logical name.")
1530
+ buff = bytearray(6)
1531
+ pos = 0
1532
+ for it in items:
1533
+ v = int(it)
1534
+ if v < 0 or v > 255:
1535
+ raise ValueError("Invalid Logical name.")
1536
+ buff[pos] = int(v)
1537
+ pos += 1
1538
+ return buff
1539
+
1540
+ @classmethod
1541
+ def getGeneralizedTime(cls, dateString):
1542
+ year = int(dateString[0:4])
1543
+ month = int(dateString[4:6])
1544
+ day = int(dateString[6:8])
1545
+ hour = int(dateString[8:10])
1546
+ minute = int(dateString[10:12])
1547
+ #If UTC time.
1548
+ if dateString.endsWith("Z"):
1549
+ if len(dateString) > 13:
1550
+ second = int(dateString[12:14])
1551
+ return datetime(year, month, day, hour, minute, second, 0, tzinfo=GXTimeZone(0))
1552
+
1553
+ if len(dateString) > 17:
1554
+ second = int(dateString.substring(12, 14))
1555
+ tz = dateString[dateString.length() - 4:]
1556
+ return datetime(year, month, day, hour, minute, second, 0, tzinfo=GXTimeZone(tz))
1557
+
1558
+ @classmethod
1559
+ def generalizedTime(cls, value):
1560
+ #Convert to UTC time.
1561
+ if isinstance(value, (GXDateTime)):
1562
+ value = value.value
1563
+ value = value.utctimetuple()
1564
+ sb = cls.integerString(value.tm_year, 4)
1565
+ sb += cls.integerString(value.tm_mon, 2)
1566
+ sb += cls.integerString(value.tm_mday, 2)
1567
+ sb += cls.integerString(value.tm_hour, 2)
1568
+ sb += cls.integerString(value.tm_min, 2)
1569
+ sb += cls.integerString(value.tm_sec, 2)
1570
+ #UTC time.
1571
+ sb += "Z"
1572
+ return sb
1573
+
1574
+ @classmethod
1575
+ def encryptManufacturer(cls, flagName):
1576
+ if len(flagName) != 3:
1577
+ raise ValueError("Invalid Flag name.")
1578
+ value = ((flagName.charAt(0) - 0x40) & 0x1f)
1579
+ value <<= 5
1580
+ value += ((flagName.charAt(0) - 0x40) & 0x1f)
1581
+ value <<= 5
1582
+ value += ((flagName.charAt(0) - 0x40) & 0x1f)
1583
+ return value
1584
+
1585
+ @classmethod
1586
+ def decryptManufacturer(cls, value):
1587
+ tmp = (value >> 8 | value << 8)
1588
+ c = str(((tmp & 0x1f) + 0x40))
1589
+ tmp = (tmp >> 5)
1590
+ c1 = str(((tmp & 0x1f) + 0x40))
1591
+ tmp = (tmp >> 5)
1592
+ c2 = str(((tmp & 0x1f) + 0x40))
1593
+ return str(c2, c1, c)
1594
+
1595
+ @classmethod
1596
+ def idisSystemTitleToString(cls, st):
1597
+ sb = '\n'
1598
+ sb += "IDIS system title:\n"
1599
+ sb += "Manufacturer Code: "
1600
+ sb += cls.__getChar(st[0]) + cls.__getChar(st[1]) + cls.__getChar(st[2])
1601
+ sb += "\nFunction type: "
1602
+ ft = st[4] >> 4
1603
+ add = False
1604
+ if (ft & 0x1) != 0:
1605
+ sb += "Disconnector extension"
1606
+ add = True
1607
+ if (ft & 0x2) != 0:
1608
+ if add:
1609
+ sb += ", "
1610
+ add = True
1611
+ sb += "Load Management extension"
1612
+
1613
+ if (ft & 0x4) != 0:
1614
+ if add:
1615
+ sb += ", "
1616
+ sb += "Multi Utility extension"
1617
+ #Serial number
1618
+ sn = (st[4] & 0xF) << 24
1619
+ sn |= st[5] << 16
1620
+ sn |= st[6] << 8
1621
+ sn |= st[7]
1622
+ sb += '\n'
1623
+ sb += "Serial number: "
1624
+ sb += str(sn) + '\n'
1625
+ return sb
1626
+
1627
+ @classmethod
1628
+ def dlmsSystemTitleToString(cls, st):
1629
+ sb = '\n'
1630
+ sb += "IDIS system title:\n"
1631
+ sb += "Manufacturer Code: "
1632
+ sb += cls.__getChar(st[0]) + cls.__getChar(st[1]) + cls.__getChar(st[2])
1633
+ sb += "Serial number: "
1634
+ sb += cls.__getChar(st[3]) + cls.__getChar(st[4]) + cls.__getChar(st[5]) + cls.__getChar(st[6]) + cls.__getChar(st[7])
1635
+ return sb
1636
+
1637
+ @classmethod
1638
+ def uniSystemTitleToString(cls, st):
1639
+ sb = '\n'
1640
+ sb += "UNI/TS system title:\n"
1641
+ sb += "Manufacturer: "
1642
+ m = st[0] << 8 | st[1]
1643
+ sb += cls.decryptManufacturer(m)
1644
+ sb += "\nSerial number: "
1645
+ sb += GXByteBuffer.toHex((st[7], st[6], st[5], st[4], st[3], st[2]), False)
1646
+ return sb
1647
+
1648
+ @classmethod
1649
+ def __getChar(cls, ch):
1650
+ try:
1651
+ return str(chr(ch))
1652
+ except Exception:
1653
+ #If python 2.7 is used.
1654
+ #pylint: disable=undefined-variable
1655
+ return str(unichr(ch))
1656
+
1657
+ @classmethod
1658
+ def systemTitleToString(cls, standard, st):
1659
+ ###Conver system title to string.
1660
+ #pylint: disable=too-many-boolean-expressions
1661
+ if standard == Standard.ITALY or not cls.__getChar(st[0]).isalpha() or \
1662
+ not cls.__getChar(st[1]).isalpha() or not cls.__getChar(st[2]).isalpha():
1663
+ return cls.uniSystemTitleToString(st)
1664
+ if standard == Standard.IDIS or not cls.__getChar(st[3]).isdigit() or \
1665
+ not cls.__getChar(st[4]).isdigit() or not cls.__getChar(st[5]).isdigit() or \
1666
+ not cls.__getChar(st[6]).isdigit() or not cls.__getChar(st[7]).isdigit():
1667
+ return cls.idisSystemTitleToString(st)
1668
+ return cls.dlmsSystemTitleToString(st)
1669
+
1670
+ #Reserved for internal use.
1671
+ @classmethod
1672
+ def swapBits(cls, value):
1673
+ return int(F'{value:08b}'[::-1], 2)