DLMS-SPODES-client 0.19.36__py3-none-any.whl → 0.19.38__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.
- DLMS_SPODES_client/FCS16.py +39 -39
- DLMS_SPODES_client/__init__.py +12 -12
- DLMS_SPODES_client/client.py +2093 -2093
- DLMS_SPODES_client/gurux_common/enums/TraceLevel.py +21 -21
- DLMS_SPODES_client/gurux_dlms/AesGcmParameter.py +37 -37
- DLMS_SPODES_client/gurux_dlms/CountType.py +16 -16
- DLMS_SPODES_client/gurux_dlms/GXByteBuffer.py +545 -545
- DLMS_SPODES_client/gurux_dlms/GXCiphering.py +196 -196
- DLMS_SPODES_client/gurux_dlms/GXDLMS.py +426 -426
- DLMS_SPODES_client/gurux_dlms/GXDLMSChippering.py +237 -237
- DLMS_SPODES_client/gurux_dlms/GXDLMSChipperingStream.py +977 -977
- DLMS_SPODES_client/gurux_dlms/GXDLMSConfirmedServiceError.py +90 -90
- DLMS_SPODES_client/gurux_dlms/GXDLMSException.py +139 -139
- DLMS_SPODES_client/gurux_dlms/GXDLMSLNParameters.py +33 -33
- DLMS_SPODES_client/gurux_dlms/GXDLMSSNParameters.py +21 -21
- DLMS_SPODES_client/gurux_dlms/GXDLMSSettings.py +254 -254
- DLMS_SPODES_client/gurux_dlms/GXReplyData.py +87 -87
- DLMS_SPODES_client/gurux_dlms/HdlcControlFrame.py +9 -9
- DLMS_SPODES_client/gurux_dlms/MBusCommand.py +8 -8
- DLMS_SPODES_client/gurux_dlms/MBusEncryptionMode.py +27 -27
- DLMS_SPODES_client/gurux_dlms/ResponseType.py +8 -8
- DLMS_SPODES_client/gurux_dlms/SetResponseType.py +29 -29
- DLMS_SPODES_client/gurux_dlms/_HDLCInfo.py +9 -9
- DLMS_SPODES_client/gurux_dlms/__init__.py +75 -75
- DLMS_SPODES_client/gurux_dlms/enums/Access.py +12 -12
- DLMS_SPODES_client/gurux_dlms/enums/ApplicationReference.py +14 -14
- DLMS_SPODES_client/gurux_dlms/enums/Authentication.py +41 -41
- DLMS_SPODES_client/gurux_dlms/enums/BerType.py +35 -35
- DLMS_SPODES_client/gurux_dlms/enums/Command.py +285 -285
- DLMS_SPODES_client/gurux_dlms/enums/Definition.py +9 -9
- DLMS_SPODES_client/gurux_dlms/enums/ErrorCode.py +46 -46
- DLMS_SPODES_client/gurux_dlms/enums/ExceptionServiceError.py +12 -12
- DLMS_SPODES_client/gurux_dlms/enums/HardwareResource.py +10 -10
- DLMS_SPODES_client/gurux_dlms/enums/HdlcFrameType.py +9 -9
- DLMS_SPODES_client/gurux_dlms/enums/Initiate.py +10 -10
- DLMS_SPODES_client/gurux_dlms/enums/LoadDataSet.py +13 -13
- DLMS_SPODES_client/gurux_dlms/enums/ObjectType.py +306 -306
- DLMS_SPODES_client/gurux_dlms/enums/Priority.py +7 -7
- DLMS_SPODES_client/gurux_dlms/enums/RequestTypes.py +9 -9
- DLMS_SPODES_client/gurux_dlms/enums/Security.py +14 -14
- DLMS_SPODES_client/gurux_dlms/enums/Service.py +16 -16
- DLMS_SPODES_client/gurux_dlms/enums/ServiceClass.py +9 -9
- DLMS_SPODES_client/gurux_dlms/enums/ServiceError.py +8 -8
- DLMS_SPODES_client/gurux_dlms/enums/Standard.py +18 -18
- DLMS_SPODES_client/gurux_dlms/enums/StateError.py +7 -7
- DLMS_SPODES_client/gurux_dlms/enums/Task.py +10 -10
- DLMS_SPODES_client/gurux_dlms/enums/VdeStateError.py +10 -10
- DLMS_SPODES_client/gurux_dlms/enums/__init__.py +33 -33
- DLMS_SPODES_client/gurux_dlms/internal/_GXCommon.py +1673 -1673
- DLMS_SPODES_client/logger.py +56 -56
- DLMS_SPODES_client/services.py +90 -90
- DLMS_SPODES_client/session.py +363 -363
- DLMS_SPODES_client/settings.py +48 -48
- DLMS_SPODES_client/task.py +1891 -1884
- {dlms_spodes_client-0.19.36.dist-info → dlms_spodes_client-0.19.38.dist-info}/METADATA +29 -29
- dlms_spodes_client-0.19.38.dist-info/RECORD +61 -0
- {dlms_spodes_client-0.19.36.dist-info → dlms_spodes_client-0.19.38.dist-info}/WHEEL +1 -1
- dlms_spodes_client-0.19.36.dist-info/RECORD +0 -61
- {dlms_spodes_client-0.19.36.dist-info → dlms_spodes_client-0.19.38.dist-info}/entry_points.txt +0 -0
- {dlms_spodes_client-0.19.36.dist-info → dlms_spodes_client-0.19.38.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)
|