DLMS-SPODES 0.87.17__py3-none-any.whl → 0.88.1__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/Values/EN/__init__.py +1 -1
- DLMS_SPODES/Values/EN/actors.py +8 -8
- DLMS_SPODES/Values/EN/relation_to_obis_names.py +387 -387
- DLMS_SPODES/Values/RU/__init__.py +1 -1
- DLMS_SPODES/Values/RU/actors.py +8 -8
- DLMS_SPODES/Values/RU/relation_to_obis_names.py +396 -396
- DLMS_SPODES/__init__.py +6 -6
- DLMS_SPODES/configEN.ini +126 -126
- DLMS_SPODES/config_parser.py +53 -53
- DLMS_SPODES/cosem_interface_classes/Overview/__init__.py +0 -0
- DLMS_SPODES/cosem_interface_classes/Overview/class_id.py +107 -0
- DLMS_SPODES/cosem_interface_classes/__class_init__.py +3 -3
- DLMS_SPODES/cosem_interface_classes/__init__.py +3 -2
- DLMS_SPODES/cosem_interface_classes/activity_calendar.py +210 -254
- DLMS_SPODES/cosem_interface_classes/arbitrator.py +78 -105
- DLMS_SPODES/cosem_interface_classes/association_ln/abstract.py +50 -34
- DLMS_SPODES/cosem_interface_classes/association_ln/authentication_mechanism_name.py +25 -25
- DLMS_SPODES/cosem_interface_classes/association_ln/mechanism_id.py +25 -25
- DLMS_SPODES/cosem_interface_classes/association_ln/method.py +5 -5
- DLMS_SPODES/cosem_interface_classes/association_ln/ver0.py +440 -485
- DLMS_SPODES/cosem_interface_classes/association_ln/ver1.py +126 -133
- DLMS_SPODES/cosem_interface_classes/association_ln/ver2.py +30 -36
- DLMS_SPODES/cosem_interface_classes/association_ln/ver3.py +3 -4
- DLMS_SPODES/cosem_interface_classes/association_sn/ver0.py +14 -12
- DLMS_SPODES/cosem_interface_classes/clock.py +81 -131
- DLMS_SPODES/cosem_interface_classes/collection.py +2106 -2122
- DLMS_SPODES/cosem_interface_classes/cosem_interface_class.py +525 -583
- DLMS_SPODES/cosem_interface_classes/data.py +12 -21
- DLMS_SPODES/cosem_interface_classes/demand_register/ver0.py +32 -59
- DLMS_SPODES/cosem_interface_classes/disconnect_control.py +56 -74
- DLMS_SPODES/cosem_interface_classes/extended_register.py +18 -27
- DLMS_SPODES/cosem_interface_classes/gprs_modem_setup.py +33 -43
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver0.py +78 -103
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver1.py +42 -40
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver2.py +6 -9
- DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver0.py +11 -11
- DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver1.py +27 -53
- DLMS_SPODES/cosem_interface_classes/iec_local_port_setup.py +9 -11
- DLMS_SPODES/cosem_interface_classes/image_transfer/image_transfer_status.py +15 -15
- DLMS_SPODES/cosem_interface_classes/image_transfer/ver0.py +54 -126
- DLMS_SPODES/cosem_interface_classes/implementations/__init__.py +3 -3
- DLMS_SPODES/cosem_interface_classes/implementations/arbitrator.py +19 -19
- DLMS_SPODES/cosem_interface_classes/implementations/data.py +491 -487
- DLMS_SPODES/cosem_interface_classes/implementations/profile_generic.py +85 -83
- DLMS_SPODES/cosem_interface_classes/ipv4_setup.py +42 -72
- DLMS_SPODES/cosem_interface_classes/limiter.py +77 -111
- DLMS_SPODES/cosem_interface_classes/ln_pattern.py +334 -333
- DLMS_SPODES/cosem_interface_classes/modem_configuration/ver0.py +51 -65
- DLMS_SPODES/cosem_interface_classes/modem_configuration/ver1.py +27 -39
- DLMS_SPODES/cosem_interface_classes/ntp_setup/ver0.py +48 -67
- DLMS_SPODES/cosem_interface_classes/obis.py +28 -23
- DLMS_SPODES/cosem_interface_classes/overview.py +198 -197
- DLMS_SPODES/cosem_interface_classes/parameter.py +548 -547
- DLMS_SPODES/cosem_interface_classes/parameters.py +172 -172
- DLMS_SPODES/cosem_interface_classes/profile_generic/ver0.py +90 -133
- DLMS_SPODES/cosem_interface_classes/profile_generic/ver1.py +268 -277
- DLMS_SPODES/cosem_interface_classes/push_setup/ver0.py +13 -12
- DLMS_SPODES/cosem_interface_classes/push_setup/ver1.py +9 -10
- DLMS_SPODES/cosem_interface_classes/push_setup/ver2.py +124 -166
- DLMS_SPODES/cosem_interface_classes/register.py +18 -45
- DLMS_SPODES/cosem_interface_classes/register_activation/ver0.py +45 -80
- DLMS_SPODES/cosem_interface_classes/register_monitor.py +33 -46
- DLMS_SPODES/cosem_interface_classes/reports.py +72 -70
- DLMS_SPODES/cosem_interface_classes/schedule.py +88 -176
- DLMS_SPODES/cosem_interface_classes/script_table.py +54 -87
- DLMS_SPODES/cosem_interface_classes/security_setup/ver0.py +45 -68
- DLMS_SPODES/cosem_interface_classes/security_setup/ver1.py +122 -158
- DLMS_SPODES/cosem_interface_classes/single_action_schedule.py +34 -50
- DLMS_SPODES/cosem_interface_classes/special_days_table.py +54 -84
- DLMS_SPODES/cosem_interface_classes/tcp_udp_setup.py +20 -42
- DLMS_SPODES/cosem_pdu.py +93 -93
- DLMS_SPODES/enums.py +625 -625
- DLMS_SPODES/exceptions.py +106 -106
- DLMS_SPODES/firmwares.py +99 -99
- DLMS_SPODES/hdlc/frame.py +875 -875
- DLMS_SPODES/hdlc/sub_layer.py +54 -54
- DLMS_SPODES/literals.py +17 -17
- DLMS_SPODES/obis/__init__.py +1 -1
- DLMS_SPODES/obis/media_id.py +931 -931
- DLMS_SPODES/pardata.py +22 -22
- DLMS_SPODES/pdu_enums.py +98 -98
- DLMS_SPODES/relation_to_OBIS.py +463 -465
- DLMS_SPODES/settings.py +551 -551
- DLMS_SPODES/types/choices.py +140 -142
- DLMS_SPODES/types/common_data_types.py +2379 -2401
- DLMS_SPODES/types/cosem_service_types.py +109 -109
- DLMS_SPODES/types/implementations/arrays.py +25 -25
- DLMS_SPODES/types/implementations/bitstrings.py +97 -97
- DLMS_SPODES/types/implementations/double_long_usingneds.py +35 -35
- DLMS_SPODES/types/implementations/enums.py +57 -57
- DLMS_SPODES/types/implementations/integers.py +12 -11
- DLMS_SPODES/types/implementations/long_unsigneds.py +127 -127
- DLMS_SPODES/types/implementations/octet_string.py +11 -11
- DLMS_SPODES/types/implementations/structs.py +64 -64
- DLMS_SPODES/types/type_alias.py +74 -0
- DLMS_SPODES/types/useful_types.py +627 -677
- {dlms_spodes-0.87.17.dist-info → dlms_spodes-0.88.1.dist-info}/METADATA +30 -30
- dlms_spodes-0.88.1.dist-info/RECORD +118 -0
- {dlms_spodes-0.87.17.dist-info → dlms_spodes-0.88.1.dist-info}/WHEEL +1 -1
- DLMS_SPODES/cosem_interface_classes/a_parameter.py +0 -20
- DLMS_SPODES/cosem_interface_classes/attr_indexes.py +0 -12
- dlms_spodes-0.87.17.dist-info/RECORD +0 -117
- {dlms_spodes-0.87.17.dist-info → dlms_spodes-0.88.1.dist-info}/top_level.txt +0 -0
|
@@ -1,333 +1,334 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Self, Literal
|
|
3
|
-
from ..types import cst
|
|
4
|
-
from copy import copy
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- L=
|
|
35
|
-
- L=
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
cls.__simple_validate(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
buffer.
|
|
101
|
-
buffer.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
cls.__simple_validate(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
#
|
|
122
|
-
buffer.
|
|
123
|
-
buffer.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
start
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
LNPattern.parse("0.b.0.1.(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
*
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Self, Literal
|
|
3
|
+
from ..types import cst
|
|
4
|
+
from copy import copy
|
|
5
|
+
from ..types.type_alias import Obis
|
|
6
|
+
|
|
7
|
+
SKIP: int = 0
|
|
8
|
+
RANGE256 = set(range(256))
|
|
9
|
+
RANGE64 = bytes(range(64))
|
|
10
|
+
RANGE64_WITH_LENGTH = b'\x40' + RANGE64
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True, slots=True)
|
|
14
|
+
class LNPattern:
|
|
15
|
+
"""
|
|
16
|
+
LNPattern ::= SEQUENCE (SIZE (6)) OF GroupPattern
|
|
17
|
+
|
|
18
|
+
GroupPattern ::= CHOICE {
|
|
19
|
+
skip [0] NULL, -- SKIP marker
|
|
20
|
+
single [1] INTEGER (0..255), -- single value
|
|
21
|
+
multiple [2] SEQUENCE OF INTEGER (0..255) -- set of values
|
|
22
|
+
}
|
|
23
|
+
LNPattern Binary Encoding Specification:
|
|
24
|
+
---------------------------------------
|
|
25
|
+
A compact tag-less binary format for storing 6 OBIS-like groups.
|
|
26
|
+
|
|
27
|
+
Structure:
|
|
28
|
+
[L1][V1][L2][V2]...[L6][V6]
|
|
29
|
+
where:
|
|
30
|
+
- Ln: 1-byte length prefix for group n (0-255)
|
|
31
|
+
- Vn: Value bytes (interpretation depends on Ln)
|
|
32
|
+
|
|
33
|
+
Length Semantics:
|
|
34
|
+
- L=0 : SKIP group (no value bytes follow)
|
|
35
|
+
- L=1 : Single value (V is 1-byte integer 0-255)
|
|
36
|
+
- L=2..255 : Value set (V contains L bytes as possible values)
|
|
37
|
+
|
|
38
|
+
Special Cases:
|
|
39
|
+
- Group 'b' (index 1) when marked SKIP uses predefined RANGE64 (0-64)
|
|
40
|
+
- Empty exclusion sets "!()" are prohibited
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
Pattern "a.1.(2-5).!().0.f" encodes as:
|
|
44
|
+
[00][01][01][04][02][03][04][05][00][01][00][00]
|
|
45
|
+
(SKIP|1|{2,3,4,5}|SKIP|0|SKIP)
|
|
46
|
+
|
|
47
|
+
Properties:
|
|
48
|
+
- Fixed overhead: 6 bytes (1 length byte per group)
|
|
49
|
+
- Max size: 6 + 255*6 = 1536 bytes
|
|
50
|
+
- Order-preserving
|
|
51
|
+
- Comparison-friendly memory layout
|
|
52
|
+
"""
|
|
53
|
+
buffer: bytes
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def parse(cls, value: str) -> Self:
|
|
57
|
+
buffer = bytearray()
|
|
58
|
+
parts = value.split('.', maxsplit=5)
|
|
59
|
+
if len(parts) != 6:
|
|
60
|
+
raise ValueError(f"got {len(parts)} elements, expected 6")
|
|
61
|
+
for i, val in enumerate(parts):
|
|
62
|
+
if val.isdigit():
|
|
63
|
+
num = int(val)
|
|
64
|
+
if 0 <= num <= 255:
|
|
65
|
+
buffer.extend((1, num))
|
|
66
|
+
continue
|
|
67
|
+
raise ValueError(f"Value {val} out of range 0-255")
|
|
68
|
+
if len(val) == 1:
|
|
69
|
+
if val == "x":
|
|
70
|
+
buffer.append(SKIP)
|
|
71
|
+
continue
|
|
72
|
+
elif (
|
|
73
|
+
i == 1
|
|
74
|
+
and val == "b"
|
|
75
|
+
):
|
|
76
|
+
buffer.extend(RANGE64_WITH_LENGTH)
|
|
77
|
+
continue
|
|
78
|
+
elif ord(val) == i + 97:
|
|
79
|
+
buffer.append(SKIP)
|
|
80
|
+
continue
|
|
81
|
+
if val == "":
|
|
82
|
+
buffer.append(SKIP)
|
|
83
|
+
continue
|
|
84
|
+
if val[0]=='(' and val[-1]==')':
|
|
85
|
+
el = set()
|
|
86
|
+
val = val.replace('(', "").replace(')', "")
|
|
87
|
+
for j in val.split(","):
|
|
88
|
+
j = j.replace(" ", '')
|
|
89
|
+
match j.count('-'):
|
|
90
|
+
case 0:
|
|
91
|
+
el.add(cls.__simple_validate(j))
|
|
92
|
+
case 1:
|
|
93
|
+
start, end = j.split("-")
|
|
94
|
+
el.update(range(
|
|
95
|
+
cls.__simple_validate(start),
|
|
96
|
+
cls.__simple_validate(end) + 1))
|
|
97
|
+
case err:
|
|
98
|
+
raise ValueError(F"got a lot of <-> in pattern: {value}, expected one")
|
|
99
|
+
# values = bytes(el)
|
|
100
|
+
# buffer.extend([len(values)] + list(values))
|
|
101
|
+
buffer.append(len(el))
|
|
102
|
+
buffer.extend(el)
|
|
103
|
+
continue
|
|
104
|
+
if val.startswith('!(') and val.endswith(')'):
|
|
105
|
+
el = copy(RANGE256)
|
|
106
|
+
val = val.replace('!(', "").replace(')', "")
|
|
107
|
+
for j in val.split(","):
|
|
108
|
+
j = j.replace(" ", '')
|
|
109
|
+
match j.count('-'):
|
|
110
|
+
case 0:
|
|
111
|
+
el.discard(cls.__simple_validate(j))
|
|
112
|
+
case 1:
|
|
113
|
+
start, end = j.split("-")
|
|
114
|
+
el.difference_update(range(
|
|
115
|
+
cls.__simple_validate(start),
|
|
116
|
+
cls.__simple_validate(end) + 1))
|
|
117
|
+
case err:
|
|
118
|
+
raise ValueError(F"got a lot of <-> in pattern: {value}, expected one")
|
|
119
|
+
if len(el)==0:
|
|
120
|
+
raise ValueError(F"no one element in group: {chr(97 + i)}")
|
|
121
|
+
# values = bytes(el)
|
|
122
|
+
# buffer.extend([len(values)] + list(values))
|
|
123
|
+
buffer.append(len(el))
|
|
124
|
+
buffer.extend(el)
|
|
125
|
+
continue
|
|
126
|
+
raise ValueError(f"Invalid pattern: {val}")
|
|
127
|
+
return cls(bytes(buffer))
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def __simple_validate(value: str) -> int:
|
|
131
|
+
if value.isdigit() and (0 <= (new := int(value)) <= 255):
|
|
132
|
+
return new
|
|
133
|
+
else:
|
|
134
|
+
raise ValueError(F"got not valid element: {value} in pattern, expected 0..255")
|
|
135
|
+
|
|
136
|
+
def __eq__(self, other: Obis) -> bool:
|
|
137
|
+
ptr = 0
|
|
138
|
+
for i in range(6):
|
|
139
|
+
length = self.buffer[ptr]
|
|
140
|
+
ptr += 1
|
|
141
|
+
if length == 0: # SKIP
|
|
142
|
+
continue
|
|
143
|
+
other_byte = other[i]
|
|
144
|
+
if length == 1: # Single byte
|
|
145
|
+
if self.buffer[ptr] != other_byte:
|
|
146
|
+
return False
|
|
147
|
+
else: # Multiple bytes
|
|
148
|
+
if other_byte not in self.buffer[ptr:ptr + length]:
|
|
149
|
+
return False
|
|
150
|
+
ptr += length
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def _format_ranges(values: list[int]) -> str:
|
|
155
|
+
if not values:
|
|
156
|
+
return "!()"
|
|
157
|
+
ranges = []
|
|
158
|
+
start = end = values[0]
|
|
159
|
+
for num in values[1:]:
|
|
160
|
+
if num==end + 1:
|
|
161
|
+
end = num
|
|
162
|
+
else:
|
|
163
|
+
ranges.append((start, end))
|
|
164
|
+
start = end = num
|
|
165
|
+
ranges.append((start, end))
|
|
166
|
+
parts = []
|
|
167
|
+
for start, end in ranges:
|
|
168
|
+
if start==end:
|
|
169
|
+
parts.append(str(start))
|
|
170
|
+
elif end==start + 1: # Диапазон из 2 чисел
|
|
171
|
+
parts.extend([str(start), str(end)])
|
|
172
|
+
else:
|
|
173
|
+
parts.append(f"{start}-{end}")
|
|
174
|
+
if len(parts) > 3 and len(values) > 128: # Эмпирический порог
|
|
175
|
+
all_values = set(range(256))
|
|
176
|
+
excluded = sorted(all_values - set(values))
|
|
177
|
+
if len(excluded) < len(values):
|
|
178
|
+
return f"!({','.join(self._format_ranges(excluded))})"
|
|
179
|
+
return f"({','.join(parts)})"
|
|
180
|
+
|
|
181
|
+
def __str__(self) -> str:
|
|
182
|
+
parts = []
|
|
183
|
+
ptr = 0
|
|
184
|
+
for _ in range(6):
|
|
185
|
+
length = self.buffer[ptr]
|
|
186
|
+
ptr += 1
|
|
187
|
+
if length == 0:
|
|
188
|
+
parts.append("x")
|
|
189
|
+
elif length == 1:
|
|
190
|
+
parts.append(str(self.buffer[ptr]))
|
|
191
|
+
else:
|
|
192
|
+
values = sorted(set(self.buffer[ptr:ptr + length]))
|
|
193
|
+
parts.append(self._format_ranges(values))
|
|
194
|
+
ptr += length
|
|
195
|
+
return ".".join(parts)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@dataclass
|
|
199
|
+
class LNPatterns:
|
|
200
|
+
value: tuple[LNPattern, ...]
|
|
201
|
+
|
|
202
|
+
def __iter__(self):
|
|
203
|
+
return iter(self.value)
|
|
204
|
+
|
|
205
|
+
def __str__(self) -> str:
|
|
206
|
+
return f"[{" | ".join(map(str, self.value))}]"
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
ABSTRACT = LNPattern.parse("0.....")
|
|
210
|
+
ELECTRICITY = LNPattern.parse("1.....")
|
|
211
|
+
HCA = LNPattern.parse("4.....")
|
|
212
|
+
THERMAL = LNPattern.parse("(5,6).....")
|
|
213
|
+
GAS = LNPattern.parse("7.....")
|
|
214
|
+
WATER = LNPattern.parse("(8,9).....")
|
|
215
|
+
OTHER_MEDIA = LNPattern.parse("15.....")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES = LNPatterns((
|
|
219
|
+
LNPattern.parse("0.b.0.1.(0,2,3,5).f"),
|
|
220
|
+
LNPattern.parse("0.b.0.1.(1,4).255")))
|
|
221
|
+
PROGRAM_ENTRIES = LNPatterns((
|
|
222
|
+
ACTIVE_FIRMWARE_IDENTIFIER := LNPattern.parse("0.b.0.2.0.255"),
|
|
223
|
+
ACTIVE_FIRMWARE_VERSION := LNPattern.parse("0.b.0.2.1.255"),
|
|
224
|
+
ACTIVE_FIRMWARE_SIGNATURE := LNPattern.parse("0.b.0.2.8.255")
|
|
225
|
+
))
|
|
226
|
+
TIME_ENTRIES = LNPattern.parse("0.b.0.9.(1,2).255")
|
|
227
|
+
CLOCK_OBJECTS = LNPatterns((
|
|
228
|
+
CLOCK := LNPattern.parse("0.b.1.0.e.255"),
|
|
229
|
+
UNIX_CLOCK := LNPattern.parse("0.b.1.1.e.255"),
|
|
230
|
+
MICROSECONDS_CLOCK := LNPattern.parse("0.b.1.2.e.255"),
|
|
231
|
+
MINUTES_CLOCK := LNPattern.parse("0.b.1.3.e.255"),
|
|
232
|
+
HOURS_CLOCK := LNPattern.parse("0.b.1.4.e.255"),
|
|
233
|
+
DAYS_CLOCK := LNPattern.parse("0.b.1.5.e.255"),
|
|
234
|
+
WEEKS_CLOCK := LNPattern.parse("0.b.1.6.e.255")
|
|
235
|
+
))
|
|
236
|
+
TARIFFICATION_SCRIPT_TABLE = LNPattern.parse("0.b.10.0.100.255")
|
|
237
|
+
PUSH_SCRIPT_TABLE = LNPattern.parse("0.b.10.0.108.255")
|
|
238
|
+
SINGLE_ACTION_SCHEDULE = LNPatterns((
|
|
239
|
+
END_OF_BILLING_PERIOD_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.0.255"),
|
|
240
|
+
DISCONNECT_CONTROL_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.1.255"),
|
|
241
|
+
IMAGE_ACTIVATION_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.2.255"),
|
|
242
|
+
OUTPUT_CONTROL_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.3.255"),
|
|
243
|
+
PUSH_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.4.255"),
|
|
244
|
+
LOAD_PROFILE_CONTROL_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.5.255"),
|
|
245
|
+
M_BUS_PROFILE_CONTROL_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.6.255"),
|
|
246
|
+
FUNCTION_CONTROL_SINGLE_ACTION_SCHEDULE := LNPattern.parse("0.b.15.0.7.255")
|
|
247
|
+
))
|
|
248
|
+
ACTIVITY_CALENDAR = LNPattern.parse("0.b.13.0.e.255")
|
|
249
|
+
ASSOCIATION = LNPattern.parse("0.0.40.0.e.255") # 6_2_33
|
|
250
|
+
NON_CURRENT_ASSOCIATION = LNPattern.parse("0.0.40.0.(1-255).255") # MY
|
|
251
|
+
SAP_ASSIGNMENT = LNPattern.parse("0.0.41.0.0.255") # 6.2.34
|
|
252
|
+
COSEM_logical_device_name = LNPattern.parse("0.0.42.0.0.255") # 6.2.35
|
|
253
|
+
INFORMATION_SECURITY_RELATED = LNPatterns((
|
|
254
|
+
LNPattern.parse("0.0.43.(0,2).e.255"),
|
|
255
|
+
INVOCATION_COUNTER := LNPattern.parse("0.b.43.1.e.255") # 6.2.36
|
|
256
|
+
))
|
|
257
|
+
IMAGE_TRANSFER = LNPattern.parse("0.b.44.0.e.255") # 6.2.37
|
|
258
|
+
FUNCTION_CONTROL = LNPattern.parse("0.b.44.1.e.255") # 6.2.38
|
|
259
|
+
COMMUNICATION_PORT_PROTECTION = LNPattern.parse("0.b.44.2.e.255") # 6.2.39
|
|
260
|
+
UTILITY_TABLE = LNPattern.parse("0.b.65.(0-63).e.255") # 6.2.40
|
|
261
|
+
COMPACT_DATA = LNPattern.parse("0.b.66.0.e.255") # 6.2.41
|
|
262
|
+
DEVICE_ID = LNPattern.parse("0.b.96.1.(0,1,2,3,4,5,6,7,8,9,255).255") # 6.2.42
|
|
263
|
+
METERING_POINT_ID = LNPattern.parse("0.b.96.1.10.255") # 6.2.43
|
|
264
|
+
PARAMETER_CHANGES_CALIBRATION_AND_ACCESS = LNPattern.parse("0.b.96.2.e.f") # 6.2.44
|
|
265
|
+
INPUT_OUTPUT_CONTROL_SIGNALS = LNPattern.parse("0.b.96.3.(0-4).f") # 6.2.45
|
|
266
|
+
DISCONNECT_CONTROL = LNPattern.parse("0.b.96.3.10.f") # 6.2.46
|
|
267
|
+
ARBITRATOR = LNPattern.parse("0.b.96.3.(20-29).f") # 6.2.47
|
|
268
|
+
INTERNAL_CONTROL_SIGNALS = LNPattern.parse("0.b.96.4.(0-4).f") # 6.2.48
|
|
269
|
+
INTERNAL_OPERATING_STATUS = LNPattern.parse("0.b.96.5.(0-4).f") # 6.2.49
|
|
270
|
+
BATTERY_ENTRIES = LNPattern.parse("0.b.96.6.(0,1,2,3,4,5,6,10,11).f") # 6.2.50
|
|
271
|
+
POWER_FAILURE_MONITORING = LNPattern.parse("0.b.96.7.(0-21).f") # 6.2.51
|
|
272
|
+
OPERATING_TIME = LNPattern.parse("0.b.96.8.(0-63).f")
|
|
273
|
+
ENVIRONMENT_RELATED_PARAMETERS = LNPattern.parse("0.b.96.9.(0-2).f")
|
|
274
|
+
STATUS_REGISTER = LNPattern.parse("0.b.96.10.(1-10).f")
|
|
275
|
+
EVENT_CODE = LNPattern.parse("0.b.96.11.(0-99).f")
|
|
276
|
+
COMMUNICATION_PORT_LOG_PARAMETERS = LNPattern.parse("0.b.96.12.(0-6).f")
|
|
277
|
+
CONSUMER_MESSAGES = LNPattern.parse("0.b.96.13.(0,1).f")
|
|
278
|
+
CURRENTLY_ACTIVE_TARIFF = LNPattern.parse("0.b.96.14.(0-15).f")
|
|
279
|
+
EVENT_COUNTER = LNPattern.parse("0.b.96.15.(0-99).f")
|
|
280
|
+
PROFILE_ENTRY_DIGITAL_SIGNATURE = LNPattern.parse("0.b.96.16.(0-9).f")
|
|
281
|
+
PROFILE_ENTRY_COUNTER = LNPattern.parse("0.b.96.17.(0-127).f")
|
|
282
|
+
METER_TAMPER_EVENT_RELATED = LNPattern.parse("0.b.96.20.(0-34).f")
|
|
283
|
+
MANUFACTURER_SPECIFIC_ABSTRACT = LNPattern.parse("0.b.96.(50-99).e.f")
|
|
284
|
+
|
|
285
|
+
GENERAL_AND_SERVICE_ENTRY = LNPatterns((
|
|
286
|
+
*BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES,
|
|
287
|
+
*PROGRAM_ENTRIES,
|
|
288
|
+
TIME_ENTRIES,
|
|
289
|
+
DEVICE_ID,
|
|
290
|
+
PARAMETER_CHANGES_CALIBRATION_AND_ACCESS,
|
|
291
|
+
INPUT_OUTPUT_CONTROL_SIGNALS,
|
|
292
|
+
INTERNAL_CONTROL_SIGNALS,
|
|
293
|
+
INTERNAL_OPERATING_STATUS,
|
|
294
|
+
BATTERY_ENTRIES,
|
|
295
|
+
POWER_FAILURE_MONITORING,
|
|
296
|
+
OPERATING_TIME,
|
|
297
|
+
ENVIRONMENT_RELATED_PARAMETERS,
|
|
298
|
+
STATUS_REGISTER,
|
|
299
|
+
EVENT_CODE,
|
|
300
|
+
COMMUNICATION_PORT_LOG_PARAMETERS,
|
|
301
|
+
CONSUMER_MESSAGES,
|
|
302
|
+
CURRENTLY_ACTIVE_TARIFF,
|
|
303
|
+
EVENT_COUNTER,
|
|
304
|
+
PROFILE_ENTRY_DIGITAL_SIGNATURE,
|
|
305
|
+
PROFILE_ENTRY_COUNTER,
|
|
306
|
+
METER_TAMPER_EVENT_RELATED,
|
|
307
|
+
MANUFACTURER_SPECIFIC_ABSTRACT))
|
|
308
|
+
"""DLMS UA 1000-1 Ed. 14 7.4.1"""
|
|
309
|
+
|
|
310
|
+
LIMITER = LNPattern.parse("0.b.17.0.e.255") # 6.2.15
|
|
311
|
+
ALARM_REGISTER = LNPattern.parse("0.b.97.98.(0-9).255") # 6.2.64
|
|
312
|
+
COUNTRY_SPECIFIC_IDENTIFIERS = LNPattern.parse("0.b.94.d.e.f") # 7.3.4.3
|
|
313
|
+
ALARM_REGISTER_FILTER = LNPattern.parse("0.b.97.98.(10-19).255") # 6.2.64
|
|
314
|
+
ALARM_REGISTER_DESCRIPTOR = LNPattern.parse("0.b.97.98.(20-29).255") # 6.2.64
|
|
315
|
+
ALARM_REGISTER_PROFILE = LNPattern.parse("0.b.97.98.255.255") # 6.2.64
|
|
316
|
+
ALARM_REGISTER_TABLE = LNPattern.parse("0.b.97.98.255.255") # 6.2.64
|
|
317
|
+
ALARM_REGISTER_FILTER_DESCRIPTOR = LNPatterns((ALARM_REGISTER, ALARM_REGISTER_FILTER, ALARM_REGISTER_DESCRIPTOR, ALARM_REGISTER_PROFILE))
|
|
318
|
+
# electricity
|
|
319
|
+
ID_NUMBERS_ELECTRICITY = LNPattern.parse("1.b.0.0.(0-9).255")
|
|
320
|
+
ELECTRIC_PROGRAM_ENTRIES = LNPattern.parse("1.b.0.2.e.255")
|
|
321
|
+
OUTPUT_PULSE_VALUES_OR_CONSTANTS = LNPattern.parse("1.0.0.3.(0-9).255")
|
|
322
|
+
RATIOS = LNPattern.parse("1.0.0.4.(0-7).255")
|
|
323
|
+
RECORDING_INTERVAL = LNPattern.parse("1.0.0.8.(4,5).255")
|
|
324
|
+
OTHER_ELECTRICITY_RELATED_GENERAL_PURPOSE = LNPattern.parse("1.b.0.(2,3,4,6,7,8,9,10).e.255")
|
|
325
|
+
# my special
|
|
326
|
+
COUNTRY_SPECIFIC = LNPattern.parse("a.b.94.d.e.f") # 7.2.4 Table 54
|
|
327
|
+
|
|
328
|
+
# 7.5.2.1
|
|
329
|
+
INSTANTANEOUS_VALUE_EL_REL = LNPattern.parse("1.b.!(0,93,94,96,97,98,99).7.e.f")
|
|
330
|
+
ELECTRICITY_RELATED = LNPatterns((
|
|
331
|
+
INSTANTANEOUS_VALUE_EL_REL,
|
|
332
|
+
# todo: more other LNPatterns
|
|
333
|
+
))
|
|
334
|
+
"""7.5.2.1 Processing of measurement values Table 66"""
|