DLMS-SPODES 0.87.16__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 -122
- 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.16.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.16.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.16.dist-info/RECORD +0 -117
- {dlms_spodes-0.87.16.dist-info → dlms_spodes-0.88.1.dist-info}/top_level.txt +0 -0
|
@@ -1,2122 +1,2106 @@
|
|
|
1
|
-
""" The OBIS identification system serves as a basis for the COSEM logical names. The system of naming COSEM objects is defined in the basic
|
|
2
|
-
principles (see Clause 4 EN 62056-62:2007), the identification of real data items is specified in IEC 62056-61. The following clauses define the
|
|
3
|
-
usage of those definitions in the COSEM environment. All codes, which are not explicitly listed, but outside the manufacturer specific range are
|
|
4
|
-
reserved for future use."""
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from .
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
21
|
-
from .association_ln
|
|
22
|
-
from .
|
|
23
|
-
from .association_ln.
|
|
24
|
-
from .
|
|
25
|
-
from .
|
|
26
|
-
from .push_setup.
|
|
27
|
-
from .
|
|
28
|
-
from .
|
|
29
|
-
from .
|
|
30
|
-
from .
|
|
31
|
-
from .
|
|
32
|
-
from .
|
|
33
|
-
from .gsm_diagnostic.
|
|
34
|
-
from .
|
|
35
|
-
from .
|
|
36
|
-
from .
|
|
37
|
-
from .
|
|
38
|
-
from .
|
|
39
|
-
from .
|
|
40
|
-
from .
|
|
41
|
-
from .
|
|
42
|
-
from .
|
|
43
|
-
from .
|
|
44
|
-
from .
|
|
45
|
-
from .
|
|
46
|
-
from .
|
|
47
|
-
from .
|
|
48
|
-
from .
|
|
49
|
-
from .
|
|
50
|
-
from .
|
|
51
|
-
from .
|
|
52
|
-
from .
|
|
53
|
-
from .
|
|
54
|
-
from .
|
|
55
|
-
from .
|
|
56
|
-
from
|
|
57
|
-
from
|
|
58
|
-
from ..
|
|
59
|
-
from ..
|
|
60
|
-
from
|
|
61
|
-
from .. import
|
|
62
|
-
from
|
|
63
|
-
from ..
|
|
64
|
-
from
|
|
65
|
-
from
|
|
66
|
-
from
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
class
|
|
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
|
-
class
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
class
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
ACDgroup((0,
|
|
376
|
-
ACDgroup((0,
|
|
377
|
-
|
|
378
|
-
ACDgroup((0,
|
|
379
|
-
ACDgroup((0, 1,
|
|
380
|
-
ACDgroup((0, 1,
|
|
381
|
-
ACDgroup((0, 1,
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
ACDgroup((0,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
ACDgroup((0,
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
#
|
|
397
|
-
ACDgroup((0,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
#
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
ACDEgroup((0,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
ACDEgroup((0, 25,
|
|
415
|
-
#
|
|
416
|
-
ACDEgroup((0, 25,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
#
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
ACDEEgroup((0, 96,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
ACDEEgroup((0, 96,
|
|
440
|
-
ACDEEgroup((0, 96,
|
|
441
|
-
ACDEEgroup((0, 96,
|
|
442
|
-
|
|
443
|
-
ACDEEgroup((0, 96,
|
|
444
|
-
ACDEEgroup((0, 96,
|
|
445
|
-
ACDEEgroup((0, 96,
|
|
446
|
-
|
|
447
|
-
ACDEEgroup((0, 96,
|
|
448
|
-
|
|
449
|
-
ACDEEgroup((0,
|
|
450
|
-
|
|
451
|
-
ACDEEgroup((0,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
#
|
|
455
|
-
ACDEEgroup((
|
|
456
|
-
|
|
457
|
-
ACDgroup((
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
ACDEEgroup((1,
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
ACDEEgroup((1,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
__func_map_for_create
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
ACDEgroup((0, 96,
|
|
501
|
-
|
|
502
|
-
ACDEgroup((0, 96,
|
|
503
|
-
ACDEgroup((0, 96,
|
|
504
|
-
ACDEgroup((0, 96,
|
|
505
|
-
ACDEgroup((0, 96,
|
|
506
|
-
ACDEgroup((0, 96,
|
|
507
|
-
ACDEgroup((0, 96, 11,
|
|
508
|
-
ACDEgroup((0, 96, 11,
|
|
509
|
-
ACDEgroup((0, 96, 11,
|
|
510
|
-
ACDEgroup((0, 96, 11,
|
|
511
|
-
ACDEgroup((0, 96, 11,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
ABCDEgroup((0, 0, 96, 51,
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
ACDEgroup((0, 96, 11,
|
|
555
|
-
ACDEgroup((0, 96, 11,
|
|
556
|
-
ACDEgroup((0, 96, 11,
|
|
557
|
-
ACDEgroup((0, 96, 11,
|
|
558
|
-
ACDEgroup((0, 96, 11,
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
or (128 <=
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
case ClassID.
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
return 7 #
|
|
607
|
-
case ClassID.
|
|
608
|
-
return
|
|
609
|
-
case (ClassID.
|
|
610
|
-
return
|
|
611
|
-
case ClassID.S_FSK_PHY_MAC_SET_UP,
|
|
612
|
-
return
|
|
613
|
-
case
|
|
614
|
-
return
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
elif isinstance(k,
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
def
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
def
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
if
|
|
873
|
-
return
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
return self.__objs
|
|
886
|
-
|
|
887
|
-
def
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
return
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
rep.
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
rep.
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
rep =
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
rep.
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
)
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
obj:
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
for obj in self.__objs.values()
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
return
|
|
1159
|
-
|
|
1160
|
-
def
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
def obis2obj(self, obis:
|
|
1235
|
-
if obj := self.__objs.get(obis):
|
|
1236
|
-
return result.
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
ass.associated_partners_id
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
el
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
d,
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
continue
|
|
1501
|
-
|
|
1502
|
-
continue
|
|
1503
|
-
|
|
1504
|
-
continue
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
case
|
|
1547
|
-
|
|
1548
|
-
case ut.CosemAttributeDescriptor() | ut.CosemMethodDescriptor():
|
|
1549
|
-
|
|
1550
|
-
case
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
case
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
elif ln.c == 1:
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
elif ln.c ==
|
|
1717
|
-
return media_id.
|
|
1718
|
-
elif ln.c ==
|
|
1719
|
-
return media_id.
|
|
1720
|
-
elif ln.c ==
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
return media_id.
|
|
1725
|
-
elif ln.c ==
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
elif ln.c ==
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
elif ln.c ==
|
|
1752
|
-
return media_id.
|
|
1753
|
-
elif ln.c ==
|
|
1754
|
-
return media_id.
|
|
1755
|
-
elif ln.c ==
|
|
1756
|
-
return media_id.
|
|
1757
|
-
elif ln.c ==
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
elif ln.d ==
|
|
1804
|
-
return media_id.
|
|
1805
|
-
elif ln.d ==
|
|
1806
|
-
return media_id.
|
|
1807
|
-
elif ln.d ==
|
|
1808
|
-
return media_id.
|
|
1809
|
-
elif ln.d ==
|
|
1810
|
-
return media_id.
|
|
1811
|
-
elif ln.d ==
|
|
1812
|
-
return media_id.
|
|
1813
|
-
elif ln.d ==
|
|
1814
|
-
return media_id.
|
|
1815
|
-
elif ln.d ==
|
|
1816
|
-
return media_id.
|
|
1817
|
-
elif ln.d
|
|
1818
|
-
return media_id.
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
return media_id.
|
|
1829
|
-
|
|
1830
|
-
return media_id.
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
return media_id.
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
elif ln.c
|
|
1848
|
-
return media_id.
|
|
1849
|
-
|
|
1850
|
-
return media_id.
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
elif ln.c in (
|
|
1862
|
-
return media_id.
|
|
1863
|
-
elif ln.c in (
|
|
1864
|
-
return media_id.
|
|
1865
|
-
elif ln.c in (
|
|
1866
|
-
return media_id.
|
|
1867
|
-
elif ln.c in (
|
|
1868
|
-
return media_id.
|
|
1869
|
-
elif ln.c in (
|
|
1870
|
-
return media_id.
|
|
1871
|
-
elif ln.c in (
|
|
1872
|
-
return media_id.
|
|
1873
|
-
elif ln.c in (
|
|
1874
|
-
return media_id.
|
|
1875
|
-
elif ln.c in (
|
|
1876
|
-
return media_id.
|
|
1877
|
-
elif ln.c in (
|
|
1878
|
-
return media_id.
|
|
1879
|
-
elif ln.c in (
|
|
1880
|
-
return media_id.
|
|
1881
|
-
elif ln.c in (
|
|
1882
|
-
return media_id.
|
|
1883
|
-
elif ln.c in (
|
|
1884
|
-
return media_id.
|
|
1885
|
-
elif ln.c
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
elif ln.
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
return media_id.
|
|
1913
|
-
elif ln.
|
|
1914
|
-
return media_id.
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
if ln.d
|
|
1941
|
-
return media_id.
|
|
1942
|
-
elif ln.d in (1, 2,
|
|
1943
|
-
return media_id.
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
elif ln.d ==
|
|
1963
|
-
return media_id.
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
elif ln.
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
if ln.d
|
|
2011
|
-
return media_id.
|
|
2012
|
-
elif ln.d
|
|
2013
|
-
return media_id.
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
elif ln.c
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
return media_id.
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
return media_id.
|
|
2040
|
-
elif ln.d
|
|
2041
|
-
return media_id.
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
return media_id.
|
|
2052
|
-
elif ln.d
|
|
2053
|
-
return media_id.
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
try:
|
|
2108
|
-
obj = use_col.get_object(ln)
|
|
2109
|
-
obj_col = col.get_object(ln)
|
|
2110
|
-
except exc.NoObject as e:
|
|
2111
|
-
ret.append(e)
|
|
2112
|
-
print(F"<add_collection> skip obj{self}: {e}")
|
|
2113
|
-
continue
|
|
2114
|
-
for i in indexes:
|
|
2115
|
-
if (attr := obj.get_attr(i)) is not None:
|
|
2116
|
-
try:
|
|
2117
|
-
attr.validate()
|
|
2118
|
-
except ValueError as e:
|
|
2119
|
-
ret.append(ValueError(F"can't decode value {attr} for {ln}:{i}"))
|
|
2120
|
-
else:
|
|
2121
|
-
ret.append(exc.NoObject(F"has't attribute {i} for {ln}"))
|
|
2122
|
-
return ret
|
|
1
|
+
""" The OBIS identification system serves as a basis for the COSEM logical names. The system of naming COSEM objects is defined in the basic
|
|
2
|
+
principles (see Clause 4 EN 62056-62:2007), the identification of real data items is specified in IEC 62056-61. The following clauses define the
|
|
3
|
+
usage of those definitions in the COSEM environment. All codes, which are not explicitly listed, but outside the manufacturer specific range are
|
|
4
|
+
reserved for future use."""
|
|
5
|
+
import logging
|
|
6
|
+
from struct import pack
|
|
7
|
+
import inspect
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from itertools import count, chain
|
|
10
|
+
from functools import reduce, cached_property, lru_cache
|
|
11
|
+
from typing import TypeAlias, Iterator, Self, Callable, Literal, Iterable, Optional, Hashable, Protocol, cast, Annotated
|
|
12
|
+
|
|
13
|
+
from DLMS_SPODES.types.type_alias import attr2d
|
|
14
|
+
from semver import Version as SemVer
|
|
15
|
+
from StructResult import result
|
|
16
|
+
from ..types import common_data_types as cdt, cosem_service_types as cst, useful_types as ut
|
|
17
|
+
from ..types.implementations import structs, enums, octet_string
|
|
18
|
+
from .ln_pattern import LNPattern, LNPatterns
|
|
19
|
+
from .activity_calendar import ActivityCalendar, DayProfileAction
|
|
20
|
+
from .arbitrator import Arbitrator
|
|
21
|
+
from .association_ln import mechanism_id
|
|
22
|
+
from .association_sn.ver0 import AssociationSN as AssociationSNVer0
|
|
23
|
+
from .association_ln.ver0 import AssociationLN as AssociationLNVer0, ObjectListElement
|
|
24
|
+
from .association_ln.ver1 import AssociationLN as AssociationLNVer1
|
|
25
|
+
from .association_ln.ver2 import AssociationLN as AssociationLNVer2
|
|
26
|
+
from .push_setup.ver0 import PushSetup as PushSetupVer0
|
|
27
|
+
from .push_setup.ver1 import PushSetup as PushSetupVer1
|
|
28
|
+
from .push_setup.ver2 import PushSetup as PushSetupVer2
|
|
29
|
+
from .clock import Clock
|
|
30
|
+
from .data import Data
|
|
31
|
+
from .disconnect_control import DisconnectControl
|
|
32
|
+
from .gprs_modem_setup import GPRSModemSetup
|
|
33
|
+
from .gsm_diagnostic.ver0 import GSMDiagnostic as GSMDiagnosticVer0
|
|
34
|
+
from .gsm_diagnostic.ver1 import GSMDiagnostic as GSMDiagnosticVer1
|
|
35
|
+
from .gsm_diagnostic.ver2 import GSMDiagnostic as GSMDiagnosticVer2
|
|
36
|
+
from .iec_hdlc_setup.ver0 import IECHDLCSetup as IECHDLCSetupVer0
|
|
37
|
+
from .iec_hdlc_setup.ver1 import IECHDLCSetup as IECHDLCSetupVer1
|
|
38
|
+
from .image_transfer.ver0 import ImageTransfer
|
|
39
|
+
from .ipv4_setup import IPv4Setup
|
|
40
|
+
from .modem_configuration.ver0 import PSTNModemConfiguration
|
|
41
|
+
from .modem_configuration.ver1 import ModemConfigurationVer1
|
|
42
|
+
from .limiter import Limiter
|
|
43
|
+
from .ntp_setup.ver0 import NTPSetup
|
|
44
|
+
from .profile_generic.ver0 import ProfileGeneric as ProfileGenericVer0
|
|
45
|
+
from .profile_generic.ver1 import ProfileGeneric as ProfileGenericVer1
|
|
46
|
+
from .register import Register
|
|
47
|
+
from .extended_register import ExtendedRegister
|
|
48
|
+
from .demand_register.ver0 import DemandRegister as DemandRegisterVer0
|
|
49
|
+
from .register_activation.ver0 import RegisterActivation
|
|
50
|
+
from .register_monitor import RegisterMonitor
|
|
51
|
+
from .schedule import Schedule
|
|
52
|
+
from .security_setup.ver0 import SecuritySetup as SecuritySetupVer0
|
|
53
|
+
from .security_setup.ver1 import SecuritySetup as SecuritySetupVer1
|
|
54
|
+
from .script_table import ScriptTable
|
|
55
|
+
from .single_action_schedule import SingleActionSchedule
|
|
56
|
+
from .special_days_table import SpecialDaysTable
|
|
57
|
+
from .tcp_udp_setup import TCPUDPSetup
|
|
58
|
+
from .. import exceptions as exc
|
|
59
|
+
from ..relation_to_OBIS import get_name
|
|
60
|
+
from ..cosem_interface_classes import implementations as impl
|
|
61
|
+
from ..cosem_interface_classes.overview import ClassID, CountrySpecificIdentifiers
|
|
62
|
+
from . import obis as o, ln_pattern
|
|
63
|
+
from .. import pdu_enums as pdu
|
|
64
|
+
from ..config_parser import config, get_message
|
|
65
|
+
from ..obis import media_id
|
|
66
|
+
from .parameter import Parameter
|
|
67
|
+
from typing_extensions import deprecated, override
|
|
68
|
+
from .cosem_interface_class import IC, Classifier, _LN_ELEMENT
|
|
69
|
+
from ..settings import settings
|
|
70
|
+
from ..types.type_alias import Obis, Encoding, Attr, Tag, attr2i, attr2obis, attr2a, attr2b, attr2c, attr2e, attr2f, attr2d, Index, unpack_attr
|
|
71
|
+
from .association_ln.abstract import ObjectListType
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class CollectionMapError(exc.DLMSException):
|
|
75
|
+
""""""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
LNContaining: TypeAlias = bytes | str | cst.LogicalName | cdt.Structure | ut.CosemObjectInstanceId | ut.CosemAttributeDescriptor | ut.CosemAttributeDescriptorWithSelection \
|
|
79
|
+
| ut.CosemMethodDescriptor
|
|
80
|
+
|
|
81
|
+
AssociationSN: TypeAlias = AssociationSNVer0
|
|
82
|
+
AssociationLN: TypeAlias = AssociationLNVer0 | AssociationLNVer1 | AssociationLNVer2
|
|
83
|
+
ModemConfiguration: TypeAlias = PSTNModemConfiguration | ModemConfigurationVer1
|
|
84
|
+
SecuritySetup: TypeAlias = SecuritySetupVer0 | SecuritySetupVer1
|
|
85
|
+
PushSetup: TypeAlias = PushSetupVer0 | PushSetupVer1 | PushSetupVer2
|
|
86
|
+
ProfileGeneric: TypeAlias = ProfileGenericVer1
|
|
87
|
+
DemandRegister: TypeAlias = DemandRegisterVer0
|
|
88
|
+
IECHDLCSetup: TypeAlias = IECHDLCSetupVer0 | IECHDLCSetupVer1
|
|
89
|
+
GSMDiagnostic: TypeAlias = GSMDiagnosticVer0 | GSMDiagnosticVer1 | GSMDiagnosticVer2
|
|
90
|
+
InterfaceClass: TypeAlias = Data | Register | ExtendedRegister | DemandRegister | ProfileGeneric | Clock | ScriptTable | Schedule | SpecialDaysTable | ActivityCalendar | \
|
|
91
|
+
SingleActionSchedule | AssociationLN | IECHDLCSetup | DisconnectControl | Limiter | ModemConfiguration | PSTNModemConfiguration | ImageTransfer | \
|
|
92
|
+
GPRSModemSetup | GSMDiagnostic | SecuritySetup | TCPUDPSetup | IPv4Setup | Arbitrator | RegisterMonitor | PushSetup | AssociationSN | \
|
|
93
|
+
NTPSetup
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
type AttributeIndex = int
|
|
97
|
+
UsedAttributes: TypeAlias = dict[cst.LogicalName, set[AttributeIndex]]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
ObjectTreeMode: TypeAlias = Literal["", "m", "g", "c", "mc", "cm", "gm", "gc", "cg", "gmc"]
|
|
101
|
+
SortMode: TypeAlias = Literal["l", "n", "c", "cl", "cn"]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# todo: make new class ClassMap(for field Collection.spec_map). fields: name, version, dict(current version ClassMap)
|
|
105
|
+
class ClassMap:
|
|
106
|
+
|
|
107
|
+
def __init__(self, *values: type[IC]) -> None:
|
|
108
|
+
self._values = values
|
|
109
|
+
|
|
110
|
+
def __hash__(self) -> int:
|
|
111
|
+
return hash(id(it) for it in self._values)
|
|
112
|
+
|
|
113
|
+
def get(self, ver: int) -> type[IC]:
|
|
114
|
+
if (ver := int(ver)) < len(self._values):
|
|
115
|
+
return self._values[ver]
|
|
116
|
+
raise RuntimeError(f"got {ver=}, expected maximal={len(self._values)}")
|
|
117
|
+
|
|
118
|
+
def renew(self, ver: int, cls_: type[IC]) -> Self:
|
|
119
|
+
"""return with one change"""
|
|
120
|
+
if ver < (l := len(self._values)):
|
|
121
|
+
tmp = list(self._values)
|
|
122
|
+
tmp.insert(ver, cls_)
|
|
123
|
+
return self.__class__(*tmp)
|
|
124
|
+
if ver == l:
|
|
125
|
+
return self.__class__(*(self._values + (cls_,)))
|
|
126
|
+
raise RuntimeError(f"got {ver=}, expected maximal={len(self._values)}")
|
|
127
|
+
|
|
128
|
+
def __str__(self) -> str:
|
|
129
|
+
return f"{self._values[0].CLASS_ID}[{len(self._values)}]"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
DataMap = ClassMap(Data)
|
|
133
|
+
DataStaticMap = ClassMap(impl.data.DataStatic)
|
|
134
|
+
DataDynamicMap = ClassMap(impl.data.DataDynamic)
|
|
135
|
+
RegisterMap = ClassMap(Register)
|
|
136
|
+
ExtendedRegisterMap = ClassMap(ExtendedRegister)
|
|
137
|
+
DemandRegisterMap = ClassMap(DemandRegisterVer0)
|
|
138
|
+
RegisterActivationMap = ClassMap(RegisterActivation)
|
|
139
|
+
ProfileGenericMap = ClassMap(ProfileGenericVer0, ProfileGenericVer1)
|
|
140
|
+
ClockMap = ClassMap(Clock)
|
|
141
|
+
ScriptTableMap = ClassMap(ScriptTable)
|
|
142
|
+
ScheduleMap = ClassMap(Schedule)
|
|
143
|
+
SpecialDaysTableMap = ClassMap(SpecialDaysTable)
|
|
144
|
+
AssociationSNMap = ClassMap(AssociationSNVer0)
|
|
145
|
+
AssociationLNMap = ClassMap(AssociationLNVer0, AssociationLNVer1, AssociationLNVer2)
|
|
146
|
+
ImageTransferMap = ClassMap(ImageTransfer)
|
|
147
|
+
ActivityCalendarMap = ClassMap(ActivityCalendar)
|
|
148
|
+
RegisterMonitorMap = ClassMap(RegisterMonitor)
|
|
149
|
+
SingleActionScheduleMap = ClassMap(SingleActionSchedule)
|
|
150
|
+
IECHDLCSetupMap = ClassMap(IECHDLCSetupVer0, IECHDLCSetupVer1)
|
|
151
|
+
ModemConfigurationMap = ClassMap(PSTNModemConfiguration, ModemConfigurationVer1)
|
|
152
|
+
TCPUDPSetupMap = ClassMap(TCPUDPSetup)
|
|
153
|
+
IPv4SetupMap = ClassMap(IPv4Setup)
|
|
154
|
+
GPRSModemSetupMap = ClassMap(GPRSModemSetup)
|
|
155
|
+
GSMDiagnosticMap = ClassMap(GSMDiagnosticVer0, GSMDiagnosticVer1, GSMDiagnosticVer2)
|
|
156
|
+
PushSetupMap = ClassMap(PushSetupVer0, PushSetupVer1, PushSetupVer2)
|
|
157
|
+
SecuritySetupMap = ClassMap(SecuritySetupVer0, SecuritySetupVer1)
|
|
158
|
+
ArbitratorMap = ClassMap(Arbitrator)
|
|
159
|
+
DisconnectControlMap = ClassMap(DisconnectControl)
|
|
160
|
+
LimiterMap = ClassMap(Limiter)
|
|
161
|
+
NTPSetupMap = ClassMap(NTPSetup)
|
|
162
|
+
|
|
163
|
+
# implementation ClassMap
|
|
164
|
+
UnsignedDataMap = ClassMap(impl.data.Unsigned)
|
|
165
|
+
|
|
166
|
+
LN_C: TypeAlias = int
|
|
167
|
+
LN_D: TypeAlias = int
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
common_interface_class_map: dict[int, ClassMap] = {
|
|
171
|
+
1: DataMap,
|
|
172
|
+
3: RegisterMap,
|
|
173
|
+
4: ExtendedRegisterMap,
|
|
174
|
+
5: DemandRegisterMap,
|
|
175
|
+
6: RegisterActivationMap,
|
|
176
|
+
7: ProfileGenericMap,
|
|
177
|
+
8: ClockMap,
|
|
178
|
+
9: ScriptTableMap,
|
|
179
|
+
10: ScheduleMap,
|
|
180
|
+
11: SpecialDaysTableMap,
|
|
181
|
+
15: AssociationLNMap,
|
|
182
|
+
18: ImageTransferMap,
|
|
183
|
+
20: ActivityCalendarMap,
|
|
184
|
+
21: RegisterMonitorMap,
|
|
185
|
+
22: SingleActionScheduleMap,
|
|
186
|
+
23: IECHDLCSetupMap,
|
|
187
|
+
27: ModemConfigurationMap,
|
|
188
|
+
41: TCPUDPSetupMap,
|
|
189
|
+
42: IPv4SetupMap,
|
|
190
|
+
45: GPRSModemSetupMap,
|
|
191
|
+
47: GSMDiagnosticMap,
|
|
192
|
+
64: SecuritySetupMap,
|
|
193
|
+
68: ArbitratorMap,
|
|
194
|
+
70: DisconnectControlMap,
|
|
195
|
+
71: LimiterMap,
|
|
196
|
+
100: NTPSetupMap,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_interface_class(class_map: dict[int, ClassMap], c_id: ut.CosemClassId, ver: int) -> result.SimpleOrError[type[IC]]:
|
|
201
|
+
"""new version <get_type_from_class>"""
|
|
202
|
+
ret = class_map.get(int(c_id), None)
|
|
203
|
+
if isinstance(ret, ClassMap):
|
|
204
|
+
return result.Simple(ret.get(ver))
|
|
205
|
+
if int(c_id) not in common_interface_class_map:
|
|
206
|
+
return result.Error.from_e(ValueError(F"unknown {c_id=}"), "get interface class")
|
|
207
|
+
else:
|
|
208
|
+
return result.Error.from_e(ValueError((F"got {c_id=}, expected {', '.join(map(str, class_map.keys()))}")))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
_CUMULATIVE = (1, 2, 11, 12, 21, 22)
|
|
212
|
+
_MAX_MIN_VALUES = (3, 6, 13, 16, 26, 51, 52, 53, 54)
|
|
213
|
+
_CURRENT_AND_LAST_AVERAGE_VALUES = (0, 4, 5, 14, 15, 24, 25, 27, 28, 49, 50, 55, 56)
|
|
214
|
+
_INSTANTANEOUS_VALUES = (7, 39, 41, 42)
|
|
215
|
+
_TIME_INTEGRAL_VALUES = (8, 9, 10, 17, 18, 19, 20, 29, 30, 58)
|
|
216
|
+
_OCCURRENCE_COUNTER = 40
|
|
217
|
+
_CONTRACTED_VALUES = (46,)
|
|
218
|
+
_UNDER_OVER_LIMIT_THRESHOLDS = (31, 35, 43, 44)
|
|
219
|
+
_UNDER_OVER_LIMIT_OCCURRENCE_COUNTERS = (32, 36)
|
|
220
|
+
_UNDER_OVER_LIMIT_DURATIONS = (33, 37)
|
|
221
|
+
_UNDER_OVER_LIMIT_MAGNITUDES = (34, 38)
|
|
222
|
+
_NOT_PROCESSING_OF_MEASUREMENT_VALUES = tuple(set(range(256)).difference((0, 93, 94, 96, 97, 98, 99))) # BlueBook DLMS UA 1000-1 Ed.14 7.5.2.1 Table 66
|
|
223
|
+
_RU_CHANGE_LIMIT_LEVEL = 134
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@lru_cache()
|
|
227
|
+
def _create_map(maps: ClassMap | tuple[ClassMap]) -> dict[int, ClassMap]:
|
|
228
|
+
if isinstance(maps, tuple):
|
|
229
|
+
return {int(map_.get(0).CLASS_ID): map_ for map_ in maps}
|
|
230
|
+
else:
|
|
231
|
+
return {int(maps.get(0).CLASS_ID): maps}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
A: TypeAlias = int
|
|
235
|
+
B: TypeAlias = int
|
|
236
|
+
C: TypeAlias = int
|
|
237
|
+
D: TypeAlias = int
|
|
238
|
+
E: TypeAlias = int
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class Xgroup(Protocol):
|
|
242
|
+
fmt: str
|
|
243
|
+
|
|
244
|
+
def get_key(self) -> Iterator[bytes]:
|
|
245
|
+
...
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class SimpleGroup(Xgroup, Protocol):
|
|
249
|
+
def get_key(self) -> Iterator[bytes]:
|
|
250
|
+
yield pack(self.fmt, *self) # type: ignore[misc]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class ACgroup(SimpleGroup, tuple[A, C]):
|
|
254
|
+
"""Grouping of OBIS codes by group A (media) and group C (attribute).
|
|
255
|
+
Creates a composite key for objects sharing the same media type and attribute.
|
|
256
|
+
"""
|
|
257
|
+
fmt = ">BB"
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class ACDgroup_(Xgroup, Protocol):
|
|
261
|
+
"""Grouping of OBIS codes by groups A (media), C (attribute), and D (data) with mask support.
|
|
262
|
+
Supports exact values or masks for attribute (C) and data (D) groups.
|
|
263
|
+
Allows flexible grouping by media type with variable attribute and data patterns.
|
|
264
|
+
"""
|
|
265
|
+
fmt: str = ">BBB"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class ACDgroup(SimpleGroup, ACDgroup_, tuple[A, C, D]):
|
|
269
|
+
...
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class ACCDgroup(ACDgroup_, tuple[A, tuple[C, ...], D]):
|
|
273
|
+
def get_key(self) -> Iterator[bytes]:
|
|
274
|
+
a, _, d = self
|
|
275
|
+
return (pack(self.fmt, a, c, d) for c in self[1])
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class ACDDgroup(ACDgroup_, tuple[A, C, tuple[D, ...]]):
|
|
279
|
+
def get_key(self) -> Iterator[bytes]:
|
|
280
|
+
a, c, _ = self
|
|
281
|
+
return (pack(self.fmt, a, c, d) for d in self[2])
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class ACCDDgroup(ACDgroup_, tuple[A, tuple[C, ...], tuple[D, ...]]):
|
|
285
|
+
"""Grouping of OBIS codes by groups A (media), C (attribute), and D (data) with mask support.
|
|
286
|
+
Supports exact values or masks for attribute (C) and data (D) groups.
|
|
287
|
+
Allows flexible grouping by media type with variable attribute and data patterns.
|
|
288
|
+
"""
|
|
289
|
+
def get_key(self) -> Iterator[bytes]:
|
|
290
|
+
a = self[0]
|
|
291
|
+
return (pack(self.fmt, a, c, d) for c in self[1] for d in self[2])
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class ACDEgroup_(Xgroup, Protocol):
|
|
295
|
+
"""Grouping of OBIS codes by groups A (media), C (attribute), D (data), and E (data version) with mask support.
|
|
296
|
+
Groups by fixed media and attribute with support for data and data version masks.
|
|
297
|
+
Enables precise control over data grouping with optional version filtering.
|
|
298
|
+
"""
|
|
299
|
+
fmt: str = ">BBBB"
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class ACDEgroup(SimpleGroup, ACDEgroup_, tuple[A, C, D, E]):
|
|
303
|
+
...
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class ACDDEgroup(ACDEgroup_, tuple[A, C, tuple[D, ...], E]):
|
|
307
|
+
def get_key(self) -> Iterator[bytes]:
|
|
308
|
+
a, c, _, e = self
|
|
309
|
+
return (pack(self.fmt, a, c, d, e) for d in self[2])
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class ACDEEgroup(ACDEgroup_, tuple[A, C, D, tuple[E, ...]]):
|
|
314
|
+
def get_key(self) -> Iterator[bytes]:
|
|
315
|
+
a, c, d, _ = self
|
|
316
|
+
return (pack(self.fmt, a, c, d, e) for e in self[3])
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class ACDDEEgroup(ACDEgroup_, tuple[A, C, tuple[D, ...], tuple[E, ...]]):
|
|
320
|
+
def get_key(self) -> Iterator[bytes]:
|
|
321
|
+
a, c, _, _ = self
|
|
322
|
+
return (pack(self.fmt, a, c, d, e) for d in self[2] for e in self[3])
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class ABCDEgroup_(Xgroup, Protocol):
|
|
326
|
+
"""Grouping of OBIS codes by all OBIS groups A-E with data version mask support.
|
|
327
|
+
|
|
328
|
+
Comprehensive grouping by:
|
|
329
|
+
- A: media
|
|
330
|
+
- B: channel/interface
|
|
331
|
+
- C: attribute
|
|
332
|
+
- D: data
|
|
333
|
+
- E: data version (with mask support)
|
|
334
|
+
|
|
335
|
+
Provides complete OBIS code grouping with flexible version matching.
|
|
336
|
+
"""
|
|
337
|
+
fmt: str = ">BBBBB"
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class ABCDEgroup(SimpleGroup, ABCDEgroup_, tuple[A, B, C, D, E]):
|
|
341
|
+
...
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class ABCCDEgroup(ABCDEgroup_, tuple[A, B, tuple[C, ...], D, E]):
|
|
345
|
+
def get_key(self) -> Iterator[bytes]:
|
|
346
|
+
a, b, _, d, e = self
|
|
347
|
+
return (pack(self.fmt, a, b, c, d, e) for c in self[2])
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class ABCDEEgroup(ABCDEgroup_, tuple[A, B, C, D, tuple[E, ...]]):
|
|
351
|
+
def get_key(self) -> Iterator[bytes]:
|
|
352
|
+
a, b, c, d, _ = self
|
|
353
|
+
return (pack(self.fmt, a, b, c, d, e) for e in self[4])
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
type PossibleGroup = ACgroup | ACDgroup_ | ACDEgroup | ABCDEgroup
|
|
357
|
+
FUNC_MAP: TypeAlias = dict[bytes, dict[int, ClassMap]]
|
|
358
|
+
"""ln.BCDE | ln.CDE | ln.CD | ln.C: {class_id: {version: CosemInterfaceClass}}"""
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
func_maps: dict[str, FUNC_MAP] = {}
|
|
362
|
+
type PossibleClassMap = tuple[ClassMap, ...] | ClassMap
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def get_func_map(for_create_map: dict[PossibleGroup, PossibleClassMap]) -> FUNC_MAP:
|
|
366
|
+
ret: FUNC_MAP = {}
|
|
367
|
+
for g in for_create_map:
|
|
368
|
+
for k in g.get_key():
|
|
369
|
+
ret[k] = _create_map(for_create_map[g])
|
|
370
|
+
return ret
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
__func_map_for_create: dict[PossibleGroup, PossibleClassMap] = {
|
|
374
|
+
# abstract
|
|
375
|
+
ACDgroup((0, 0, 1)): DataMap,
|
|
376
|
+
ACDgroup((0, 0, 2)): DataMap,
|
|
377
|
+
ACDEgroup((0, 0, 2, 1)): ClassMap(impl.data.ActiveFirmwareId),
|
|
378
|
+
ACDgroup((0, 0, 9)): DataMap,
|
|
379
|
+
ACDgroup((0, 1, 0)): ClockMap,
|
|
380
|
+
ACDgroup((0, 1, 1)): DataMap,
|
|
381
|
+
ACDgroup((0, 1, 2)): DataMap,
|
|
382
|
+
ACDgroup((0, 1, 3)): DataMap,
|
|
383
|
+
ACDgroup((0, 1, 4)): DataMap,
|
|
384
|
+
ACDgroup((0, 1, 5)): DataMap,
|
|
385
|
+
ACDgroup((0, 1, 6)): DataMap,
|
|
386
|
+
ACDEgroup((0, 2, 0, 0)): ModemConfigurationMap,
|
|
387
|
+
#
|
|
388
|
+
ACDEEgroup((0, 10, 0, (0, 1, 125)+tuple(range(100, 112)))): ScriptTableMap,
|
|
389
|
+
ACDgroup((0, 11, 0)): SpecialDaysTableMap,
|
|
390
|
+
ACDgroup((0, 12, 0)): ScheduleMap,
|
|
391
|
+
ACDgroup((0, 13, 0)): ActivityCalendarMap,
|
|
392
|
+
ACDgroup((0, 14, 0)): RegisterActivationMap,
|
|
393
|
+
ACDEEgroup((0, 15, 0, tuple(range(0, 8)))): SingleActionScheduleMap,
|
|
394
|
+
ACDgroup((0, 16, 0)): RegisterMonitorMap,
|
|
395
|
+
ACDEEgroup((0, 16, 1, tuple(range(0, 10)))): RegisterMonitorMap,
|
|
396
|
+
#
|
|
397
|
+
ACDgroup((0, 17, 0)): LimiterMap,
|
|
398
|
+
#
|
|
399
|
+
ACDDEEgroup((0, 19, tuple(range(50, 60)), (1, 2))): DataMap,
|
|
400
|
+
#
|
|
401
|
+
ACDgroup((0, 21, 0)): (DataMap, ProfileGenericMap),
|
|
402
|
+
ACDEgroup((0, 22, 0, 0)): IECHDLCSetupMap,
|
|
403
|
+
#
|
|
404
|
+
ACDEgroup((0, 23, 2, 0)): DataMap,
|
|
405
|
+
ACDEEgroup((0, 23, 3, tuple(range(0, 10)))): (DataMap, ProfileGenericMap),
|
|
406
|
+
ACDEEgroup((0, 23, 3, tuple(range(10, 256)))): DataMap,
|
|
407
|
+
#
|
|
408
|
+
ACDgroup((0, 24, 2)): ExtendedRegisterMap,
|
|
409
|
+
ACDgroup((0, 24, 3)): ProfileGenericMap,
|
|
410
|
+
ACDEgroup((0, 24, 4, 0)): DisconnectControlMap,
|
|
411
|
+
ACDEgroup((0, 24, 5, 0)): ProfileGenericMap,
|
|
412
|
+
#
|
|
413
|
+
ACDEgroup((0, 25, 0, 0)): TCPUDPSetupMap,
|
|
414
|
+
ACDEgroup((0, 25, 1, 0)): IPv4SetupMap,
|
|
415
|
+
#
|
|
416
|
+
ACDEgroup((0, 25, 4, 0)): GPRSModemSetupMap,
|
|
417
|
+
#
|
|
418
|
+
ACDEgroup((0, 25, 6, 0)): GSMDiagnosticMap,
|
|
419
|
+
#
|
|
420
|
+
ACDEgroup((0, 25, 9, 0)): PushSetupMap,
|
|
421
|
+
ACDEgroup((0, 25, 10, 0)): NTPSetupMap,
|
|
422
|
+
#
|
|
423
|
+
ABCDEEgroup((0, 0, 40, 0, tuple(range(8)))): (AssociationSNMap, AssociationLNMap), # todo: now limit by 8 association, solve it
|
|
424
|
+
#
|
|
425
|
+
ABCDEgroup((0, 0, 42, 0, 0)): ClassMap(impl.data.LDN),
|
|
426
|
+
ABCDEEgroup((0, 0, 43, 0, tuple(range(256)))): SecuritySetupMap,
|
|
427
|
+
ACDgroup((0, 43, 1)): DataMap,
|
|
428
|
+
#
|
|
429
|
+
ABCDEEgroup((0, 0, 44, 0, tuple(range(256)))): ImageTransferMap,
|
|
430
|
+
#
|
|
431
|
+
ACDEEgroup((0, 96, 1, tuple(range(0, 11)))): ClassMap(impl.data.DLMSDeviceIDObject),
|
|
432
|
+
ACDEgroup((0, 96, 1, 255)): ProfileGenericMap, # todo: add RegisterTable
|
|
433
|
+
ACDgroup((0, 96, 2)): DataDynamicMap,
|
|
434
|
+
ACDEEgroup((0, 96, 3, tuple(range(0, 4)))): DataMap, # todo: add StatusMapping
|
|
435
|
+
ACDEgroup((0, 96, 3, 10)): DisconnectControlMap,
|
|
436
|
+
ACDEEgroup((0, 96, 3, tuple(range(20, 29)))): ArbitratorMap,
|
|
437
|
+
ACDDEgroup((0, 96, (4, 5), 0)): (DataMap, ProfileGenericMap), # todo: add RegisterTable, StatusMapping
|
|
438
|
+
ACDDEEgroup((0, 96, (4, 5), (1, 2, 3, 4))): DataMap, # todo: add StatusMapping
|
|
439
|
+
ACDEEgroup((0, 96, 6, tuple(range(0, 7)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
440
|
+
ACDEEgroup((0, 96, 7, tuple(range(0, 22)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
441
|
+
ACDEEgroup((0, 96, 8, tuple(range(0, 64)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
442
|
+
ACDEEgroup((0, 96, 9, (0, 1, 2))): (RegisterMap, ExtendedRegisterMap),
|
|
443
|
+
ACDEEgroup((0, 96, 10, tuple(range(1, 10)))): DataMap, # todo: add StatusMapping
|
|
444
|
+
ACDEEgroup((0, 96, 11, tuple(range(100)))): (DataDynamicMap, RegisterMap, ExtendedRegisterMap),
|
|
445
|
+
ACDEEgroup((0, 96, 12, (0, 1, 2, 3, 5, 6))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
446
|
+
ACDEgroup((0, 96, 12, 4)): ClassMap(impl.data.CommunicationPortParameter),
|
|
447
|
+
ACDEEgroup((0, 96, 13, (0, 1))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
448
|
+
ACDEEgroup((0, 96, 14, tuple(range(16)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
449
|
+
ACDEEgroup((0, 96, 15, tuple(range(100)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
450
|
+
ACDEEgroup((0, 96, 16, tuple(range(10)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
451
|
+
ACDEEgroup((0, 96, 17, tuple(range(128)))): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
452
|
+
ACDgroup((0, 96, 20)): (DataMap, RegisterMap, ExtendedRegisterMap),
|
|
453
|
+
ACDEEgroup((0, 97, 97, tuple(range(10)))): DataMap,
|
|
454
|
+
ACDDEgroup((0, 97, (97, 98), 255)): ProfileGenericMap, # todo: add RegisterTable
|
|
455
|
+
ACDEEgroup((0, 97, 98, tuple(range(10))+tuple(range(10, 30)))): DataMap,
|
|
456
|
+
ACgroup((0, 98)): ProfileGenericMap,
|
|
457
|
+
ACDgroup((0, 99, 98)): ProfileGenericMap,
|
|
458
|
+
# electricity
|
|
459
|
+
ACDEEgroup((1, 0, 0, tuple(range(10)))): DataMap,
|
|
460
|
+
ACDEgroup((1, 0, 0, 255)): ProfileGenericMap, # todo: add RegisterTable
|
|
461
|
+
ACDgroup((1, 0, 1)): DataMap,
|
|
462
|
+
ACDgroup((1, 0, 2)): DataStaticMap,
|
|
463
|
+
ACDDgroup((1, 0, (3, 4, 7, 8, 9))): (DataStaticMap, RegisterMap, ExtendedRegisterMap),
|
|
464
|
+
ACDDgroup((1, 0, (6, 10))): (RegisterMap, ExtendedRegisterMap),
|
|
465
|
+
ACDEEgroup((1, 0, 11, tuple(range(1, 8)))): DataMap,
|
|
466
|
+
ACDEEgroup((1, 96, 1, tuple(range(10)))): DataMap,
|
|
467
|
+
ACDEgroup((1, 96, 1, 255)): ProfileGenericMap, # todo: add RegisterTable
|
|
468
|
+
ACDEEgroup((1, 96, 5, (0, 1, 2, 3, 4, 5))): DataMap, # todo: add StatusMapping
|
|
469
|
+
ACDEEgroup((1, 96, 10, (0, 1, 2, 3))): DataMap, # todo: add StatusMapping
|
|
470
|
+
ACgroup((1, 98)): ProfileGenericMap,
|
|
471
|
+
ACDDgroup((1, 99, (1, 2, 11, 12, 97, 98, 99))): ProfileGenericMap,
|
|
472
|
+
ACDDEgroup((1, 99, (3, 13, 14), 0)): ProfileGenericMap,
|
|
473
|
+
ACDEEgroup((1, 99, 10, (1, 2, 3))): ProfileGenericMap,
|
|
474
|
+
ACCDgroup((1, _CUMULATIVE, _RU_CHANGE_LIMIT_LEVEL)): RegisterMap,
|
|
475
|
+
ACCDDgroup((
|
|
476
|
+
1,
|
|
477
|
+
_NOT_PROCESSING_OF_MEASUREMENT_VALUES,
|
|
478
|
+
tuple(chain(
|
|
479
|
+
_CUMULATIVE,
|
|
480
|
+
_TIME_INTEGRAL_VALUES,
|
|
481
|
+
_CONTRACTED_VALUES,
|
|
482
|
+
_UNDER_OVER_LIMIT_THRESHOLDS,
|
|
483
|
+
_UNDER_OVER_LIMIT_OCCURRENCE_COUNTERS,
|
|
484
|
+
_UNDER_OVER_LIMIT_DURATIONS,
|
|
485
|
+
_UNDER_OVER_LIMIT_MAGNITUDES
|
|
486
|
+
)))): (RegisterMap, ExtendedRegisterMap),
|
|
487
|
+
ACCDDgroup((1, _NOT_PROCESSING_OF_MEASUREMENT_VALUES, _INSTANTANEOUS_VALUES)): RegisterMap,
|
|
488
|
+
ACCDDgroup((1, _NOT_PROCESSING_OF_MEASUREMENT_VALUES, _MAX_MIN_VALUES)): (RegisterMap, ExtendedRegisterMap, ProfileGenericMap),
|
|
489
|
+
ACCDDgroup((1, _NOT_PROCESSING_OF_MEASUREMENT_VALUES, _CURRENT_AND_LAST_AVERAGE_VALUES)): (RegisterMap, DemandRegisterMap),
|
|
490
|
+
ACCDgroup((1, _NOT_PROCESSING_OF_MEASUREMENT_VALUES, 40)): (DataMap, RegisterMap),
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
func_maps["DLMS_6"] = get_func_map(__func_map_for_create)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
# SPODES3 Update
|
|
497
|
+
__func_map_for_create.update({
|
|
498
|
+
ACDgroup((0, 21, 0)): ProfileGenericMap.renew(1, impl.profile_generic.SPODES3DisplayReadout),
|
|
499
|
+
ACDEEgroup((0, 96, 1, (0, 2, 4, 5, 8, 9, 10))): ClassMap(impl.data.SPODES3IDNotSpecific),
|
|
500
|
+
ACDEgroup((0, 96, 1, 6)): ClassMap(impl.data.SPODES3SPODESVersion),
|
|
501
|
+
ACDEEgroup((0, 96, 2, (1, 2, 3, 5, 6, 7, 11, 12))): ClassMap(impl.data.AnyDateTime),
|
|
502
|
+
ACDEgroup((0, 96, 3, 20)): ClassMap(impl.arbitrator.SPODES3Arbitrator),
|
|
503
|
+
ACDEgroup((0, 96, 4, 3)): ClassMap(impl.data.SPODES3LoadLocker),
|
|
504
|
+
ACDEgroup((0, 96, 5, 1)): ClassMap(impl.data.SPODES3PowerQuality2Event),
|
|
505
|
+
ACDEgroup((0, 96, 5, 4)): ClassMap(impl.data.SPODES3PowerQuality1Event),
|
|
506
|
+
ACDEgroup((0, 96, 5, 132)): ClassMap(impl.data.Unsigned), # TODO: make according with СПОДЭС3 13.9. Контроль чередования фаз
|
|
507
|
+
ACDEgroup((0, 96, 11, 0)): ClassMap(impl.data.SPODES3VoltageEvent),
|
|
508
|
+
ACDEgroup((0, 96, 11, 1)): ClassMap(impl.data.SPODES3CurrentEvent),
|
|
509
|
+
ACDEgroup((0, 96, 11, 2)): ClassMap(impl.data.SPODES3CommutationEvent),
|
|
510
|
+
ACDEgroup((0, 96, 11, 3)): ClassMap(impl.data.SPODES3ProgrammingEvent),
|
|
511
|
+
ACDEgroup((0, 96, 11, 4)): ClassMap(impl.data.SPODES3ExternalEvent),
|
|
512
|
+
ACDEgroup((0, 96, 11, 5)): ClassMap(impl.data.SPODES3CommunicationEvent),
|
|
513
|
+
ACDEgroup((0, 96, 11, 6)): ClassMap(impl.data.SPODES3AccessEvent),
|
|
514
|
+
ACDEgroup((0, 96, 11, 7)): ClassMap(impl.data.SPODES3SelfDiagnosticEvent),
|
|
515
|
+
ACDEgroup((0, 96, 11, 8)): ClassMap(impl.data.SPODES3ReactivePowerEvent),
|
|
516
|
+
ABCDEgroup((0, 0, 96, 51, 0)): ClassMap(impl.data.OpeningBody),
|
|
517
|
+
ABCDEgroup((0, 0, 96, 51, 1)): ClassMap(impl.data.OpeningCover),
|
|
518
|
+
ABCDEgroup((0, 0, 96, 51, 3)): ClassMap(impl.data.ExposureToMagnet),
|
|
519
|
+
ABCDEgroup((0, 0, 96, 51, 4)): ClassMap(impl.data.ExposureToHSField),
|
|
520
|
+
ABCDEgroup((0, 0, 96, 51, 5)): ClassMap(impl.data.SealStatus),
|
|
521
|
+
ABCDEEgroup((0, 0, 96, 51, (6, 7))): UnsignedDataMap,
|
|
522
|
+
ABCDEEgroup((0, 0, 96, 51, (8, 9))): ClassMap(impl.data.OctetStringDateTime),
|
|
523
|
+
ABCDEEgroup((0, 0, 97, 98, (0, 10, 20))): ClassMap(impl.data.SPODES3Alarm1),
|
|
524
|
+
ABCDEEgroup((0, 0, 97, 98, (1, 11))): ClassMap(impl.data.SPODES3ControlAlarm1),
|
|
525
|
+
# electricity
|
|
526
|
+
ACDEEgroup((1, 0, 8, (4, 5))): ClassMap(impl.data.SPODES3MeasurementPeriod),
|
|
527
|
+
ACDgroup((1, 98, 1)): ProfileGenericMap.renew(1, impl.profile_generic.SPODES3MonthProfile),
|
|
528
|
+
ACDgroup((1, 98, 2)): ProfileGenericMap.renew(1, impl.profile_generic.SPODES3DailyProfile),
|
|
529
|
+
ACDDgroup((1, 99, (1, 2))): ProfileGenericMap.renew(1, impl.profile_generic.SPODES3LoadProfile),
|
|
530
|
+
ABCDEgroup((1, 0, 131, 35, 0)): RegisterMap,
|
|
531
|
+
ABCDEgroup((1, 0, 133, 35, 0)): RegisterMap,
|
|
532
|
+
ABCDEgroup((1, 0, 147, 133, 0)): RegisterMap,
|
|
533
|
+
ABCDEgroup((1, 0, 148, 136, 0)): RegisterMap,
|
|
534
|
+
ACDEgroup((1, 94, 7, 0)): ProfileGenericMap.renew(1, impl.profile_generic.SPODES3CurrentProfile),
|
|
535
|
+
ACDEEgroup((1, 94, 7, (1, 2, 3, 4, 5, 6))): ProfileGenericMap.renew(1, impl.profile_generic.SPODES3ScalesProfile), # Todo: RU. Scaler-profile With 1 entry and more
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
func_maps["SPODES_3"] = get_func_map(__func_map_for_create)
|
|
539
|
+
|
|
540
|
+
# KPZ Update
|
|
541
|
+
__func_map_for_create.update({
|
|
542
|
+
ACDEgroup((0, 96, 11, 4)): ClassMap(impl.data.KPZSPODES3ExternalEvent),
|
|
543
|
+
ABCDEEgroup((0, 0, 97, 98, (0, 10, 20))): ClassMap(impl.data.KPZAlarm1),
|
|
544
|
+
ABCDEgroup((0, 128, 25, 6, 0)): ClassMap(impl.data.DataStatic),
|
|
545
|
+
ABCDEEgroup((0, 128, 96, 2, (0, 1, 2))): ClassMap(impl.data.KPZAFEOffsets),
|
|
546
|
+
ABCDEgroup((0, 128, 96, 13, 1)): ClassMap(impl.data.ITEBitMap),
|
|
547
|
+
ABCDEgroup((0, 128, 154, 0, 0)): ClassMap(impl.data.KPZGSMPingIP),
|
|
548
|
+
ACDEEgroup((0, 0, 128, (100, 101, 102, 103, 150, 151, 152, 170))): DataMap,
|
|
549
|
+
ABCCDEgroup((128, 0, tuple(range(20)), 0, 0)): RegisterMap
|
|
550
|
+
})
|
|
551
|
+
func_maps["KPZ"] = get_func_map(__func_map_for_create)
|
|
552
|
+
# KPZ1 with bag in log event profiles
|
|
553
|
+
__func_map_for_create.update({
|
|
554
|
+
ACDEgroup((0, 96, 11, 0)): ClassMap(impl.data.KPZ1SPODES3VoltageEvent),
|
|
555
|
+
ACDEgroup((0, 96, 11, 1)): ClassMap(impl.data.KPZ1SPODES3CurrentEvent),
|
|
556
|
+
ACDEgroup((0, 96, 11, 2)): ClassMap(impl.data.KPZ1SPODES3CommutationEvent),
|
|
557
|
+
ACDEgroup((0, 96, 11, 3)): ClassMap(impl.data.KPZ1SPODES3ProgrammingEvent),
|
|
558
|
+
ACDEgroup((0, 96, 11, 4)): ClassMap(impl.data.KPZ1SPODES3ExternalEvent),
|
|
559
|
+
ACDEgroup((0, 96, 11, 5)): ClassMap(impl.data.KPZ1SPODES3CommunicationEvent),
|
|
560
|
+
ACDEgroup((0, 96, 11, 6)): ClassMap(impl.data.KPZ1SPODES3AccessEvent),
|
|
561
|
+
ACDEgroup((0, 96, 11, 7)): ClassMap(impl.data.KPZ1SPODES3SelfDiagnosticEvent),
|
|
562
|
+
ACDEgroup((0, 96, 11, 8)): ClassMap(impl.data.KPZ1SPODES3ReactivePowerEvent),
|
|
563
|
+
})
|
|
564
|
+
func_maps["KPZ1"] = get_func_map(__func_map_for_create)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def get_type(class_id: ut.CosemClassId,
|
|
568
|
+
ver: int,
|
|
569
|
+
obis: Obis,
|
|
570
|
+
func_map: FUNC_MAP) -> result.SimpleOrError[type[IC]]:
|
|
571
|
+
"""use DLMS UA 1000-1 Ed. 14 Table 54"""
|
|
572
|
+
c_m: Optional[dict[int, ClassMap]]
|
|
573
|
+
if (
|
|
574
|
+
(128 <= attr2b(obis) <= 199)
|
|
575
|
+
or (128 <= attr2c(obis) <= 199)
|
|
576
|
+
or obis[2] == 240
|
|
577
|
+
or (128 <= attr2d(obis) <= 254)
|
|
578
|
+
or (128 <= attr2e(obis) <= 254)
|
|
579
|
+
or (128 <= attr2f(obis) <= 254)
|
|
580
|
+
):
|
|
581
|
+
# try search in ABCDE group for manufacture object before in CDE
|
|
582
|
+
c_m = func_map.get(obis[:5], common_interface_class_map)
|
|
583
|
+
else:
|
|
584
|
+
# try search in ABCDE group
|
|
585
|
+
c_m = func_map.get(obis[:5], None)
|
|
586
|
+
if c_m is None:
|
|
587
|
+
# try search in A-CDE group
|
|
588
|
+
c_m = func_map.get(obis[:1] + obis[2:5], None)
|
|
589
|
+
if c_m is None:
|
|
590
|
+
# try search in A-CD group
|
|
591
|
+
c_m = func_map.get(obis[:1] + obis[2:4], None)
|
|
592
|
+
if c_m is None:
|
|
593
|
+
# try search in A-C group
|
|
594
|
+
c_m = func_map.get(obis[:1] + obis[3:4], common_interface_class_map)
|
|
595
|
+
return get_interface_class(class_map=c_m,
|
|
596
|
+
c_id=class_id,
|
|
597
|
+
ver=ver)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
@lru_cache(20000)
|
|
601
|
+
def get_unit(class_id: ClassID, elements: Iterable[int]) -> Optional[int]:
|
|
602
|
+
match class_id, *elements:
|
|
603
|
+
case (ClassID.LIMITER, 6 | 7) | (ClassID.LIMITER, 8, 2) | (ClassID.DEMAND_REGISTER, 8) | (ClassID.PROFILE_GENERIC, 4) | (ClassID.PUSH_SETUP, 5) |\
|
|
604
|
+
(ClassID.PUSH_SETUP, 7, _) | (ClassID.PUSH_SETUP, 12, 1) | (ClassID.COMMUNICATION_PORT_PROTECTION, 4 | 6) | (ClassID.CHARGE, 8) | (ClassID.IEC_HDLC_SETUP, 8) \
|
|
605
|
+
| (ClassID.AUTO_CONNECT, 4):
|
|
606
|
+
return 7 # second
|
|
607
|
+
case ClassID.CLOCK, 3 | 7:
|
|
608
|
+
return 6 # min
|
|
609
|
+
case (ClassID.IEC_HDLC_SETUP, 7) | (ClassID.MODEM_CONFIGURATION, 3, 2):
|
|
610
|
+
return 7 # millisecond
|
|
611
|
+
case ClassID.S_FSK_PHY_MAC_SET_UP, 7, _:
|
|
612
|
+
return 44 # HZ
|
|
613
|
+
case (ClassID.S_FSK_PHY_MAC_SET_UP, 4 | 5):
|
|
614
|
+
return 72 # Db
|
|
615
|
+
case ClassID.S_FSK_PHY_MAC_SET_UP, 6:
|
|
616
|
+
return 71 # DbmicroV
|
|
617
|
+
case _:
|
|
618
|
+
return None
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
type ObjFilteredKey = tuple[ClassID | LNPattern | LNPatterns | Channel, ...]
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def get_filtered(objects: Iterable[IC],
|
|
625
|
+
keys: ObjFilteredKey) -> list[IC]:
|
|
626
|
+
c_ids: list[ut.CosemClassId] = []
|
|
627
|
+
patterns: list[LNPattern] = []
|
|
628
|
+
ch: Optional[Channel] = None
|
|
629
|
+
for k in keys:
|
|
630
|
+
if isinstance(k, ut.CosemClassId):
|
|
631
|
+
c_ids.append(k)
|
|
632
|
+
elif isinstance(k, LNPattern):
|
|
633
|
+
patterns.append(k)
|
|
634
|
+
elif isinstance(k, LNPatterns):
|
|
635
|
+
patterns.extend(k)
|
|
636
|
+
elif isinstance(k, Channel):
|
|
637
|
+
ch = k
|
|
638
|
+
new_list = []
|
|
639
|
+
for obj in objects:
|
|
640
|
+
if obj.CLASS_ID in c_ids:
|
|
641
|
+
pass
|
|
642
|
+
elif obj.obis in patterns:
|
|
643
|
+
pass
|
|
644
|
+
else:
|
|
645
|
+
continue
|
|
646
|
+
if ch and not ch.is_approve(obj.obis[1]):
|
|
647
|
+
continue
|
|
648
|
+
new_list.append(obj)
|
|
649
|
+
return new_list
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
@deprecated("use <AttrData>")
|
|
653
|
+
@dataclass(unsafe_hash=True, frozen=True)
|
|
654
|
+
class ParameterValue:
|
|
655
|
+
par: bytes
|
|
656
|
+
value: bytes
|
|
657
|
+
|
|
658
|
+
def __str__(self) -> str:
|
|
659
|
+
return F"{'.'.join(map(str, self.par[:6]))}:{self.par[6]} - {cdt.get_instance_and_pdu_from_value(self.value)[0].__repr__()}"
|
|
660
|
+
|
|
661
|
+
def __bytes__(self) -> bytes:
|
|
662
|
+
"""par + 0x00 + value""" # todo: 00 in future other parameters
|
|
663
|
+
return self.par + b'\x00' + self.value
|
|
664
|
+
|
|
665
|
+
@classmethod
|
|
666
|
+
def parse(cls, value: bytes) -> Self:
|
|
667
|
+
if value[7] != 0:
|
|
668
|
+
raise exc.ITEApplication(F"wrong {value!r} for {cls.__name__}")
|
|
669
|
+
return cls(
|
|
670
|
+
par=value[:7],
|
|
671
|
+
value=value[8:]
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@dataclass(unsafe_hash=True, frozen=True)
|
|
676
|
+
class AttrData:
|
|
677
|
+
attr: Attr
|
|
678
|
+
data: Encoding
|
|
679
|
+
|
|
680
|
+
def __str__(self) -> str:
|
|
681
|
+
return f"{".".join(map(str, attr2obis(self.attr)))}:{attr2i(self.attr)} - {cdt.get_instance_and_pdu_from_value(self.data)[0].__repr__()}"
|
|
682
|
+
|
|
683
|
+
def __bytes__(self) -> bytes:
|
|
684
|
+
return self.attr + self.data
|
|
685
|
+
|
|
686
|
+
@classmethod
|
|
687
|
+
def parse(cls, value: bytes) -> Self:
|
|
688
|
+
return cls(value[:7], value[7:])
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
@dataclass(frozen=True, unsafe_hash=True)
|
|
692
|
+
class ID:
|
|
693
|
+
man: bytes
|
|
694
|
+
f_id: AttrData
|
|
695
|
+
f_ver: AttrData
|
|
696
|
+
sap: enums.ClientSAP
|
|
697
|
+
|
|
698
|
+
def __bytes__(self) -> bytes:
|
|
699
|
+
return self.man + self.sap.contents + bytes(self.f_id) + bytes(self.f_ver)
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
class EmptyAttribute(exc.DLMSException):
|
|
703
|
+
"""need read attribute"""
|
|
704
|
+
def __init__(self,
|
|
705
|
+
ln: cst.LogicalName,
|
|
706
|
+
i: int):
|
|
707
|
+
Exception.__init__(self, F"empty {ln}: {i}")
|
|
708
|
+
self.ln = ln
|
|
709
|
+
self.i = i
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
class Collection:
|
|
713
|
+
__id: ID
|
|
714
|
+
__dlms_ver: int
|
|
715
|
+
__country: Optional[CountrySpecificIdentifiers]
|
|
716
|
+
__country_ver: Optional[AttrData]
|
|
717
|
+
__objs: dict[Obis, IC]
|
|
718
|
+
_data: dict[Attr, cdt.CommonDataType]
|
|
719
|
+
_t: dict[Attr, Tag]
|
|
720
|
+
__const_objs: int
|
|
721
|
+
spec_map: str
|
|
722
|
+
|
|
723
|
+
def __init__(self,
|
|
724
|
+
id_: ID,
|
|
725
|
+
dlms_ver: int = 6,
|
|
726
|
+
country: Optional[CountrySpecificIdentifiers] = None,
|
|
727
|
+
cntr_ver: Optional[AttrData] = None):
|
|
728
|
+
self.__id = id_
|
|
729
|
+
self.__dlms_ver = dlms_ver
|
|
730
|
+
self.__country = country
|
|
731
|
+
self.__country_ver = cntr_ver
|
|
732
|
+
"""country version specification"""
|
|
733
|
+
self.spec_map = "DLMS_6"
|
|
734
|
+
self.__objs = {}
|
|
735
|
+
"""all DLMS objects container with obis key"""
|
|
736
|
+
self._data = {}
|
|
737
|
+
"""data of collection"""
|
|
738
|
+
self._t = {}
|
|
739
|
+
"""expected Tags of Attributes"""
|
|
740
|
+
|
|
741
|
+
def getCDT(self, attr: Attr) -> result.SimpleOrError[cdt.CommonDataType]:
|
|
742
|
+
if (data := self._data.get(attr)) is None:
|
|
743
|
+
return result.Error.from_e(ValueError(f"not find data in collection with {attr=}"))
|
|
744
|
+
return result.Simple(data)
|
|
745
|
+
|
|
746
|
+
def get[T: cdt.CommonDataType](self, attr: Attr, e_type: type[T]) -> result.SimpleOrError[T]:
|
|
747
|
+
if (data := self._data.get(attr)) is None:
|
|
748
|
+
return result.Error.from_e(ValueError(f"not find data in collection with {attr=}"))
|
|
749
|
+
if isinstance(data, e_type):
|
|
750
|
+
return result.Simple(data)
|
|
751
|
+
return result.Error.from_e(TypeError(f"got {data.__class__}, expected {e_type}"))
|
|
752
|
+
|
|
753
|
+
@property
|
|
754
|
+
def id(self) -> ID:
|
|
755
|
+
return self.__id
|
|
756
|
+
|
|
757
|
+
def set_id(self, value: ID) -> None:
|
|
758
|
+
if not self.__id:
|
|
759
|
+
self.__id = value
|
|
760
|
+
else:
|
|
761
|
+
if value != self.__id:
|
|
762
|
+
raise ValueError(F"got id: {value}, expected {self.__id}")
|
|
763
|
+
else:
|
|
764
|
+
"""success validation"""
|
|
765
|
+
|
|
766
|
+
def add_from_object_list(self, obj_list: ObjectListType) -> result.StrictOk | result.Error:
|
|
767
|
+
res = result.StrictOk()
|
|
768
|
+
for o_l_el in obj_list:
|
|
769
|
+
o_l_el: structs.ObjectListElement
|
|
770
|
+
if isinstance(res_new := self.addIC(
|
|
771
|
+
class_id=ut.CosemClassId(int(o_l_el.class_id)),
|
|
772
|
+
version=int(o_l_el.version),
|
|
773
|
+
obis=o_l_el.logical_name.contents
|
|
774
|
+
), result.Error):
|
|
775
|
+
res.append_err(res_new.err)
|
|
776
|
+
return res
|
|
777
|
+
|
|
778
|
+
def __eq__(self, other: object) -> bool:
|
|
779
|
+
if isinstance(other, Collection):
|
|
780
|
+
return hash(self) == hash(other)
|
|
781
|
+
raise NotImplementedError
|
|
782
|
+
|
|
783
|
+
def __hash__(self) -> int:
|
|
784
|
+
return hash(self.id)
|
|
785
|
+
|
|
786
|
+
@property
|
|
787
|
+
def dlms_ver(self) -> int:
|
|
788
|
+
return self.__dlms_ver
|
|
789
|
+
|
|
790
|
+
def set_dlms_ver(self, value: int) -> result.Ok | result.Error:
|
|
791
|
+
if not self.__dlms_ver:
|
|
792
|
+
self.__dlms_ver = value
|
|
793
|
+
elif value != self.__dlms_ver:
|
|
794
|
+
return result.Error.from_e(ValueError(F"got dlms_version: {value}, expected {self.__dlms_ver}"))
|
|
795
|
+
return result.OK
|
|
796
|
+
|
|
797
|
+
@property
|
|
798
|
+
def country(self) -> Optional[CountrySpecificIdentifiers]:
|
|
799
|
+
return self.__country
|
|
800
|
+
|
|
801
|
+
def set_country(self, value: CountrySpecificIdentifiers) -> result.Ok | result.Error:
|
|
802
|
+
if not self.__country:
|
|
803
|
+
self.__country = value
|
|
804
|
+
elif value != self.__country:
|
|
805
|
+
return result.Error.from_e(ValueError(F"got country: {value}, expected {self.__country}"))
|
|
806
|
+
return result.OK
|
|
807
|
+
|
|
808
|
+
@property
|
|
809
|
+
def country_ver(self) -> Optional[AttrData]:
|
|
810
|
+
return self.__country_ver
|
|
811
|
+
|
|
812
|
+
def set_country_ver(self, value: AttrData) -> result.Ok | result.Error:
|
|
813
|
+
"""country version specification"""
|
|
814
|
+
if not self.__country_ver:
|
|
815
|
+
self.__country_ver = value
|
|
816
|
+
elif value != self.__country_ver:
|
|
817
|
+
return result.Error.from_e(ValueError(F"got country version: {value}, expected {self.__country_ver}"))
|
|
818
|
+
return result.OK
|
|
819
|
+
|
|
820
|
+
def __str__(self) -> str:
|
|
821
|
+
return F"[{len(self.__objs)}] DLMS version: {self.__dlms_ver}, country: {self.__country}, country specific version: {self.__country_ver}, " \
|
|
822
|
+
F"id: {self.id}, uses specification: {self.spec_map}"
|
|
823
|
+
|
|
824
|
+
def __iter__(self) -> Iterator[IC]:
|
|
825
|
+
return iter(self.__objs.values())
|
|
826
|
+
|
|
827
|
+
def get_spec(self) -> str:
|
|
828
|
+
"""return functional map to specification by identification fields"""
|
|
829
|
+
match self.id.man:
|
|
830
|
+
case b"KPZ":
|
|
831
|
+
return "KPZ"
|
|
832
|
+
case b"101" | b"102" | b"103" | b"104":
|
|
833
|
+
return "KPZ1"
|
|
834
|
+
case _:
|
|
835
|
+
if self.country_ver:
|
|
836
|
+
if self.country == CountrySpecificIdentifiers.RUSSIA:
|
|
837
|
+
if (
|
|
838
|
+
self.country_ver.par == b'\x00\x00`\x01\x06\xff\x02'
|
|
839
|
+
and SemVer.parse(bytes(cdt.OctetString(self.country_ver.value)), True) == SemVer(3, 0)
|
|
840
|
+
):
|
|
841
|
+
return "SPODES_3"
|
|
842
|
+
if self.dlms_ver == 6:
|
|
843
|
+
return "DLMS_6"
|
|
844
|
+
else:
|
|
845
|
+
raise exc.DLMSException("unknown specification")
|
|
846
|
+
|
|
847
|
+
@deprecated("use <addIC>")
|
|
848
|
+
def add_if_missing(self, class_id: ut.CosemClassId,
|
|
849
|
+
version: int,
|
|
850
|
+
obis: Obis) -> IC:
|
|
851
|
+
""" like as add method with check for missing """
|
|
852
|
+
if (res := self.__objs.get(obis)) is None:
|
|
853
|
+
return self.add(
|
|
854
|
+
class_id=class_id,
|
|
855
|
+
version=version,
|
|
856
|
+
obis=obis)
|
|
857
|
+
else:
|
|
858
|
+
return res
|
|
859
|
+
|
|
860
|
+
def __getitem__(self, item: o.OBIS) -> IC:
|
|
861
|
+
return self.__objs[item]
|
|
862
|
+
|
|
863
|
+
def par2obj(self, par: Parameter) -> result.SimpleOrError[IC]:
|
|
864
|
+
"""return: DLMSObject"""
|
|
865
|
+
return self.obis2ic(par.obis)
|
|
866
|
+
|
|
867
|
+
def par2data(self, par: Parameter) -> result.Option[cdt.CommonDataType] | result.Error:
|
|
868
|
+
""":return CDT by Parameter, return None if data wasn't setting"""
|
|
869
|
+
if isinstance((res_obj := self.par2obj(par)), result.Error):
|
|
870
|
+
return res_obj
|
|
871
|
+
res = result.Option(res_obj.value.get_attr(par.i))
|
|
872
|
+
if res.value is None:
|
|
873
|
+
return res
|
|
874
|
+
for i, el in enumerate(par.elements()):
|
|
875
|
+
if isinstance(res.value, cdt.ComplexDataType):
|
|
876
|
+
res.value = res.value[el]
|
|
877
|
+
else:
|
|
878
|
+
return result.Error.from_e(ValueError(f"object with {par} not has element {i}"))
|
|
879
|
+
return res
|
|
880
|
+
|
|
881
|
+
def values(self) -> tuple[IC, ...]:
|
|
882
|
+
return tuple(self.__objs.values())
|
|
883
|
+
|
|
884
|
+
def __len__(self) -> int:
|
|
885
|
+
return len(self.__objs)
|
|
886
|
+
|
|
887
|
+
def setupCDT(self, attr: Attr, encoding: Encoding) -> result.Simple[cdt.CommonDataType] | result.Error:
|
|
888
|
+
if isinstance(res_obj := self.obis2ic(attr2obis(attr)), result.Error):
|
|
889
|
+
return res_obj
|
|
890
|
+
if isinstance(res_data := res_obj.value.getCDT(attr2i(attr), encoding), result.Error):
|
|
891
|
+
return res_data
|
|
892
|
+
return self.setup_data(attr, res_data.value)
|
|
893
|
+
|
|
894
|
+
def setup[T: cdt.CommonDataType](self, attr: Attr, encoding: Encoding, e_type: type[T]) -> result.Simple[T] | result.Error:
|
|
895
|
+
if isinstance(res_obj := self.obis2ic(attr2obis(attr)), result.Error):
|
|
896
|
+
return res_obj
|
|
897
|
+
if isinstance(res_data := res_obj.value.get(attr2i(attr), encoding, e_type), result.Error):
|
|
898
|
+
return res_data
|
|
899
|
+
return self.setup_data(attr, res_data.value)
|
|
900
|
+
|
|
901
|
+
def setup_data[T: cdt.CommonDataType](self, attr: Attr, data: T) -> result.Simple[T] | result.Error:
|
|
902
|
+
if data != self._data.setdefault(attr, data):
|
|
903
|
+
return result.Error.from_e(ValueError(f"collection already exist other <Encoding> for {attr=}"))
|
|
904
|
+
return result.Simple(data)
|
|
905
|
+
|
|
906
|
+
def setupTag(self, attr: Attr, encoding: Tag | Encoding) -> result.Simple[Tag] | result.Error:
|
|
907
|
+
tag = encoding[:1] # use only 1 byte
|
|
908
|
+
obis, i = unpack_attr(attr)
|
|
909
|
+
if isinstance(res_obj := self.obis2ic(obis), result.Error):
|
|
910
|
+
return res_obj
|
|
911
|
+
if isinstance(res_el := res_obj.value.getAElement(i), result.Error):
|
|
912
|
+
return res_el
|
|
913
|
+
if not isinstance(d_t := res_el.value.DATA_TYPE, ut.CHOICE):
|
|
914
|
+
return result.Error.from_e(TypeError(f"for {attr=} can't setup TAG to not CHOICE element"))
|
|
915
|
+
expected = d_t.get_types()
|
|
916
|
+
if not any((t_.TAG == tag for t_ in expected)):
|
|
917
|
+
return result.Error.from_e(ValueError(f"got {tag=}, expected {tuple(t_.TAG for t_ in expected)}"))
|
|
918
|
+
if tag != self._t.setdefault(attr, tag):
|
|
919
|
+
return result.Error.from_e(ValueError(f"collection already exist oter <Tag> for {attr=}"))
|
|
920
|
+
return result.Simple(tag)
|
|
921
|
+
|
|
922
|
+
def addIC(self, class_id: ut.CosemClassId,
|
|
923
|
+
version: Optional[int],
|
|
924
|
+
obis: Obis) -> result.SimpleOrError[IC]:
|
|
925
|
+
""" append new DLMS object to collection with return it"""
|
|
926
|
+
if isinstance(res_ic_type := get_type(
|
|
927
|
+
class_id=class_id,
|
|
928
|
+
ver=self.find_version(class_id) if version is None else version,
|
|
929
|
+
obis=obis,
|
|
930
|
+
func_map=func_maps[self.spec_map]), result.Error):
|
|
931
|
+
return res_ic_type
|
|
932
|
+
new = res_ic_type.value(obis)
|
|
933
|
+
return result.Simple(self.__objs.setdefault(obis, new))
|
|
934
|
+
|
|
935
|
+
def get_class_version(self) -> dict[ut.CosemClassId, cdt.Unsigned]:
|
|
936
|
+
"""use for check all class version by unique"""
|
|
937
|
+
ret: dict[ut.CosemClassId, cdt.Unsigned] = {}
|
|
938
|
+
for obj in self.__objs.values():
|
|
939
|
+
if ver := ret.get(obj.CLASS_ID):
|
|
940
|
+
if obj.VERSION != ver:
|
|
941
|
+
raise ValueError(F"for {obj.CLASS_ID=} exist several versions: {obj.VERSION}, {ver} in one collection")
|
|
942
|
+
else:
|
|
943
|
+
ret[obj.CLASS_ID] = obj.VERSION
|
|
944
|
+
return ret
|
|
945
|
+
|
|
946
|
+
def get_n_phases(self) -> int:
|
|
947
|
+
"""search objects with L2 phase"""
|
|
948
|
+
ret: int | None = None
|
|
949
|
+
for obj in filter(lambda obj: attr2a(obj.obis) == 1, self):
|
|
950
|
+
if 41 <= attr2c(obj.obis) <= 60:
|
|
951
|
+
return 3
|
|
952
|
+
ret = 1
|
|
953
|
+
if ret is None:
|
|
954
|
+
raise exc.NoObject("no one electricity object was find")
|
|
955
|
+
else:
|
|
956
|
+
return ret
|
|
957
|
+
|
|
958
|
+
@lru_cache(maxsize=100) # amount of all ClassID
|
|
959
|
+
def find_version(self, class_id: ut.CosemClassId) -> int:
|
|
960
|
+
"""use for add new object from profile_generic if absence in object list"""
|
|
961
|
+
return next(filter(lambda obj: obj.CLASS_ID == class_id, self.__objs.values())).VERSION
|
|
962
|
+
|
|
963
|
+
def is_in_collection(self, value: LNContaining) -> bool:
|
|
964
|
+
obis = lnContents2obis(value)
|
|
965
|
+
return False if self.__objs.get(obis) is None else True
|
|
966
|
+
|
|
967
|
+
def get_object(self, value: LNContaining) -> IC:
|
|
968
|
+
""" return object from obis<string> or raise exception if it absence """
|
|
969
|
+
return self.obis2ic(lnContents2obis(value)).unwrap()
|
|
970
|
+
|
|
971
|
+
@deprecated("use <par2rep>")
|
|
972
|
+
def get_report(self,
|
|
973
|
+
obj: IC,
|
|
974
|
+
par: bytes,
|
|
975
|
+
a_val: Optional[cdt.CommonDataType]
|
|
976
|
+
) -> cdt.Report:
|
|
977
|
+
"""par: attribute_index, par1, par2, ..."""
|
|
978
|
+
rep = cdt.Report(str(a_val))
|
|
979
|
+
try:
|
|
980
|
+
if a_val is None:
|
|
981
|
+
rep.msg = settings.report.empty
|
|
982
|
+
rep.log = cdt.EMPTY_VAL
|
|
983
|
+
elif isinstance(a_val, cdt.ReportMixin):
|
|
984
|
+
rep = a_val.get_report()
|
|
985
|
+
elif isinstance(a_val, DayProfileAction):
|
|
986
|
+
rep.msg = F"{get_message("$rate$")}-{a_val.script_selector}: {a_val.start_time}"
|
|
987
|
+
if isinstance(script_obj := self.get_object(a_val.script_logical_name), ScriptTable):
|
|
988
|
+
for script in script_obj.scripts:
|
|
989
|
+
if script.script_identifier == a_val.script_selector:
|
|
990
|
+
break
|
|
991
|
+
else:
|
|
992
|
+
rep.log = cdt.Log(logging.ERROR, F"absent script with ID: {a_val.script_selector}")
|
|
993
|
+
else:
|
|
994
|
+
rep.log = cdt.Log(logging.ERROR, f"wrong script object with {a_val.script_logical_name}")
|
|
995
|
+
else:
|
|
996
|
+
if unit := get_unit(obj.CLASS_ID, par):
|
|
997
|
+
rep.unit = cdt.Unit(unit).get_name()
|
|
998
|
+
else:
|
|
999
|
+
if s_u := self.get_scaler_unit(obj, par):
|
|
1000
|
+
rep.msg = (settings.report.scaler_format).format(int(a_val) * 10 ** int(s_u.scaler))
|
|
1001
|
+
rep.unit = s_u.unit.get_name()
|
|
1002
|
+
else:
|
|
1003
|
+
match obj.CLASS_ID, *par:
|
|
1004
|
+
case (ClassID.PROFILE_GENERIC, 3, _) | (ClassID.PROFILE_GENERIC, 6):
|
|
1005
|
+
a_val: structs.CaptureObjectDefinition
|
|
1006
|
+
obj = self.get_object(a_val.logical_name)
|
|
1007
|
+
rep.msg = F"{get_name(a_val.logical_name)}.{obj.get_attr_element(int(a_val.attribute_index))}"
|
|
1008
|
+
case _:
|
|
1009
|
+
pass
|
|
1010
|
+
rep.log = cdt.Log(logging.INFO)
|
|
1011
|
+
except Exception as e:
|
|
1012
|
+
rep.log = cdt.Log(logging.ERROR, e)
|
|
1013
|
+
finally:
|
|
1014
|
+
return rep
|
|
1015
|
+
|
|
1016
|
+
def par2rep(self, par: Parameter, data: Optional[cdt.CommonDataType]) -> cdt.Report:
|
|
1017
|
+
rep = cdt.Report(str(data))
|
|
1018
|
+
try:
|
|
1019
|
+
if data is None:
|
|
1020
|
+
rep.msg = settings.report.empty
|
|
1021
|
+
rep.log = cdt.EMPTY_VAL
|
|
1022
|
+
elif isinstance(data, cdt.ReportMixin):
|
|
1023
|
+
rep = data.get_report()
|
|
1024
|
+
elif isinstance(data, DayProfileAction):
|
|
1025
|
+
rep.msg = F"{get_message("$rate$")}-{data.script_selector}: {data.start_time}"
|
|
1026
|
+
if isinstance(script_obj := self.get_object(data.script_logical_name), ScriptTable):
|
|
1027
|
+
for script in script_obj.scripts:
|
|
1028
|
+
if script.script_identifier == data.script_selector:
|
|
1029
|
+
break
|
|
1030
|
+
else:
|
|
1031
|
+
rep.log = cdt.Log(logging.ERROR, F"absent script with ID: {data.script_selector}")
|
|
1032
|
+
else:
|
|
1033
|
+
rep.log = cdt.Log(logging.ERROR, f"wrong script object with {data.script_logical_name}")
|
|
1034
|
+
else:
|
|
1035
|
+
obj = self.par2obj(par).unwrap()
|
|
1036
|
+
elements = tuple(par.elements())
|
|
1037
|
+
if unit := get_unit(obj.CLASS_ID, elements):
|
|
1038
|
+
rep.unit = cdt.Unit(unit).get_name()
|
|
1039
|
+
else:
|
|
1040
|
+
if s_u := self.par2su(par):
|
|
1041
|
+
rep.msg = (settings.report.scaler_format).format(int(data) * 10 ** int(s_u.scaler))
|
|
1042
|
+
rep.unit = s_u.unit.get_name()
|
|
1043
|
+
else:
|
|
1044
|
+
match obj.CLASS_ID, *elements:
|
|
1045
|
+
case (ClassID.PROFILE_GENERIC, 3, _) | (ClassID.PROFILE_GENERIC, 6):
|
|
1046
|
+
data_: structs.CaptureObjectDefinition
|
|
1047
|
+
obj = self.get_object(data_.logical_name)
|
|
1048
|
+
rep.msg = F"{get_name(data_.logical_name)}.{obj.get_attr_element(int(data_.attribute_index))}"
|
|
1049
|
+
case _:
|
|
1050
|
+
pass
|
|
1051
|
+
rep.log = cdt.Log(logging.INFO)
|
|
1052
|
+
except Exception as e:
|
|
1053
|
+
rep.log = cdt.Log(logging.ERROR, e)
|
|
1054
|
+
finally:
|
|
1055
|
+
return rep
|
|
1056
|
+
|
|
1057
|
+
@deprecated("use par2su")
|
|
1058
|
+
def get_scaler_unit(self,
|
|
1059
|
+
obj: IC,
|
|
1060
|
+
par: bytes
|
|
1061
|
+
) -> cdt.ScalUnitType | None:
|
|
1062
|
+
match obj.CLASS_ID, *par:
|
|
1063
|
+
case (ClassID.REGISTER | ClassID.EXT_REGISTER, 2) | (ClassID.DEMAND_REGISTER, 2 | 3):
|
|
1064
|
+
obj: Register | DemandRegister
|
|
1065
|
+
if (s_u := obj.scaler_unit) is None:
|
|
1066
|
+
raise EmptyAttribute(obj.logical_name, 3)
|
|
1067
|
+
else:
|
|
1068
|
+
if (s := cdt.get_unit_scaler(s_u.unit.contents)) != 0:
|
|
1069
|
+
s_u = copy(s_u)
|
|
1070
|
+
s_u.scaler.set(int(s_u.scaler)-s)
|
|
1071
|
+
return s_u
|
|
1072
|
+
case ClassID.LIMITER, 3 | 4 | 5:
|
|
1073
|
+
obj: Limiter
|
|
1074
|
+
if m_v := obj.monitored_value:
|
|
1075
|
+
return self.get_scaler_unit( # recursion 1 level
|
|
1076
|
+
obj=self.get_object(m_v.logical_name),
|
|
1077
|
+
par=m_v.attribute_index.contents)
|
|
1078
|
+
else:
|
|
1079
|
+
raise EmptyAttribute(obj.logical_name, 2)
|
|
1080
|
+
case ClassID.REGISTER_MONITOR, 2, _:
|
|
1081
|
+
obj: RegisterMonitor
|
|
1082
|
+
if (m_v := obj.monitored_value) is None:
|
|
1083
|
+
raise EmptyAttribute(obj.logical_name, 3)
|
|
1084
|
+
else:
|
|
1085
|
+
return self.get_scaler_unit( # recursion 1 level
|
|
1086
|
+
obj=self.get_object(m_v.logical_name),
|
|
1087
|
+
par=m_v.attribute_index.contents
|
|
1088
|
+
)
|
|
1089
|
+
case _:
|
|
1090
|
+
return None
|
|
1091
|
+
|
|
1092
|
+
@lru_cache(20000)
|
|
1093
|
+
def par2su(self, par: Parameter) -> Optional[cdt.ScalUnitType]:
|
|
1094
|
+
"""convert Parameter -> Optional[ScalerUnit],
|
|
1095
|
+
raise: NoObject, EmptyAttribute"""
|
|
1096
|
+
match (obj := self.obis2ic(par.obis).unwrap()), par.i:
|
|
1097
|
+
case (exc.NoObject, _):
|
|
1098
|
+
raise obj
|
|
1099
|
+
case (ClassID.REGISTER | ClassID.EXT_REGISTER, 2) | (ClassID.DEMAND_REGISTER, 2 | 3):
|
|
1100
|
+
obj: Register | DemandRegister
|
|
1101
|
+
if (s_u := obj.scaler_unit) is None:
|
|
1102
|
+
raise EmptyAttribute(obj.logical_name, 3)
|
|
1103
|
+
else:
|
|
1104
|
+
if (s := cdt.get_unit_scaler(s_u.unit.contents)) != 0:
|
|
1105
|
+
s_u = copy(s_u)
|
|
1106
|
+
s_u.scaler.set(int(s_u.scaler)-s)
|
|
1107
|
+
return s_u
|
|
1108
|
+
case ClassID.LIMITER, 3 | 4 | 5:
|
|
1109
|
+
obj: Limiter
|
|
1110
|
+
if m_v := obj.monitored_value:
|
|
1111
|
+
return self.par2su(Parameter(m_v.logical_name.contents).set_i(int(m_v.attribute_index))) # recursion 1 level
|
|
1112
|
+
else:
|
|
1113
|
+
raise EmptyAttribute(obj.logical_name, 2)
|
|
1114
|
+
case ClassID.REGISTER_MONITOR, 2, _:
|
|
1115
|
+
obj: RegisterMonitor
|
|
1116
|
+
if (m_v := obj.monitored_value) is None:
|
|
1117
|
+
raise EmptyAttribute(obj.logical_name, 3)
|
|
1118
|
+
else:
|
|
1119
|
+
return self.par2su(Parameter(m_v.logical_name.contents).set_i(int(m_v.attribute_index))) # recursion 1 level
|
|
1120
|
+
case _:
|
|
1121
|
+
return None
|
|
1122
|
+
|
|
1123
|
+
def par2float(self, par: Parameter) -> float:
|
|
1124
|
+
"""try convert CDT value according with Parameter to build-in float"""
|
|
1125
|
+
data = self.par2data(par).unwrap()
|
|
1126
|
+
if isinstance(data, cdt.Digital):
|
|
1127
|
+
value = float(int(data))
|
|
1128
|
+
elif isinstance(data, cdt.Float):
|
|
1129
|
+
value = float(data)
|
|
1130
|
+
else:
|
|
1131
|
+
raise TypeError("can't convert Parameter data to int or float")
|
|
1132
|
+
if (su := self.par2su(par)):
|
|
1133
|
+
value *= 10 ** int(su.scaler)
|
|
1134
|
+
return value
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
def filter_by_ass(self, ass_id: int) -> list[IC]:
|
|
1138
|
+
"""return only association objects"""
|
|
1139
|
+
ret = list()
|
|
1140
|
+
for olt in self.getASSOCIATION(ass_id).object_list:
|
|
1141
|
+
ret.append(self.par2obj(Parameter(olt.logical_name.contents)).unwrap())
|
|
1142
|
+
return ret
|
|
1143
|
+
|
|
1144
|
+
def sap2objects(self, sap: enums.ClientSAP) -> result.List[IC]:
|
|
1145
|
+
res = result.List()
|
|
1146
|
+
for par in self.sap2association(sap).iter_pars():
|
|
1147
|
+
if isinstance(res1 := self.par2obj(par), result.Error):
|
|
1148
|
+
res1.append_err(res.err)
|
|
1149
|
+
else:
|
|
1150
|
+
res.append(res1)
|
|
1151
|
+
return res
|
|
1152
|
+
|
|
1153
|
+
def iter_classID_objects(self,
|
|
1154
|
+
class_id: ut.CosemClassId) -> Iterator[IC]:
|
|
1155
|
+
return (obj for obj in self.__objs.values() if obj.CLASS_ID == class_id)
|
|
1156
|
+
|
|
1157
|
+
def iter_objects[T: IC](self, e_type: type[IC]) -> Iterator[IC]:
|
|
1158
|
+
return (obj for obj in self.__objs.values() if isinstance(obj, e_type))
|
|
1159
|
+
|
|
1160
|
+
def LNPattern2objects(self,
|
|
1161
|
+
pat: LNPattern) -> list[InterfaceClass]:
|
|
1162
|
+
ret = []
|
|
1163
|
+
for obj in self.__objs.values():
|
|
1164
|
+
if obj.obis in pat:
|
|
1165
|
+
ret.append(obj)
|
|
1166
|
+
return ret
|
|
1167
|
+
|
|
1168
|
+
def get_first(self, values: list[str | bytes | cst.LogicalName]) -> InterfaceClass:
|
|
1169
|
+
""" return first object from it exist in collection from value"""
|
|
1170
|
+
for val in values:
|
|
1171
|
+
if self.is_in_collection(val):
|
|
1172
|
+
return self.get_object(val)
|
|
1173
|
+
else:
|
|
1174
|
+
"""search next"""
|
|
1175
|
+
else:
|
|
1176
|
+
raise exc.NoObject(F"not found at least one DLMS Objects from collection with {values=}")
|
|
1177
|
+
|
|
1178
|
+
@deprecated("use <iter_classID_objects>")
|
|
1179
|
+
def get_objects_by_class_id(self, value: ut.CosemClassId) -> list[InterfaceClass]:
|
|
1180
|
+
return list(filter(lambda obj: obj.CLASS_ID == value, self.__objs.values()))
|
|
1181
|
+
|
|
1182
|
+
def get_writable_attr(self) -> result.SimpleOrError[UsedAttributes]:
|
|
1183
|
+
"""return all writable {obj.ln: {attribute_index}}"""
|
|
1184
|
+
ret = {}
|
|
1185
|
+
res = result.Simple(ret)
|
|
1186
|
+
for ass in self.iter_classID_objects(ClassID.ASSOCIATION_LN):
|
|
1187
|
+
if attr2e(ass.obis) == 0:
|
|
1188
|
+
"""skip current association"""
|
|
1189
|
+
elif isinstance(res_obj_list := self.get(AssociationLNVer0.object_list, ObjectListType, result.Error)):
|
|
1190
|
+
res.append_e(TypeError(f"got {type(res_obj_list.value)} expected {ObjectListType.__name__}"))
|
|
1191
|
+
else:
|
|
1192
|
+
for list_type in res.value:
|
|
1193
|
+
for attr_access in list_type.access_rights.attribute_access:
|
|
1194
|
+
if attr_access.access_mode.is_writable():
|
|
1195
|
+
if ret.get(list_type.logical_name, None) is None:
|
|
1196
|
+
ret[list_type.logical_name] = set()
|
|
1197
|
+
ret[list_type.logical_name].add(int(attr_access.attribute_id))
|
|
1198
|
+
if (
|
|
1199
|
+
len(ret) == 0
|
|
1200
|
+
and res.err is not None
|
|
1201
|
+
):
|
|
1202
|
+
return res.result
|
|
1203
|
+
return res
|
|
1204
|
+
|
|
1205
|
+
def get_profile_s_u(self,
|
|
1206
|
+
obj: ProfileGeneric,
|
|
1207
|
+
mask: Optional[set[int]] = None
|
|
1208
|
+
) -> list[Optional[cdt.ScalUnitType]]:
|
|
1209
|
+
"""return container of scaler_units if possible, mask: position number in capture_objects"""
|
|
1210
|
+
res: list[cdt.ScalUnitType | None] = []
|
|
1211
|
+
for i, obj_def in enumerate(obj.capture_objects):
|
|
1212
|
+
obj_def: structs.CaptureObjectDefinition
|
|
1213
|
+
if (
|
|
1214
|
+
mask
|
|
1215
|
+
and i not in mask
|
|
1216
|
+
):
|
|
1217
|
+
continue
|
|
1218
|
+
s_u = None
|
|
1219
|
+
try:
|
|
1220
|
+
s_u = self.get_scaler_unit(
|
|
1221
|
+
obj=self.get_object(obj_def.logical_name),
|
|
1222
|
+
par=bytes([int(obj_def.attribute_index)]))
|
|
1223
|
+
except EmptyAttribute as e:
|
|
1224
|
+
print(F"Can't fill Scaler and Unit for {get_name(obj_def.logical_name)}: {e}")
|
|
1225
|
+
finally:
|
|
1226
|
+
res.append(s_u)
|
|
1227
|
+
return res
|
|
1228
|
+
|
|
1229
|
+
def obis2ic(self, obis: Obis) -> result.SimpleOrError[IC]:
|
|
1230
|
+
if obj := self.__objs.get(obis, None):
|
|
1231
|
+
return result.Simple(obj)
|
|
1232
|
+
return result.Error.from_e(ValueError(f"not exist DLMSObject with {obis=}"))
|
|
1233
|
+
|
|
1234
|
+
def obis2obj[T: IC](self, obis: Obis, e_type: T) -> result.SimpleOrError[T]:
|
|
1235
|
+
if obj := self.__objs.get(obis) is None:
|
|
1236
|
+
return result.Error.from_e(ValueError(f"not exist DLMSObject with {obis=}"))
|
|
1237
|
+
if isinstance(obj, e_type):
|
|
1238
|
+
return result.Simple(obj)
|
|
1239
|
+
return result.Error.from_e(TypeError(f"got {obj} expected {e_type}"))
|
|
1240
|
+
|
|
1241
|
+
def logicalName2obj(self, ln: cst.LogicalName) -> result.SimpleOrError[InterfaceClass]:
|
|
1242
|
+
return self.obis2ic(ln.contents)
|
|
1243
|
+
|
|
1244
|
+
@cached_property
|
|
1245
|
+
def LDN(self) -> impl.data.LDN:
|
|
1246
|
+
return self.obis2ic(b"\x00\x00\x2A\x00\x00\xff").unwrap()
|
|
1247
|
+
|
|
1248
|
+
@cached_property
|
|
1249
|
+
def current_association(self) -> AssociationLN:
|
|
1250
|
+
return self.obis2ic(o.CURRENT_ASSOCIATION).unwrap()
|
|
1251
|
+
|
|
1252
|
+
def getASSOCIATION(self, instance: int) -> AssociationLN:
|
|
1253
|
+
return self.obis2obj(bytes((0, 0, 40, 0, instance, 255)), AssociationLN).unwrap()
|
|
1254
|
+
|
|
1255
|
+
@cached_property
|
|
1256
|
+
def PUBLIC_ASSOCIATION(self) -> AssociationLN:
|
|
1257
|
+
return self.obis2ic(bytes(0, 0, 40, 0, 1, 255)).unwrap()
|
|
1258
|
+
|
|
1259
|
+
@property
|
|
1260
|
+
def COMMUNICATION_PORT_PARAMETER(self) -> impl.data.CommunicationPortParameter:
|
|
1261
|
+
return self.obis2ic(bytes((0, 0, 96, 12, 4, 255))).unwrap()
|
|
1262
|
+
|
|
1263
|
+
@property
|
|
1264
|
+
def clock(self) -> Clock:
|
|
1265
|
+
return self.obis2ic(bytes((0, 0, 1, 0, 0, 255))).unwrap()
|
|
1266
|
+
|
|
1267
|
+
@property
|
|
1268
|
+
def boot_image_transfer(self) -> ImageTransfer:
|
|
1269
|
+
return self.obis2ic(bytes((0, 0, 44, 0, 128, 255))).unwrap()
|
|
1270
|
+
|
|
1271
|
+
@property
|
|
1272
|
+
def firmware_image_transfer(self) -> ImageTransfer:
|
|
1273
|
+
return self.obis2ic(bytes((0, 0, 44, 0, 0, 255))).unwrap()
|
|
1274
|
+
|
|
1275
|
+
@property
|
|
1276
|
+
def firmwares_description(self) -> Data:
|
|
1277
|
+
""" Consist from boot_version, descriptor, ex.: 0005PWRM_M2M_3_F1_5ppm_Spvq. 0.0.128.100.0.255 """
|
|
1278
|
+
return self.obis2ic(bytes((0, 0, 128, 100, 0, 255))).unwrap()
|
|
1279
|
+
|
|
1280
|
+
@property
|
|
1281
|
+
def RU_CLOSE_ELECTRIC_SEAL(self) -> Data:
|
|
1282
|
+
""" Russian. СПОДЕС Г.2 """
|
|
1283
|
+
return self.obis2ic(bytes((0, 0, 96, 51, 6, 255))).unwrap()
|
|
1284
|
+
|
|
1285
|
+
@property
|
|
1286
|
+
def RU_ERASE_MAGNETIC_EVENTS(self) -> Data:
|
|
1287
|
+
""" Russian. СПОДЕС Г.2 """
|
|
1288
|
+
return self.obis2ic(bytes((0, 0, 96, 51, 7, 255))).unwrap()
|
|
1289
|
+
|
|
1290
|
+
@property
|
|
1291
|
+
def RU_FILTER_ALARM_2(self) -> Data:
|
|
1292
|
+
""" Russian. Filter of Alarm register relay"""
|
|
1293
|
+
return self.obis2ic(bytes((0, 0, 97, 98, 11, 255))).unwrap()
|
|
1294
|
+
|
|
1295
|
+
@property
|
|
1296
|
+
def RU_DAILY_PROFILE(self) -> ProfileGeneric:
|
|
1297
|
+
""" Russian. Profile of daily values """
|
|
1298
|
+
return self.obis2ic(bytes((1, 0, 98, 2, 0, 255))).unwrap()
|
|
1299
|
+
|
|
1300
|
+
@property
|
|
1301
|
+
def RU_MAXIMUM_CURRENT_EXCESS_LIMIT(self) -> Register:
|
|
1302
|
+
""" RU. СТО 34.01-5.1-006-2021 ver3, 11.1. Maximum current excess limit before the subscriber is disconnected, % of IMAX """
|
|
1303
|
+
return self.obis2ic(bytes((1, 0, 11, 134, 0, 255))).unwrap()
|
|
1304
|
+
|
|
1305
|
+
@property
|
|
1306
|
+
def RU_MAXIMUM_VOLTAGE_EXCESS_LIMIT(self) -> Register:
|
|
1307
|
+
""" RU. СТО 34.01-5.1-006-2021 ver3, 11.1. Maximum voltage excess limit before the subscriber is disconnected, % of Unominal """
|
|
1308
|
+
return self.obis2ic(bytes((1, 0, 12, 134, 0, 255))).unwrap()
|
|
1309
|
+
|
|
1310
|
+
def getDISCONNECT_CONTROL(self, ch: int = 0) -> DisconnectControl:
|
|
1311
|
+
"""DLMS UA 1000-1 Ed 14 6.2.46 Disconnect control objects by channel"""
|
|
1312
|
+
return self.obis2ic(bytes((0, ch, 96, 3, 10, 255))).unwrap()
|
|
1313
|
+
|
|
1314
|
+
def getARBITRATOR(self, ch: int = 0) -> Arbitrator:
|
|
1315
|
+
"""DLMS UA 1000-1 Ed 14 6.2.47 Arbitrator objects objects by channel"""
|
|
1316
|
+
return self.obis2ic(bytes((0, ch, 96, 3, 20, 255))).unwrap()
|
|
1317
|
+
|
|
1318
|
+
@property
|
|
1319
|
+
def boot_version(self) -> str:
|
|
1320
|
+
try:
|
|
1321
|
+
return self.firmwares_description.value.to_str()[:4]
|
|
1322
|
+
except Exception as e:
|
|
1323
|
+
print(e)
|
|
1324
|
+
return 'unknown'
|
|
1325
|
+
|
|
1326
|
+
def get_script_names(self, ln: cst.LogicalName, selector: cdt.LongUnsigned) -> str:
|
|
1327
|
+
"""return name from script by selector"""
|
|
1328
|
+
obj = self.obis2obj(ln.contents, ScriptTable).unwrap()
|
|
1329
|
+
for script in obj.scripts:
|
|
1330
|
+
script: ScriptTable.scripts
|
|
1331
|
+
if script.script_identifier == selector:
|
|
1332
|
+
names: list[str] = []
|
|
1333
|
+
for action in script.actions:
|
|
1334
|
+
action_obj = self.obis2obj(action.logical_name.contents).unwrap()
|
|
1335
|
+
if int(action_obj.CLASS_ID) != int(action.class_id):
|
|
1336
|
+
raise ValueError(F"got {action_obj.CLASS_ID}, expected {action.class_id}")
|
|
1337
|
+
match int(action.service_id):
|
|
1338
|
+
case 1: # for write
|
|
1339
|
+
if isinstance(action.parameter, cdt.NullData):
|
|
1340
|
+
names.append(str(action_obj.getAElement(int(action.index)).unwrap()))
|
|
1341
|
+
else:
|
|
1342
|
+
raise TypeError(F"not support by framework") # TODO: make it
|
|
1343
|
+
case 2: # for execute
|
|
1344
|
+
if isinstance(action.parameter, cdt.NullData):
|
|
1345
|
+
names.append(str(action_obj.get_meth_element(int(action.index))))
|
|
1346
|
+
else:
|
|
1347
|
+
raise TypeError(F"not support by framework") # TODO: make it
|
|
1348
|
+
return ", ".join(names)
|
|
1349
|
+
else:
|
|
1350
|
+
raise ValueError(F"not find {selector} in {obj}")
|
|
1351
|
+
|
|
1352
|
+
@lru_cache(4)
|
|
1353
|
+
def get_association_id(self, client_sap: enums.ClientSAP) -> int:
|
|
1354
|
+
"""return id(association instance) from it client address without current"""
|
|
1355
|
+
for ass in get_filtered(iter(self), (ln_pattern.NON_CURRENT_ASSOCIATION,)):
|
|
1356
|
+
if ass.associated_partners_id.client_SAP == client_sap:
|
|
1357
|
+
return ass.logical_name.e
|
|
1358
|
+
else:
|
|
1359
|
+
continue
|
|
1360
|
+
else:
|
|
1361
|
+
raise ValueError(F"absent association with {client_sap}")
|
|
1362
|
+
|
|
1363
|
+
def sap2association(self, sap: enums.ClientSAP) -> AssociationLN:
|
|
1364
|
+
for ass in self.iter_classID_objects(ClassID.ASSOCIATION_LN):
|
|
1365
|
+
if (
|
|
1366
|
+
ass.associated_partners_id is not None
|
|
1367
|
+
and ass.associated_partners_id.client_SAP == sap
|
|
1368
|
+
):
|
|
1369
|
+
return ass
|
|
1370
|
+
else:
|
|
1371
|
+
continue
|
|
1372
|
+
else:
|
|
1373
|
+
raise exc.NoObject(F"hasn't association with {sap}")
|
|
1374
|
+
|
|
1375
|
+
@lru_cache(maxsize=1000)
|
|
1376
|
+
def is_readable(self, ln: cst.LogicalName,
|
|
1377
|
+
index: int,
|
|
1378
|
+
association_id: int,
|
|
1379
|
+
security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
|
|
1380
|
+
) -> bool:
|
|
1381
|
+
return self.getASSOCIATION(association_id).is_readable(
|
|
1382
|
+
ln=ln,
|
|
1383
|
+
index=index,
|
|
1384
|
+
security_policy=security_policy
|
|
1385
|
+
)
|
|
1386
|
+
|
|
1387
|
+
@lru_cache(maxsize=1000)
|
|
1388
|
+
def is_writable(self, ln: cst.LogicalName,
|
|
1389
|
+
index: int,
|
|
1390
|
+
association_id: int,
|
|
1391
|
+
security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
|
|
1392
|
+
) -> bool:
|
|
1393
|
+
return self.getASSOCIATION(association_id).is_writable(
|
|
1394
|
+
ln=ln,
|
|
1395
|
+
index=index,
|
|
1396
|
+
security_policy=security_policy
|
|
1397
|
+
)
|
|
1398
|
+
|
|
1399
|
+
@lru_cache(maxsize=1000)
|
|
1400
|
+
def isnt_mutable(self,
|
|
1401
|
+
par: Parameter,
|
|
1402
|
+
association_id: int,
|
|
1403
|
+
security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
|
|
1404
|
+
) -> bool:
|
|
1405
|
+
"""is not writable and STATIC data"""
|
|
1406
|
+
if (
|
|
1407
|
+
not self.is_writable(par.ln, par.i, association_id, security_policy, security_policy)
|
|
1408
|
+
and self.par2obj(par
|
|
1409
|
+
).unwrap().getAElement(par.i
|
|
1410
|
+
).unwrap().classifier == Classifier.STATIC
|
|
1411
|
+
):
|
|
1412
|
+
return True
|
|
1413
|
+
return False
|
|
1414
|
+
|
|
1415
|
+
@lru_cache(maxsize=1000)
|
|
1416
|
+
def is_accessible(self, ln: cst.LogicalName,
|
|
1417
|
+
index: int,
|
|
1418
|
+
association_id: int,
|
|
1419
|
+
m_id: mechanism_id.MechanismIdElement = None
|
|
1420
|
+
) -> bool:
|
|
1421
|
+
"""for ver 0 and 1 only"""
|
|
1422
|
+
|
|
1423
|
+
return self.getASSOCIATION(association_id).is_accessible(
|
|
1424
|
+
ln=ln,
|
|
1425
|
+
index=index,
|
|
1426
|
+
m_id=m_id
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1429
|
+
@lru_cache(maxsize=100)
|
|
1430
|
+
def get_name_and_type(self, value: structs.CaptureObjectDefinition) -> tuple[list[str], type[cdt.CommonDataType]]:
|
|
1431
|
+
""" return names and type of element from collection"""
|
|
1432
|
+
names: list[str] = []
|
|
1433
|
+
obj = self.obis2obj(value.logical_name.contents).unwrap()
|
|
1434
|
+
names.append(get_name(obj.logical_name))
|
|
1435
|
+
attr_index = int(value.attribute_index)
|
|
1436
|
+
data_index = int(value.data_index)
|
|
1437
|
+
data_type: type[cdt.CommonDataType] = obj.get_attr_data_type(attr_index)
|
|
1438
|
+
names.append(str(obj.get_attr_element(attr_index)))
|
|
1439
|
+
if data_index == 0:
|
|
1440
|
+
pass
|
|
1441
|
+
elif issubclass(data_type, cdt.Structure):
|
|
1442
|
+
if len(data_type.ELEMENTS) < data_index:
|
|
1443
|
+
raise ValueError(F"can't create buffer_struct_type for {self}, got {data_index=} in struct {data_type.__name__}, expected 1..{len(data_type.ELEMENTS)}")
|
|
1444
|
+
else:
|
|
1445
|
+
el: cdt.StructElement = data_type.ELEMENTS[data_index - 1]
|
|
1446
|
+
names.append(el.NAME)
|
|
1447
|
+
data_type = el.TYPE
|
|
1448
|
+
elif isinstance(obj, ProfileGeneric) and attr_index == 2:
|
|
1449
|
+
"""according to DLMS UA 1000-1 Ed 14. ProfileGeneric.capture_object.data_index annex"""
|
|
1450
|
+
return self.get_name_and_type(obj.capture_objects[data_index - 1]) # todo: is recurse need rewrite here
|
|
1451
|
+
else:
|
|
1452
|
+
pass
|
|
1453
|
+
return names, data_type
|
|
1454
|
+
|
|
1455
|
+
def get_attr_tree(self,
|
|
1456
|
+
ass_id: int,
|
|
1457
|
+
obj_mode: ObjectTreeMode = "c",
|
|
1458
|
+
obj_filter: ObjFilteredKey = None,
|
|
1459
|
+
sort_mode: SortMode = "",
|
|
1460
|
+
af_mode: Literal["l", "r", "w", "lr", "lw", "wr", "lrw", "m", "mlrw", "mlr"] = "l",
|
|
1461
|
+
oi_filter: tuple[tuple[ClassID, tuple[int, ...]], ...] = None # todo: maybe ai_filter with LNPattern, indexes need?
|
|
1462
|
+
) -> dict[ClassID | media_id.MediaId, dict[IC, list[int]]] | dict[IC, list[int]]: # todo: not all ret annotation
|
|
1463
|
+
"""af_mode(attribute filter mode): l-reduce logical_name, r-show only readable, w-show only writeable,
|
|
1464
|
+
oi_filter(object attribute index filter), example: ((ClassID.REGISTER, (2,))) - is restricted for Register only Value attribute without logical_name and scaler_unit
|
|
1465
|
+
"""
|
|
1466
|
+
objects: dict[IC, list[int]]
|
|
1467
|
+
without_ln = True if "l" in af_mode else False
|
|
1468
|
+
only_read = True if "r" in af_mode else False
|
|
1469
|
+
only_write = True if "w" in af_mode else False
|
|
1470
|
+
with_methods = True if "m" in af_mode else False
|
|
1471
|
+
oi_f = dict(oi_filter) if oi_filter else {}
|
|
1472
|
+
"""objects indexes filter"""
|
|
1473
|
+
filtered = self.filter_by_ass(ass_id)
|
|
1474
|
+
if obj_filter:
|
|
1475
|
+
filtered = get_filtered(filtered, obj_filter)
|
|
1476
|
+
ret = get_object_tree(
|
|
1477
|
+
objects=get_sorted(
|
|
1478
|
+
objects=filtered,
|
|
1479
|
+
mode=sort_mode),
|
|
1480
|
+
mode=obj_mode)
|
|
1481
|
+
stack = [(None, None, ret)]
|
|
1482
|
+
while len(stack) != 0:
|
|
1483
|
+
d, k, v = stack.pop()
|
|
1484
|
+
if isinstance(d, dict):
|
|
1485
|
+
for k_, v_ in tuple(d.items()):
|
|
1486
|
+
if len(v_) == 0:
|
|
1487
|
+
d.pop(k_)
|
|
1488
|
+
if isinstance(v, list):
|
|
1489
|
+
objects = dict()
|
|
1490
|
+
for obj in v:
|
|
1491
|
+
obj: IC
|
|
1492
|
+
f_i = oi_f.get(obj.CLASS_ID)
|
|
1493
|
+
"""filter indexes"""
|
|
1494
|
+
indexes = list()
|
|
1495
|
+
elements = obj.A_ELEMENTS
|
|
1496
|
+
if not without_ln:
|
|
1497
|
+
elements = _LN_ELEMENT + elements
|
|
1498
|
+
for i, attr in elements:
|
|
1499
|
+
if only_read and not self.is_readable(obj.logical_name, i, ass_id):
|
|
1500
|
+
continue
|
|
1501
|
+
if only_write and not self.is_writable(obj.logical_name, i, ass_id):
|
|
1502
|
+
continue
|
|
1503
|
+
if f_i and i not in f_i:
|
|
1504
|
+
continue
|
|
1505
|
+
indexes.append(i)
|
|
1506
|
+
if with_methods:
|
|
1507
|
+
i_meth = count(1)
|
|
1508
|
+
for i, m_el in zip(i_meth, obj.M_ELEMENTS):
|
|
1509
|
+
try:
|
|
1510
|
+
if not self.is_accessible(obj.logical_name, i, ass_id, mechanism_id.LOW):
|
|
1511
|
+
continue
|
|
1512
|
+
elif f_i and -i not in f_i:
|
|
1513
|
+
continue
|
|
1514
|
+
indexes.append(-i)
|
|
1515
|
+
except exc.ITEApplication as e:
|
|
1516
|
+
print(F"skip {i}... methods for {obj}: {e}")
|
|
1517
|
+
break
|
|
1518
|
+
if len(indexes) != 0:
|
|
1519
|
+
objects[obj] = indexes
|
|
1520
|
+
if d is None:
|
|
1521
|
+
return objects
|
|
1522
|
+
if len(objects) != 0:
|
|
1523
|
+
d[k] = objects
|
|
1524
|
+
else:
|
|
1525
|
+
d.pop(k)
|
|
1526
|
+
elif isinstance(v, dict):
|
|
1527
|
+
for k_, v_ in v.items():
|
|
1528
|
+
stack.append((v, k_, v_))
|
|
1529
|
+
else:
|
|
1530
|
+
raise ValueError('not support')
|
|
1531
|
+
return ret
|
|
1532
|
+
|
|
1533
|
+
|
|
1534
|
+
if config is not None:
|
|
1535
|
+
try:
|
|
1536
|
+
__collection_path = settings.collection.path
|
|
1537
|
+
except KeyError as e:
|
|
1538
|
+
raise exc.TomlKeyError(F"not find {e} in [DLMS.collection]<path>")
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+
def lnContents2obis(value: LNContaining) -> Obis:
|
|
1542
|
+
"""return LN as OBIS for use in any searching"""
|
|
1543
|
+
match value:
|
|
1544
|
+
case bytes():
|
|
1545
|
+
return value
|
|
1546
|
+
case cst.LogicalName() | ut.CosemObjectInstanceId():
|
|
1547
|
+
return value.contents
|
|
1548
|
+
case ut.CosemAttributeDescriptor() | ut.CosemMethodDescriptor():
|
|
1549
|
+
return value.instance_id.contents
|
|
1550
|
+
case ut.CosemAttributeDescriptorWithSelection():
|
|
1551
|
+
return value.cosem_attribute_descriptor.instance_id.contents
|
|
1552
|
+
case cdt.Structure(logical_name=value.logical_name):
|
|
1553
|
+
return value.logical_name.contents
|
|
1554
|
+
case cdt.Structure() as s:
|
|
1555
|
+
s: cdt.Structure
|
|
1556
|
+
for it in s:
|
|
1557
|
+
if isinstance(it, cst.LogicalName):
|
|
1558
|
+
return o.OBIS(it.contents)
|
|
1559
|
+
raise ValueError(F"can't convert {value=} to Logical Name contents. Struct {s} not content the Logical Name")
|
|
1560
|
+
case str() if value.find('.') != -1:
|
|
1561
|
+
return cst.LogicalName.from_obis(value).contents
|
|
1562
|
+
case str():
|
|
1563
|
+
return cst.LogicalName(value).contents
|
|
1564
|
+
case _: raise ValueError(F"can't convert {value=} to Logical Name contents")
|
|
1565
|
+
|
|
1566
|
+
|
|
1567
|
+
class AttrDesc:
|
|
1568
|
+
"""keep constant descriptors # todo: make better"""
|
|
1569
|
+
OBJECT_LIST = ut.CosemAttributeDescriptor((ClassID.ASSOCIATION_LN, ut.CosemObjectInstanceId("0.0.40.0.0.255"), ut.CosemObjectAttributeId(2)))
|
|
1570
|
+
LDN_VALUE = ut.CosemAttributeDescriptor((ClassID.DATA, ut.CosemObjectInstanceId("0.0.42.0.0.255"), ut.CosemObjectAttributeId(2)))
|
|
1571
|
+
SPODES_VERSION = ut.CosemAttributeDescriptor((ClassID.DATA, ut.CosemObjectInstanceId("0.0.96.1.6.255"), ut.CosemObjectAttributeId(2)))
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
__range10_and_255: tuple[int] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255
|
|
1575
|
+
__range63: tuple[int] = tuple(range(0, 64))
|
|
1576
|
+
__range120_and_124_127: tuple[int] = tuple(chain(range(0, 121), range(124, 128)))
|
|
1577
|
+
__range100_and_255: tuple[int] = tuple(chain(range(0, 100), (255,)))
|
|
1578
|
+
__range100_and_101_125_and_255: tuple[int] = tuple(chain(__range100_and_255, range(101, 126)))
|
|
1579
|
+
__c1: tuple[int] = tuple(chain(range(1, 10), (13, 14, 33, 34, 53, 54, 73, 74, 82), range(16, 31), range(36, 51), range(56, 70), range(76, 81), range(84, 90)))
|
|
1580
|
+
"""DLMS UA 1000-1 Ed 14. Table 45 c1"""
|
|
1581
|
+
__table44: tuple[int] = (11, 12, 15, 31, 32, 35, 51, 52, 55, 71, 72, 75, 90, 91, 92)
|
|
1582
|
+
"""DLMS UA 1000-1 Ed 14. Table 44 for active power"""
|
|
1583
|
+
__c2: tuple[int] = tuple(chain(__table44, range(100, 108)))
|
|
1584
|
+
"""DLMS UA 1000-1 Ed 14. Table 45 c2"""
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
def get_media_id(ln: cst.LogicalName) -> media_id.MediaId:
|
|
1588
|
+
return media_id.MediaId.from_int(ln.a)
|
|
1589
|
+
|
|
1590
|
+
|
|
1591
|
+
def get_class_id(obj: InterfaceClass) -> ClassID:
|
|
1592
|
+
return obj.CLASS_ID
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
def get_map_by_obj(objects: list[InterfaceClass] | tuple[InterfaceClass], key: Callable[[InterfaceClass], ...]) -> dict[media_id.MediaId, list[InterfaceClass]]:
|
|
1596
|
+
ret = dict()
|
|
1597
|
+
for obj in objects:
|
|
1598
|
+
if ret.get(new_key := key(obj)):
|
|
1599
|
+
ret[new_key].append(obj)
|
|
1600
|
+
else:
|
|
1601
|
+
ret[new_key] = [obj]
|
|
1602
|
+
return ret
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
def get_object_tree(objects: list[IC] | tuple[IC],
|
|
1606
|
+
mode: ObjectTreeMode) -> dict[media_id.MediaId | ClassID, list[IC]]:
|
|
1607
|
+
"""mode: m-media_id, g-relation_group, c-class_id"""
|
|
1608
|
+
mode = list(mode)
|
|
1609
|
+
ret = objects
|
|
1610
|
+
while mode:
|
|
1611
|
+
match mode.pop():
|
|
1612
|
+
case "m":
|
|
1613
|
+
key = lambda obj: get_media_id(obj.logical_name)
|
|
1614
|
+
case "g":
|
|
1615
|
+
key = lambda obj: get_relation_group(obj.logical_name)
|
|
1616
|
+
case "c":
|
|
1617
|
+
key = get_class_id
|
|
1618
|
+
case _:
|
|
1619
|
+
raise KeyError(F"got unknown {mode=} for get_map")
|
|
1620
|
+
stack = [(None, None, ret)]
|
|
1621
|
+
while len(stack) != 0:
|
|
1622
|
+
d, k, v = stack.pop()
|
|
1623
|
+
if isinstance(v, list):
|
|
1624
|
+
res = get_map_by_obj(v, key)
|
|
1625
|
+
if d is None:
|
|
1626
|
+
ret = res
|
|
1627
|
+
else:
|
|
1628
|
+
d[k] = res
|
|
1629
|
+
elif isinstance(v, dict):
|
|
1630
|
+
stack.extend(((v, k_, v_) for k_, v_ in v.items()))
|
|
1631
|
+
else:
|
|
1632
|
+
raise ValueError('not support')
|
|
1633
|
+
return ret
|
|
1634
|
+
|
|
1635
|
+
|
|
1636
|
+
def get_sorted(objects: list[IC],
|
|
1637
|
+
mode: SortMode) -> list[IC]:
|
|
1638
|
+
"""mode: l-logical_name, n-name, c-class_id
|
|
1639
|
+
"""
|
|
1640
|
+
mode: list[str] = list(mode)
|
|
1641
|
+
while mode:
|
|
1642
|
+
match mode.pop():
|
|
1643
|
+
case "l":
|
|
1644
|
+
key = None
|
|
1645
|
+
case "n":
|
|
1646
|
+
key = lambda obj: get_name(obj.logical_name)
|
|
1647
|
+
case "c":
|
|
1648
|
+
key = lambda obj: obj.CLASS_ID
|
|
1649
|
+
case _:
|
|
1650
|
+
raise KeyError(F"got unknown {mode=} for get_map")
|
|
1651
|
+
objects = sorted(objects, key=key)
|
|
1652
|
+
return objects
|
|
1653
|
+
|
|
1654
|
+
@dataclass
|
|
1655
|
+
class Channel:
|
|
1656
|
+
"""for object filter approve"""
|
|
1657
|
+
n: int
|
|
1658
|
+
|
|
1659
|
+
def __post_init__(self) -> None:
|
|
1660
|
+
if not self.is_channel(self.n):
|
|
1661
|
+
raise ValueError(F"got value={self.n}, expected (0..64)")
|
|
1662
|
+
|
|
1663
|
+
@staticmethod
|
|
1664
|
+
def is_channel(b: int) -> bool:
|
|
1665
|
+
return True if 0 <= b <= 64 else False
|
|
1666
|
+
|
|
1667
|
+
def is_approve(self, b: int) -> bool:
|
|
1668
|
+
if self.is_channel(b):
|
|
1669
|
+
return True if b == self.n else False
|
|
1670
|
+
else:
|
|
1671
|
+
return True
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
RelationGroup: TypeAlias = media_id.Abstract | media_id.Electricity | media_id.Hca | media_id.Gas | media_id.Thermal | media_id.Water | media_id.Other
|
|
1675
|
+
RelationGroups: tuple[media_id.MediaId, ...] = (media_id.ABSTRACT, media_id.ELECTRICITY, media_id.HCA, media_id.GAS, media_id.THERMAL, media_id.WATER)
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
@lru_cache(maxsize=1000)
|
|
1679
|
+
def get_relation_group(ln: cst.LogicalName) -> RelationGroup:
|
|
1680
|
+
if ln.a == media_id.ABSTRACT:
|
|
1681
|
+
if ln.c == 0:
|
|
1682
|
+
if ln.d == 1:
|
|
1683
|
+
return media_id.BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES
|
|
1684
|
+
elif ln.d in (2, 9):
|
|
1685
|
+
return media_id.OTHER_ABSTRACT_GENERAL_PURPOSE_OBIS_CODES
|
|
1686
|
+
elif ln.c == 1:
|
|
1687
|
+
if ln.d in (0, 1, 2, 3, 4, 5, 6):
|
|
1688
|
+
return media_id.CLOCK_OBJECTS
|
|
1689
|
+
elif ln.c == 2:
|
|
1690
|
+
if ln.d in (0, 1, 2):
|
|
1691
|
+
return media_id.MODEM_CONFIGURATION_AND_RELATED_OBJECTS
|
|
1692
|
+
elif ln.c == 10 and ln.d == 0 and ln.e in (0, 1, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 125):
|
|
1693
|
+
return media_id.SCRIPT_TABLE_OBJECTS
|
|
1694
|
+
elif ln.c == 11 and ln.d == 0:
|
|
1695
|
+
return media_id.SPECIAL_DAYS_TABLE_OBJECTS
|
|
1696
|
+
elif ln.c == 12 and ln.d == 0:
|
|
1697
|
+
return media_id.SCHEDULE_OBJECTS
|
|
1698
|
+
elif ln.c == 13 and ln.d == 0:
|
|
1699
|
+
return media_id.ACTIVITY_CALENDAR_OBJECTS
|
|
1700
|
+
elif ln.c == 14 and ln.d == 0:
|
|
1701
|
+
return media_id.REGISTER_ACTIVATION_OBJECTS
|
|
1702
|
+
elif ln.c == 15 and ln.d == 0 and ln.e in (0, 1, 2, 3, 4, 5, 6, 7):
|
|
1703
|
+
return media_id.SINGLE_ACTION_SCHEDULE_OBJECTS
|
|
1704
|
+
elif ln.c == 16:
|
|
1705
|
+
if ln.d == 0 or (ln.d == 1 and ln.e in range(0, 10)):
|
|
1706
|
+
return media_id.REGISTER_OBJECTS_MONITOR
|
|
1707
|
+
elif ln.d == 2:
|
|
1708
|
+
return media_id.PARAMETER_MONITOR_OBJECTS
|
|
1709
|
+
elif ln.c == 17 and ln.d == 0:
|
|
1710
|
+
return media_id.LIMITER_OBJECTS
|
|
1711
|
+
elif ln.c == 18 and ln.d == 0:
|
|
1712
|
+
return media_id.ARRAY_MANAGER_OBJECT
|
|
1713
|
+
elif ln.c == 19:
|
|
1714
|
+
if (ln.d in range(0, 10) and ln.e == 0) or ln.d in range(10, 50) or ln.d in (range(50, 60) and ln.e in (1, 2)):
|
|
1715
|
+
return media_id.PAYMENT_METERING_RELATED_OBJECTS
|
|
1716
|
+
elif ln.c == 20 and ln.d == 0 and ln.e in (0, 1):
|
|
1717
|
+
return media_id.IEC_LOCAL_PORT_SETUP_OBJECTS
|
|
1718
|
+
elif ln.c == 21 and ln.d == 0:
|
|
1719
|
+
return media_id.STANDARD_READOUT_PROFILE_OBJECTS
|
|
1720
|
+
elif ln.c == 22 and ln.d == 0 and ln.e == 0:
|
|
1721
|
+
return media_id.IEC_HDLC_SETUP_OBJECTS
|
|
1722
|
+
elif ln.c == 23:
|
|
1723
|
+
if (ln.d in 0, 1, 2 and ln.e == 0) or ln.d == 3:
|
|
1724
|
+
return media_id.IEC_TWISTED_PAIR_1_SETUP_OBJECTS
|
|
1725
|
+
elif ln.c == 24:
|
|
1726
|
+
if (ln.d in (0, 1, 4, 5, 6) and ln.e == 0) or (ln.d in (2, 8, 9)):
|
|
1727
|
+
return media_id.OBJECTS_RELATED_TO_DATA_EXCHANGE_OVER_M_BUS
|
|
1728
|
+
elif ln.c == 31 and ln.d == 0 and ln.e == 0:
|
|
1729
|
+
return media_id.OBJECTS_RELATED_TO_DATA_EXCHANGE_OVER_M_BUS
|
|
1730
|
+
elif ln.c == 25:
|
|
1731
|
+
if ln.d in (0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15) and ln.e == 0:
|
|
1732
|
+
return media_id.OBJECTS_TO_SET_UP_DATA_EXCHANGE_OVER_THE_INTERNET
|
|
1733
|
+
elif ln.d == 9 and ln.e == 0:
|
|
1734
|
+
return media_id.OBJECTS_TO_SET_UP_PUSH_SETUP
|
|
1735
|
+
elif ln.c == 26 and ln.d in (0, 1, 2, 3, 5, 6) and ln.e == 0:
|
|
1736
|
+
return media_id.OBJECTS_FOR_SETTING_UP_DATA_EXCHANGE_USING_S_FSK_PLC
|
|
1737
|
+
elif ln.c == 27 and ln.d in (0, 1, 2) and ln.e == 0:
|
|
1738
|
+
return media_id.OBJECTS_FOR_SETTING_UP_THE_ISO_IEC_8802_2_LLC_LAYER
|
|
1739
|
+
elif ln.c == 28 and ln.d in (0, 1, 2, 3, 4, 5, 6, 7) and ln.e == 0:
|
|
1740
|
+
return media_id.OBJECTS_FOR_DATA_EXCHANGE_USING_NARROWBAND_OFDM_PLC_FOR_PRIME_NETWORKS
|
|
1741
|
+
elif ln.c == 29 and ln.d in (0, 1, 2) and ln.e == 0:
|
|
1742
|
+
return media_id.OBJECTS_FOR_DATA_EXCHANGE_USING_NARROW_BAND_OFDM_PLC_FOR_G3_PLC_NETWORKS
|
|
1743
|
+
elif ln.c == 30 and ln.d in (0, 1, 2, 3, 4):
|
|
1744
|
+
return media_id.ZIGBEE_SETUP_OBJECTS
|
|
1745
|
+
elif ln.c == 32 and ln.d in (0, 1, 2, 3) and ln.e == 0:
|
|
1746
|
+
return media_id.OBJECTS_FOR_SETTING_UP_AND_MANAGING_DATA_EXCHANGE_USING_ISO_IEC_14908_PLC_NETWORKS
|
|
1747
|
+
elif ln.c == 33 and ln.d in (0, 1, 2, 3) and ln.e == 0:
|
|
1748
|
+
return media_id.OBJECTS_FOR_DATA_EXCHANGE_USING_HS_PLC_ISO_IEC_12139_1_ISO_EC_12139_1_NETWORKS
|
|
1749
|
+
elif ln.c == 34 and ln.d in (0, 1, 2, 3) and ln.e == 0:
|
|
1750
|
+
return media_id.OBJECTS_FOR_DATA_EXCHANGE_USING_WI_SUN_NETWORKS
|
|
1751
|
+
elif ln.c == 40 and ln.b == 0 and ln.d == 0:
|
|
1752
|
+
return media_id.ASSOCIATION_OBJECTS
|
|
1753
|
+
elif ln.c == 41 and ln.b == 0 and ln.d == 0 and ln.e == 0:
|
|
1754
|
+
return media_id.SAP_ASSIGNMENT_OBJECT
|
|
1755
|
+
elif ln.c == 42 and ln.b == 0 and ln.d == 0 and ln.e == 0:
|
|
1756
|
+
return media_id.COSEM_LOGICAL_DEVICE_NAME_OBJECT
|
|
1757
|
+
elif ln.c == 43:
|
|
1758
|
+
if (ln.b == 0 and ln.d == 0) or ln.d in (1, 2):
|
|
1759
|
+
return media_id.INFORMATION_SECURITY_RELATED_OBJECTS
|
|
1760
|
+
elif ln.c == 44:
|
|
1761
|
+
if ln.b == 0:
|
|
1762
|
+
if ln.d == 0:
|
|
1763
|
+
return media_id.IMAGE_TRANSFER_OBJECTS
|
|
1764
|
+
elif ln.d == 1:
|
|
1765
|
+
return media_id.FUNCTION_CONTROL_OBJECTS
|
|
1766
|
+
elif ln.d == 2:
|
|
1767
|
+
return media_id.COMMUNICATION_PORT_PROTECTION_OBJECTS
|
|
1768
|
+
elif ln.c == 65 and ln.d in __range63:
|
|
1769
|
+
return media_id.UTILITY_TABLE_OBJECTS
|
|
1770
|
+
elif ln.c == 66 and ln.d == 0:
|
|
1771
|
+
return media_id.COMPACT_DATA_OBJECTS
|
|
1772
|
+
elif ln.c == 96:
|
|
1773
|
+
if ln.d == 1:
|
|
1774
|
+
if ln.e in __range10_and_255:
|
|
1775
|
+
return media_id.DEVICE_ID_OBJECTS
|
|
1776
|
+
elif ln.e == 10:
|
|
1777
|
+
return media_id.METERING_POINT_ID_OBJECTS
|
|
1778
|
+
elif ln.d == 2:
|
|
1779
|
+
return media_id.PARAMETER_CHANGES_AND_CALIBRATION_OBJECTS
|
|
1780
|
+
elif ln.d == 3:
|
|
1781
|
+
if ln.e in (0, 1, 2, 3, 4):
|
|
1782
|
+
return media_id.I_O_CONTROL_SIGNAL_OBJECTS
|
|
1783
|
+
elif ln.e == 10:
|
|
1784
|
+
return media_id.DISCONNECT_CONTROL_OBJECTS
|
|
1785
|
+
elif ln.e in range(20, 30):
|
|
1786
|
+
return media_id.ARBITRATOR_OBJECTS
|
|
1787
|
+
elif ln.d == 4 and ln.e in (0, 1, 2, 3, 4):
|
|
1788
|
+
return media_id.STATUS_OF_INTERNAL_CONTROL_SIGNALS_OBJECTS
|
|
1789
|
+
elif ln.d == 5 and ln.e in (0, 1, 2, 3, 4):
|
|
1790
|
+
return media_id.INTERNAL_OPERATING_STATUS_OBJECTS
|
|
1791
|
+
elif ln.d == 6 and ln.e in (0, 1, 2, 3, 4, 5, 6):
|
|
1792
|
+
return media_id.BATTERY_ENTRIES_OBJECTS
|
|
1793
|
+
elif ln.d == 7 and ln.e in range(0, 22):
|
|
1794
|
+
return media_id.POWER_FAILURE_MONITORING_OBJECTS
|
|
1795
|
+
elif ln.d == 8 and ln.e in __range63:
|
|
1796
|
+
return media_id.OPERATING_TIME_OBJECTS
|
|
1797
|
+
elif ln.d == 9 and ln.e in (0, 1, 2):
|
|
1798
|
+
return media_id.ENVIRONMENT_RELATED_PARAMETERS_OBJECTS
|
|
1799
|
+
elif ln.d == 10 and ln.e in range(1, 11):
|
|
1800
|
+
return media_id.STATUS_REGISTER_OBJECTS
|
|
1801
|
+
elif ln.d == 11 and ln.e in range(0, 100):
|
|
1802
|
+
return media_id.EVENT_CODE_OBJECTS
|
|
1803
|
+
elif ln.d == 12 and ln.e in range(0, 7):
|
|
1804
|
+
return media_id.COMMUNICATION_PORT_LOG_PARAMETER_OBJECTS
|
|
1805
|
+
elif ln.d == 13 and ln.e in (0, 1):
|
|
1806
|
+
return media_id.CONSUMER_MESSAGE_OBJECTS
|
|
1807
|
+
elif ln.d == 14 and ln.e in range(0, 16):
|
|
1808
|
+
return media_id.CURRENTLY_ACTIVE_TARIFF_OBJECTS
|
|
1809
|
+
elif ln.d == 15 and ln.e in range(0, 100):
|
|
1810
|
+
return media_id.EVENT_COUNTER_OBJECTS
|
|
1811
|
+
elif ln.d == 16 and ln.e in range(0, 10):
|
|
1812
|
+
return media_id.PROFILE_ENTRY_DIGITAL_SIGNATURE_OBJECTS
|
|
1813
|
+
elif ln.d == 17 and ln.e in range(0, 128):
|
|
1814
|
+
return media_id.PROFILE_ENTRY_COUNTER_OBJECTS
|
|
1815
|
+
elif ln.d == 20:
|
|
1816
|
+
return media_id.METER_TAMPER_EVENT_RELATED_OBJECTS
|
|
1817
|
+
elif ln.d in range(50, 100):
|
|
1818
|
+
return media_id.ABSTRACT_MANUFACTURER_SPECIFIC
|
|
1819
|
+
elif ln.c == 97:
|
|
1820
|
+
if ln.d == 97 and ln.e in __range10_and_255:
|
|
1821
|
+
return media_id.ERROR_REGISTER_OBJECTS
|
|
1822
|
+
elif ln.d == 98 and (ln.e in chain(range(0, 30), (255,))):
|
|
1823
|
+
return media_id.ALARM_REGISTER_FILTER_DESCRIPTOR_OBJECTS
|
|
1824
|
+
elif ln.c == 98:
|
|
1825
|
+
return media_id.GENERAL_LIST_OBJECTS
|
|
1826
|
+
elif ln.c == 99:
|
|
1827
|
+
if ln.d in (1, 2, 12, 13, 14, 15, 16, 17, 18) or (ln.d == 3 and ln.e == 0):
|
|
1828
|
+
return media_id.ABSTRACT_DATA_PROFILE_OBJECTS
|
|
1829
|
+
if ln.d == 98:
|
|
1830
|
+
return media_id.EVENT_LOG_OBJECTS
|
|
1831
|
+
elif ln.c == 127 and ln.d == 0:
|
|
1832
|
+
return media_id.INACTIVE_OBJECTS
|
|
1833
|
+
else:
|
|
1834
|
+
return media_id.ABSTRACT
|
|
1835
|
+
elif ln.a == media_id.ELECTRICITY:
|
|
1836
|
+
if ln.c == 0:
|
|
1837
|
+
if ln.d == 0 and ln.e in __range10_and_255:
|
|
1838
|
+
return media_id.ID_NUMBERS_ELECTRICITY
|
|
1839
|
+
elif ln.d == 1:
|
|
1840
|
+
return media_id.BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES_EL
|
|
1841
|
+
elif ln.d in (2, 3, 4, 6, 7, 8, 9, 10):
|
|
1842
|
+
return media_id.OTHER_ELECTRICITY_RELATED_GENERAL_PURPOSE_OBJECTS
|
|
1843
|
+
elif ln.d == 11 and ln.e in (1, 2, 3, 4, 5, 6, 7):
|
|
1844
|
+
return media_id.MEASUREMENT_ALGORITHM
|
|
1845
|
+
elif ln.c in (1, 21, 41, 61):
|
|
1846
|
+
return media_id.ACTIVE_POWER_PLUS
|
|
1847
|
+
elif ln.c in (2, 22, 42, 62):
|
|
1848
|
+
return media_id.ACTIVE_POWER_MINUS
|
|
1849
|
+
elif ln.c in (3, 23, 43, 63):
|
|
1850
|
+
return media_id.REACTIVE_POWER_PLUS
|
|
1851
|
+
elif ln.c in (4, 24, 44, 64):
|
|
1852
|
+
return media_id.REACTIVE_POWER_MINUS
|
|
1853
|
+
elif ln.c in (5, 25, 45, 65):
|
|
1854
|
+
return media_id.REACTIVE_POWER_QI
|
|
1855
|
+
elif ln.c in (6, 26, 46, 66):
|
|
1856
|
+
return media_id.REACTIVE_POWER_QII
|
|
1857
|
+
elif ln.c in (7, 27, 47, 67):
|
|
1858
|
+
return media_id.REACTIVE_POWER_QIII
|
|
1859
|
+
elif ln.c in (8, 28, 48, 68):
|
|
1860
|
+
return media_id.REACTIVE_POWER_QIV
|
|
1861
|
+
elif ln.c in (9, 29, 49, 69):
|
|
1862
|
+
return media_id.APPARENT_POWER_PLUS
|
|
1863
|
+
elif ln.c in (10, 30, 50, 70):
|
|
1864
|
+
return media_id.APPARENT_POWER_MINUS
|
|
1865
|
+
elif ln.c in (11, 31, 51, 71):
|
|
1866
|
+
return media_id.CURRENT
|
|
1867
|
+
elif ln.c in (12, 32, 52, 72):
|
|
1868
|
+
return media_id.VOLTAGE
|
|
1869
|
+
elif ln.c in (13, 33, 53, 73):
|
|
1870
|
+
return media_id.POWER_FACTOR
|
|
1871
|
+
elif ln.c in (14, 34, 54, 74):
|
|
1872
|
+
return media_id.SUPPLY_FREQUENCY
|
|
1873
|
+
elif ln.c in (15, 35, 55, 75):
|
|
1874
|
+
return media_id.ACTIVE_POWER_SUM
|
|
1875
|
+
elif ln.c in (16, 36, 56, 76):
|
|
1876
|
+
return media_id.ACTIVE_POWER_DIFF
|
|
1877
|
+
elif ln.c in (17, 37, 57, 77):
|
|
1878
|
+
return media_id.ACTIVE_POWER_QI
|
|
1879
|
+
elif ln.c in (18, 38, 58, 78):
|
|
1880
|
+
return media_id.ACTIVE_POWER_QII
|
|
1881
|
+
elif ln.c in (19, 39, 59, 79):
|
|
1882
|
+
return media_id.ACTIVE_POWER_QIII
|
|
1883
|
+
elif ln.c in (20, 40, 60, 80):
|
|
1884
|
+
return media_id.ACTIVE_POWER_QIV
|
|
1885
|
+
elif ln.c == 96:
|
|
1886
|
+
if ln.d == 1 and ln.e in __range10_and_255:
|
|
1887
|
+
return media_id.ELECTRICITY_METERING_POINT_ID_OBJECTS
|
|
1888
|
+
elif ln.d == 5 and ln.e in (0, 1, 2, 3, 4, 5):
|
|
1889
|
+
return media_id.ELECTRICITY_RELATED_STATUS_OBJECTS
|
|
1890
|
+
elif ln.d == 10 and ln.e in (0, 1, 2, 3):
|
|
1891
|
+
return media_id.ELECTRICITY_RELATED_STATUS_OBJECTS
|
|
1892
|
+
elif ln.c == 98:
|
|
1893
|
+
return media_id.LIST_OBJECTS_ELECTRICITY
|
|
1894
|
+
elif ln.d in range(31, 46) and ln.f in __range100_and_255:
|
|
1895
|
+
if ln.c in __c1 and ln.e in __range63:
|
|
1896
|
+
return media_id.THRESHOLD_VALUES
|
|
1897
|
+
elif ln.c in __table44 and ln.e in __range120_and_124_127:
|
|
1898
|
+
return media_id.THRESHOLD_VALUES
|
|
1899
|
+
elif ln.f in __range100_and_255:
|
|
1900
|
+
if ln.d in (31, 35, 39, 4, 5, 14, 15, 24, 25):
|
|
1901
|
+
if ln.c in __c1 and ln.e in __range63:
|
|
1902
|
+
return media_id.REGISTER_MONITOR_OBJECTS
|
|
1903
|
+
elif ln.c in __c2 and ln.e in __range120_and_124_127:
|
|
1904
|
+
return media_id.REGISTER_MONITOR_OBJECTS
|
|
1905
|
+
else:
|
|
1906
|
+
return media_id.ELECTRICITY
|
|
1907
|
+
elif ln.a == media_id.HCA:
|
|
1908
|
+
if ln.c == 0:
|
|
1909
|
+
if ln.d == 0 and ln.e in __range10_and_255:
|
|
1910
|
+
return media_id.ID_NUMBERS_HCA
|
|
1911
|
+
elif ln.d == 1 and ln.e in (1, 2, 10, 11):
|
|
1912
|
+
return media_id.BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES_HCA
|
|
1913
|
+
elif ln.d == 2 and ln.e in (0, 1, 2, 3):
|
|
1914
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_HCA
|
|
1915
|
+
elif ln.d == 4 and ln.e in (0, 1, 2, 3, 4, 5, 6):
|
|
1916
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_HCA
|
|
1917
|
+
elif ln.d == 5 and ln.e in (10, 11):
|
|
1918
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_HCA
|
|
1919
|
+
elif ln.d == 8 and ln.e in (0, 4, 6):
|
|
1920
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_HCA
|
|
1921
|
+
elif ln.d == 9 and ln.e in (1, 2, 3):
|
|
1922
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_HCA
|
|
1923
|
+
elif ln.c in (1, 2) and ln.e == 0:
|
|
1924
|
+
if ln.d in (0, 6) and ln.f == 255:
|
|
1925
|
+
return media_id.MEASURED_VALUES_HCA_CONSUMPTION
|
|
1926
|
+
elif ln.d in (1, 2, 3, 4, 5) and ln.f in __range100_and_101_125_and_255:
|
|
1927
|
+
return media_id.MEASURED_VALUES_HCA_CONSUMPTION
|
|
1928
|
+
elif ln.c in range(3, 8) and ln.d in (0, 4, 5, 6) and ln.e == 255 and ln.f == 255:
|
|
1929
|
+
return media_id.MEASURED_VALUES_HCA_TEMPERATURE
|
|
1930
|
+
elif ln.c == 97 and ln.d == 97:
|
|
1931
|
+
return media_id.ERROR_REGISTER_OBJECTS_HCA
|
|
1932
|
+
elif ln.c == 98:
|
|
1933
|
+
return media_id.LIST_OBJECTS_HCA
|
|
1934
|
+
elif ln.c == 99 and ln.d == 1:
|
|
1935
|
+
return media_id.DATA_PROFILE_OBJECTS_HCA
|
|
1936
|
+
else:
|
|
1937
|
+
return media_id.HCA
|
|
1938
|
+
elif ln.a == media_id.THERMAL:
|
|
1939
|
+
if ln.c == 0:
|
|
1940
|
+
if ln.d == 0 and ln.e in __range10_and_255:
|
|
1941
|
+
return media_id.ID_NUMBERS_THERMAL
|
|
1942
|
+
elif ln.d == 1 and ln.e in (1, 2, 10, 11):
|
|
1943
|
+
return media_id.BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES_THERMAL
|
|
1944
|
+
elif ln.d == 2 and ln.e in chain(range(0, 5), range(10, 14)):
|
|
1945
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_THERMAL
|
|
1946
|
+
elif ln.d == 4 and ln.e in (1, 2, 3):
|
|
1947
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_THERMAL
|
|
1948
|
+
elif ln.d == 5 and ln.e in chain(range(1, 10), range(21, 25)):
|
|
1949
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_THERMAL
|
|
1950
|
+
elif ln.d == 8 and ln.e in chain(range(0, 8), range(11, 15), range(21, 26), range(31, 35)):
|
|
1951
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_THERMAL
|
|
1952
|
+
elif ln.d == 9 and ln.e in (1, 2, 3):
|
|
1953
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_THERMAL
|
|
1954
|
+
elif ln.c in range(1, 8):
|
|
1955
|
+
if ln.e in range(10):
|
|
1956
|
+
if ln.d in (0, 1, 2, 3, 7) and ln.f == 255:
|
|
1957
|
+
return media_id.MEASURED_VALUES_THERMAL_CONSUMPTION
|
|
1958
|
+
elif ln.d in (3, 8, 9) and ln.f in __range100_and_101_125_and_255:
|
|
1959
|
+
return media_id.MEASURED_VALUES_THERMAL_CONSUMPTION
|
|
1960
|
+
elif ln.d in (1, 2, 4, 5, 12, 13, 14, 15) and ln.f in chain(range(100), range(100, 126)):
|
|
1961
|
+
return media_id.MEASURED_VALUES_THERMAL_CONSUMPTION
|
|
1962
|
+
elif ln.d == 6 and ln.e == 255 and ln.f == 255:
|
|
1963
|
+
return media_id.MEASURED_VALUES_THERMAL_CONSUMPTION
|
|
1964
|
+
elif ln.e in range(10):
|
|
1965
|
+
if ln.f in __range100_and_101_125_and_255:
|
|
1966
|
+
if ln.c in range(1, 8) and ln.d in (5, 15):
|
|
1967
|
+
return media_id.MEASURED_VALUES_THERMAL_ENERGY
|
|
1968
|
+
elif ln.c in (8, 9) and ln.d in (1, 4, 5, 12, 13, 14, 15):
|
|
1969
|
+
return media_id.MEASURED_VALUES_THERMAL_ENERGY
|
|
1970
|
+
elif ln.c in range(10, 14):
|
|
1971
|
+
if ln.d == 0 and ln.f == 255:
|
|
1972
|
+
return media_id.MEASURED_VALUES_THERMAL_ENERGY
|
|
1973
|
+
elif ln.d in (4, 5, 14, 15) and ln.f in chain(range(100), range(101, 126)):
|
|
1974
|
+
return media_id.MEASURED_VALUES_THERMAL_ENERGY
|
|
1975
|
+
elif ln.d in (6, 7, 10, 11) and ln.f == 255:
|
|
1976
|
+
return media_id.MEASURED_VALUES_THERMAL_ENERGY
|
|
1977
|
+
elif ln.c in range(1, 14) and (ln.d in range(20, 26)) and ln.f == 255:
|
|
1978
|
+
return media_id.MEASURED_VALUES_THERMAL_ENERGY
|
|
1979
|
+
elif ln.c == 97 and ln.d == 97 and ln.e in (0, 1, 2):
|
|
1980
|
+
return media_id.ERROR_REGISTER_OBJECTS_THERMAL
|
|
1981
|
+
elif ln.c == 98:
|
|
1982
|
+
return media_id.LIST_OBJECTS_THERMAL
|
|
1983
|
+
elif ln.c == 99 and ln.f == 255:
|
|
1984
|
+
if ln.d in (1, 2) and ln.e in (1, 2, 3):
|
|
1985
|
+
return media_id.DATA_PROFILE_OBJECTS_THERMAL
|
|
1986
|
+
elif ln.d == 3 and ln.e == 1:
|
|
1987
|
+
return media_id.DATA_PROFILE_OBJECTS_THERMAL
|
|
1988
|
+
elif ln.d == 99:
|
|
1989
|
+
return media_id.DATA_PROFILE_OBJECTS_THERMAL
|
|
1990
|
+
else:
|
|
1991
|
+
return media_id.THERMAL
|
|
1992
|
+
elif media_id.GAS == ln.a:
|
|
1993
|
+
if ln.c == 0:
|
|
1994
|
+
if ln.d == 0 and ln.e in __range10_and_255:
|
|
1995
|
+
return media_id.ID_NUMBERS_GAS
|
|
1996
|
+
elif ln.d == 1:
|
|
1997
|
+
return media_id.BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES_GAS
|
|
1998
|
+
elif ln.d in (2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15):
|
|
1999
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_GAS
|
|
2000
|
+
elif ln.c == 96 and ln.d == 5 and (ln.e in range(10)):
|
|
2001
|
+
return media_id.INTERNAL_OPERATING_STATUS_OBJECTS_GAS
|
|
2002
|
+
elif ln.c in chain(range(1, 9), range(11, 17), range(21, 27), range(31, 36), range(61, 66)) and ln.e in __range63:
|
|
2003
|
+
if ln.d in (24, 25, 26, 42, 43, 44, 63, 64, 65, 81, 82, 83) and ln.f in chain(range(100), range(101, 127)):
|
|
2004
|
+
return media_id.MEASURED_VALUES_GAS_INDEXES_AND_INDEX_DIFFERENCES
|
|
2005
|
+
elif ln.d in chain(range(6, 24), range(27, 33), range(45, 51), range(66, 72), range(84, 90)) and ln.f == 255:
|
|
2006
|
+
return media_id.MEASURED_VALUES_GAS_INDEXES_AND_INDEX_DIFFERENCES
|
|
2007
|
+
elif ln.d in chain(range(33, 42), range(52, 63), range(72, 81), range(90, 99)) and ln.f in chain(__range100_and_101_125_and_255, (126,)):
|
|
2008
|
+
return media_id.MEASURED_VALUES_GAS_INDEXES_AND_INDEX_DIFFERENCES
|
|
2009
|
+
elif ln.c == 42 and ln.e == 0:
|
|
2010
|
+
if ln.d in chain((0, 1, 2, 13), range(15, 19), range(19, 31), range(35, 51), range(55, 71)) and ln.f == 255:
|
|
2011
|
+
return media_id.MEASURED_VALUES_GAS_FLOW_RATE
|
|
2012
|
+
elif ln.d in chain(range(31, 35), range(51, 55)) and ln.f in chain(__range100_and_101_125_and_255, (126,)):
|
|
2013
|
+
return media_id.MEASURED_VALUES_GAS_FLOW_RATE
|
|
2014
|
+
elif ln.c in chain((41, 42), range(44, 50)) and ln.d in (0, 2, 3, 10, 11, 13, range(15, 92)) and ln.e == 0 and ln.f == 255:
|
|
2015
|
+
return media_id.MEASURED_VALUES_GAS_PROCESS_VALUES
|
|
2016
|
+
elif ln.c in range(51, 56):
|
|
2017
|
+
if ln.d in (0, 2, 3, 10, 11) and ln.e in chain((0, 1), range(11, 29)) and ln.f == 255:
|
|
2018
|
+
return media_id.CONVERSION_RELATED_FACTORS_AND_COEFFICIENTS_GAS
|
|
2019
|
+
elif ln.d == 12 and ln.e in range(20) and ln.f == 255:
|
|
2020
|
+
return media_id.CALCULATION_METHODS_GAS
|
|
2021
|
+
elif ln.c == 70 and ln.f == 255:
|
|
2022
|
+
if ln.d in (8, 9) and ln.e == 0:
|
|
2023
|
+
return media_id.NATURAL_GAS_ANALYSIS
|
|
2024
|
+
elif ln.d in chain(range(10, 21), range(60, 85)) and ln.e in chain((0, 1), range(11, 29)):
|
|
2025
|
+
return media_id.NATURAL_GAS_ANALYSIS
|
|
2026
|
+
elif ln.c == 98:
|
|
2027
|
+
return media_id.LIST_OBJECTS_GAS
|
|
2028
|
+
else:
|
|
2029
|
+
return media_id.GAS
|
|
2030
|
+
elif ln.a == media_id.WATER:
|
|
2031
|
+
if ln.c == 0:
|
|
2032
|
+
if ln.d == 0 and ln.e in __range10_and_255:
|
|
2033
|
+
return media_id.ID_NUMBERS_WATER
|
|
2034
|
+
elif ln.d == 1 and ln.e in (1, 2, 10, 11, 12):
|
|
2035
|
+
return media_id.BILLING_PERIOD_VALUES_RESET_COUNTER_ENTRIES_WATER
|
|
2036
|
+
elif ln.d == 2 and ln.e in (0, 3):
|
|
2037
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_WATER
|
|
2038
|
+
elif ln.d in (5, 7) and ln.e == 1:
|
|
2039
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_WATER
|
|
2040
|
+
elif ln.d == 8 and ln.e in (1, 6):
|
|
2041
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_WATER
|
|
2042
|
+
elif ln.d == 9 and ln.e in (1, 2, 3):
|
|
2043
|
+
return media_id.GENERAL_PURPOSE_OBJECTS_WATER
|
|
2044
|
+
elif ln.c == 1 and ln.e in range(0, 13):
|
|
2045
|
+
if ln.d in (0, 1, 2, 3, 6) and ln.f == 255:
|
|
2046
|
+
return media_id.MEASURED_VALUES_WATER_CONSUMPTION
|
|
2047
|
+
elif ln.d in range(1, 6) and ln.f in chain(range(100), range(101, 126)):
|
|
2048
|
+
return media_id.MEASURED_VALUES_WATER_CONSUMPTION
|
|
2049
|
+
elif ln.c in (2, 3) and ln.e in range(0, 13):
|
|
2050
|
+
if ln.d in (0, 1, 2, 3, 6) and ln.f == 255:
|
|
2051
|
+
return media_id.MEASURED_VALUES_WATER_MONITORING_VALUES
|
|
2052
|
+
elif ln.d in range(1, 6) and ln.f in chain(range(100), range(101, 126)):
|
|
2053
|
+
return media_id.MEASURED_VALUES_WATER_MONITORING_VALUES
|
|
2054
|
+
elif ln.c == 97 and ln.d == 97:
|
|
2055
|
+
return media_id.ERROR_REGISTER_OBJECTS_WATER
|
|
2056
|
+
elif ln.c == 98:
|
|
2057
|
+
return media_id.LIST_OBJECTS_WATER
|
|
2058
|
+
elif ln.c == 99 and ln.d == 1:
|
|
2059
|
+
return media_id.DATA_PROFILE_OBJECTS_WATER
|
|
2060
|
+
else:
|
|
2061
|
+
return media_id.WATER
|
|
2062
|
+
return media_id.OTHER_MEDIA
|
|
2063
|
+
|
|
2064
|
+
|
|
2065
|
+
DLMSObjectContainer: TypeAlias = Collection | list[InterfaceClass] | filter
|
|
2066
|
+
|
|
2067
|
+
|
|
2068
|
+
@dataclass
|
|
2069
|
+
class Template:
|
|
2070
|
+
name: str
|
|
2071
|
+
collections: list[Collection]
|
|
2072
|
+
used: UsedAttributes
|
|
2073
|
+
description: str = ""
|
|
2074
|
+
verified: bool = False
|
|
2075
|
+
|
|
2076
|
+
def get_not_contains(self, cols: Iterable[Collection]) -> list[Collection]:
|
|
2077
|
+
"""return of collections not contains in template"""
|
|
2078
|
+
ret = []
|
|
2079
|
+
for i, col in enumerate(cols):
|
|
2080
|
+
if col not in self.collections:
|
|
2081
|
+
ret.append(col)
|
|
2082
|
+
return ret
|
|
2083
|
+
|
|
2084
|
+
def get_not_valid(self, col: Collection) -> list[Exception]:
|
|
2085
|
+
"""with update col"""
|
|
2086
|
+
attr: cdt.CommonDataType
|
|
2087
|
+
ret = []
|
|
2088
|
+
use_col = self.collections[0]
|
|
2089
|
+
"""temporary used first collection"""
|
|
2090
|
+
for ln, indexes in self.used.items():
|
|
2091
|
+
try:
|
|
2092
|
+
obj = use_col.get_object(ln)
|
|
2093
|
+
obj_col = col.get_object(ln)
|
|
2094
|
+
except exc.NoObject as e:
|
|
2095
|
+
ret.append(e)
|
|
2096
|
+
print(F"<add_collection> skip obj{self}: {e}")
|
|
2097
|
+
continue
|
|
2098
|
+
for i in indexes:
|
|
2099
|
+
if (attr := obj.get_attr(i)) is not None:
|
|
2100
|
+
try:
|
|
2101
|
+
attr.validate()
|
|
2102
|
+
except ValueError as e:
|
|
2103
|
+
ret.append(ValueError(F"can't decode value {attr} for {ln}:{i}"))
|
|
2104
|
+
else:
|
|
2105
|
+
ret.append(exc.NoObject(F"has't attribute {i} for {ln}"))
|
|
2106
|
+
return ret
|