DLMS-SPODES 0.87.13__py3-none-any.whl → 0.87.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. DLMS_SPODES/Values/EN/__init__.py +1 -1
  2. DLMS_SPODES/Values/EN/actors.py +8 -8
  3. DLMS_SPODES/Values/EN/relation_to_obis_names.py +387 -387
  4. DLMS_SPODES/Values/RU/__init__.py +1 -1
  5. DLMS_SPODES/Values/RU/actors.py +8 -8
  6. DLMS_SPODES/Values/RU/relation_to_obis_names.py +396 -396
  7. DLMS_SPODES/__init__.py +6 -6
  8. DLMS_SPODES/configEN.ini +126 -126
  9. DLMS_SPODES/config_parser.py +53 -53
  10. DLMS_SPODES/cosem_interface_classes/__class_init__.py +3 -3
  11. DLMS_SPODES/cosem_interface_classes/__init__.py +1 -1
  12. DLMS_SPODES/cosem_interface_classes/a_parameter.py +20 -20
  13. DLMS_SPODES/cosem_interface_classes/activity_calendar.py +254 -254
  14. DLMS_SPODES/cosem_interface_classes/arbitrator.py +105 -105
  15. DLMS_SPODES/cosem_interface_classes/association_ln/abstract.py +34 -34
  16. DLMS_SPODES/cosem_interface_classes/association_ln/authentication_mechanism_name.py +25 -25
  17. DLMS_SPODES/cosem_interface_classes/association_ln/mechanism_id.py +25 -25
  18. DLMS_SPODES/cosem_interface_classes/association_ln/method.py +5 -5
  19. DLMS_SPODES/cosem_interface_classes/association_ln/ver0.py +485 -485
  20. DLMS_SPODES/cosem_interface_classes/association_ln/ver1.py +133 -133
  21. DLMS_SPODES/cosem_interface_classes/association_ln/ver2.py +36 -36
  22. DLMS_SPODES/cosem_interface_classes/association_ln/ver3.py +4 -4
  23. DLMS_SPODES/cosem_interface_classes/association_sn/ver0.py +12 -12
  24. DLMS_SPODES/cosem_interface_classes/attr_indexes.py +12 -12
  25. DLMS_SPODES/cosem_interface_classes/clock.py +131 -131
  26. DLMS_SPODES/cosem_interface_classes/collection.py +2122 -2122
  27. DLMS_SPODES/cosem_interface_classes/cosem_interface_class.py +583 -583
  28. DLMS_SPODES/cosem_interface_classes/data.py +21 -21
  29. DLMS_SPODES/cosem_interface_classes/demand_register/ver0.py +59 -59
  30. DLMS_SPODES/cosem_interface_classes/disconnect_control.py +74 -74
  31. DLMS_SPODES/cosem_interface_classes/extended_register.py +27 -27
  32. DLMS_SPODES/cosem_interface_classes/gprs_modem_setup.py +43 -43
  33. DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver0.py +103 -103
  34. DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver1.py +40 -40
  35. DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver2.py +9 -9
  36. DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver0.py +11 -11
  37. DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver1.py +53 -53
  38. DLMS_SPODES/cosem_interface_classes/iec_local_port_setup.py +11 -11
  39. DLMS_SPODES/cosem_interface_classes/image_transfer/image_transfer_status.py +15 -15
  40. DLMS_SPODES/cosem_interface_classes/image_transfer/ver0.py +126 -126
  41. DLMS_SPODES/cosem_interface_classes/implementations/__init__.py +3 -3
  42. DLMS_SPODES/cosem_interface_classes/implementations/arbitrator.py +19 -19
  43. DLMS_SPODES/cosem_interface_classes/implementations/data.py +487 -487
  44. DLMS_SPODES/cosem_interface_classes/implementations/profile_generic.py +83 -83
  45. DLMS_SPODES/cosem_interface_classes/ipv4_setup.py +72 -72
  46. DLMS_SPODES/cosem_interface_classes/limiter.py +111 -111
  47. DLMS_SPODES/cosem_interface_classes/ln_pattern.py +333 -333
  48. DLMS_SPODES/cosem_interface_classes/modem_configuration/ver0.py +65 -65
  49. DLMS_SPODES/cosem_interface_classes/modem_configuration/ver1.py +39 -39
  50. DLMS_SPODES/cosem_interface_classes/ntp_setup/ver0.py +67 -67
  51. DLMS_SPODES/cosem_interface_classes/obis.py +23 -23
  52. DLMS_SPODES/cosem_interface_classes/overview.py +197 -197
  53. DLMS_SPODES/cosem_interface_classes/parameter.py +547 -547
  54. DLMS_SPODES/cosem_interface_classes/parameters.py +172 -172
  55. DLMS_SPODES/cosem_interface_classes/profile_generic/ver0.py +122 -122
  56. DLMS_SPODES/cosem_interface_classes/profile_generic/ver1.py +277 -277
  57. DLMS_SPODES/cosem_interface_classes/push_setup/ver0.py +12 -12
  58. DLMS_SPODES/cosem_interface_classes/push_setup/ver1.py +10 -10
  59. DLMS_SPODES/cosem_interface_classes/push_setup/ver2.py +166 -166
  60. DLMS_SPODES/cosem_interface_classes/register.py +45 -45
  61. DLMS_SPODES/cosem_interface_classes/register_activation/ver0.py +80 -80
  62. DLMS_SPODES/cosem_interface_classes/register_monitor.py +46 -46
  63. DLMS_SPODES/cosem_interface_classes/reports.py +70 -70
  64. DLMS_SPODES/cosem_interface_classes/schedule.py +176 -176
  65. DLMS_SPODES/cosem_interface_classes/script_table.py +87 -87
  66. DLMS_SPODES/cosem_interface_classes/security_setup/ver0.py +68 -68
  67. DLMS_SPODES/cosem_interface_classes/security_setup/ver1.py +158 -158
  68. DLMS_SPODES/cosem_interface_classes/single_action_schedule.py +50 -50
  69. DLMS_SPODES/cosem_interface_classes/special_days_table.py +84 -84
  70. DLMS_SPODES/cosem_interface_classes/tcp_udp_setup.py +42 -42
  71. DLMS_SPODES/cosem_pdu.py +93 -93
  72. DLMS_SPODES/enums.py +625 -625
  73. DLMS_SPODES/exceptions.py +106 -106
  74. DLMS_SPODES/firmwares.py +99 -99
  75. DLMS_SPODES/hdlc/frame.py +875 -875
  76. DLMS_SPODES/hdlc/sub_layer.py +54 -54
  77. DLMS_SPODES/literals.py +17 -17
  78. DLMS_SPODES/obis/__init__.py +1 -1
  79. DLMS_SPODES/obis/media_id.py +931 -931
  80. DLMS_SPODES/pardata.py +22 -22
  81. DLMS_SPODES/pdu_enums.py +98 -98
  82. DLMS_SPODES/relation_to_OBIS.py +465 -463
  83. DLMS_SPODES/settings.py +551 -551
  84. DLMS_SPODES/types/choices.py +142 -142
  85. DLMS_SPODES/types/common_data_types.py +2401 -2401
  86. DLMS_SPODES/types/cosem_service_types.py +109 -109
  87. DLMS_SPODES/types/implementations/arrays.py +25 -25
  88. DLMS_SPODES/types/implementations/bitstrings.py +97 -97
  89. DLMS_SPODES/types/implementations/double_long_usingneds.py +35 -35
  90. DLMS_SPODES/types/implementations/enums.py +57 -57
  91. DLMS_SPODES/types/implementations/integers.py +11 -11
  92. DLMS_SPODES/types/implementations/long_unsigneds.py +127 -127
  93. DLMS_SPODES/types/implementations/octet_string.py +11 -11
  94. DLMS_SPODES/types/implementations/structs.py +64 -64
  95. DLMS_SPODES/types/useful_types.py +677 -677
  96. {dlms_spodes-0.87.13.dist-info → dlms_spodes-0.87.16.dist-info}/METADATA +30 -30
  97. dlms_spodes-0.87.16.dist-info/RECORD +117 -0
  98. {dlms_spodes-0.87.13.dist-info → dlms_spodes-0.87.16.dist-info}/WHEEL +1 -1
  99. dlms_spodes-0.87.13.dist-info/RECORD +0 -117
  100. {dlms_spodes-0.87.13.dist-info → dlms_spodes-0.87.16.dist-info}/top_level.txt +0 -0
DLMS_SPODES/hdlc/frame.py CHANGED
@@ -1,875 +1,875 @@
1
- from __future__ import annotations
2
- from abc import ABC, abstractmethod
3
- from struct import unpack, pack
4
- from functools import cached_property
5
- from typing import Deque
6
- from enum import IntFlag
7
-
8
- _FLAG: int = 0x7e
9
-
10
-
11
- class NotEnoughDataError(Exception):
12
- """ Not enough data received, need more for parse full Frame """
13
-
14
-
15
- class FormatDataError(Exception):
16
- """ Frame format is not Type 3 HDLC """
17
-
18
-
19
- class Format:
20
- """ This optional field is present only when using the non-basic frame format. When present, it follows the opening flag sequence. The frame format field is 2 octets in length
21
- and consists of three subfields referred to as the format type subfield, the segmentation subfield and the frame length subfield. The format of the frame format field is as
22
- follows, ISO/IEC 13239:2002(E), 4.9 Frame format field, Type 3 :
23
- Type(4 bits) - Segmentation(1 bit) - Length(11 bit) """
24
- __content: bytes
25
-
26
- def __init__(self, content: bytes = None,
27
- is_segmentation: bool = None,
28
- length: int = None):
29
- if content is not None:
30
- if len(content) != 2:
31
- raise ValueError(F'Wrong length Frame format type, must be 2, got {len(content)}')
32
- else:
33
- self.__content = content
34
- if self.type != 0xA:
35
- raise FormatDataError(F'Frame format type not according HDLC Type 3, must be 0xA, got {hex(self.type)}')
36
- else:
37
- if length.bit_length() <= 13:
38
- value = length
39
- if is_segmentation:
40
- value |= 0b1010_1_00000000000
41
- else:
42
- value |= 0b1010_0_00000000000
43
- self.__content = pack('>H', value)
44
- else:
45
- raise ValueError(F'Frame length overflow, max be 2048, got {length}')
46
-
47
- @property
48
- def content(self) -> bytes:
49
- return self.__content
50
-
51
- @cached_property
52
- def type(self) -> int:
53
- """ Must be 0b1010 in first 4 bits """
54
- return self.__content[0] >> 4
55
-
56
- @cached_property
57
- def length(self) -> int:
58
- """ return length of frame. Mask 11bit. """
59
- return unpack('>H', self.__content)[0] & 0b0000_0_111_11111111
60
-
61
- @cached_property
62
- def is_segmentation(self) -> bool:
63
- return bool(self.__content[0] & 0b00001000)
64
-
65
- def __str__(self):
66
- return F'Type 3: length-{self.length} {"segmentation" if self.is_segmentation else ""}'
67
-
68
-
69
- class AddressLength(int):
70
- AUTO = -1
71
- __VALID_VALUES = (AUTO, 1, 2, 4)
72
-
73
- def __new__(cls, *args, **kwargs):
74
- if args[0] not in cls.__VALID_VALUES:
75
- raise ValueError(F"got {cls.__name__}={args[0]}, expected {cls.__VALID_VALUES}")
76
- return super().__new__(cls, *args)
77
-
78
- @classmethod
79
- def from_str(cls, value: str):
80
- if value == "AUTO":
81
- value = -1
82
- return cls(int(value))
83
-
84
- def __str__(self):
85
- if self == -1:
86
- return "AUTO"
87
- else:
88
- return super().__str__()
89
-
90
- @classmethod
91
- def get_values(cls):
92
- return tuple(str(cls(it)) for it in cls.__VALID_VALUES)
93
-
94
-
95
- class Address:
96
- """content: if not None then init from content
97
- upper_address, lower_address: set address according
98
- length: forcedly installing length
99
- """
100
- __content: bytes
101
- __MASK: int = 0x3f80
102
-
103
- def __init__(self, content: bytes = None,
104
- upper_address: int = None,
105
- lower_address: int = None,
106
- length: AddressLength = AddressLength.AUTO):
107
- if content is not None:
108
- if len(content) not in (1, 2, 4):
109
- raise ValueError(F"got length Frame from content {len(content)}, expected 1, 2 or 4")
110
- else:
111
- self.__content = content
112
- else:
113
- # calculate length
114
- if not lower_address:
115
- if (upper_address & self.__MASK) == 0:
116
- length2 = 1
117
- else:
118
- lower_address = 0
119
- length2 = 4
120
- else:
121
- if ((upper_address | lower_address) & self.__MASK) == 0:
122
- length2 = 2
123
- else:
124
- length2 = 4
125
- if length == AddressLength.AUTO:
126
- length = length2
127
- elif length >= length2:
128
- """setting more or equally is OK"""
129
- else:
130
- raise ValueError(F"got {length=}, but with {lower_address=}, {upper_address=} it's not possible")
131
- if length == 1:
132
- self.__content = pack('B',
133
- upper_address << 1 | 1)
134
- elif lower_address is not None:
135
- if length == 2:
136
- self.__content = pack("BB",
137
- upper_address << 1,
138
- lower_address << 1 | 1)
139
- if length == 4:
140
- self.__content = pack("BBBB",
141
- upper_address >> 6 & 0b11111110,
142
- upper_address << 1 & 0b11111110,
143
- lower_address >> 6 & 0b11111110,
144
- lower_address << 1 & 0b11111110 | 1)
145
- else:
146
- raise ValueError(F"got {length=}, but lower address is absense, expected (0..16383)")
147
-
148
- @classmethod
149
- def from_frame(cls, value: bytearray) -> Address:
150
- for it in (0, 1, 3):
151
- if value[it] % 2 == 1:
152
- match it:
153
- case 0: new = cls(bytes(value[:1])); break
154
- case 1: new = cls(bytes(value[:2])); break
155
- case 3: new = cls(bytes(value[:4])); break
156
- else:
157
- raise ValueError('HDLC source address wrong, not found end bit')
158
- del value[:len(new)]
159
- return new
160
-
161
- @property
162
- def content(self) -> bytes:
163
- return self.__content
164
-
165
- def __eq__(self, other: Address):
166
- return self.__content == other.content
167
-
168
- @cached_property
169
- def upper(self) -> int:
170
- """ return of upper address with int type """
171
- if len(self.__content) in (1, 2):
172
- return self.__content[0] >> 1
173
- else:
174
- return (self.__content[0] >> 1)*128 + (self.__content[1] >> 1)
175
-
176
- @cached_property
177
- def lower(self) -> int | None:
178
- """ return of lower address with int type """
179
- if len(self.__content) == 1:
180
- return None
181
- elif len(self.__content) == 2:
182
- return self.__content[1] >> 1
183
- else:
184
- return (self.__content[2] >> 1)*128 + (self.__content[3] >> 1)
185
-
186
- def __str__(self):
187
- return F'{self.upper}{"/"+str(self.lower) if self.lower is not None else ""}'
188
-
189
- def __len__(self):
190
- return len(self.__content)
191
-
192
- def __hash__(self):
193
- return int.from_bytes(self.__content, "big")
194
-
195
- def __repr__(self):
196
- return F"{self.__class__.__name__}(upper_address={self.upper}, lower_address={self.lower})"
197
-
198
-
199
- _type = ['Information', 'Supervisory', 'Information', 'Unnumbered']
200
-
201
-
202
- class Control(IntFlag):
203
- """ ISO/IEC 13239:2002(E). P/F = poll bit -- primary station or combined station command frame transmissions/final bit -- secondary station or combined station response
204
- frame transmissions (1 = poll/final) """
205
- # Information transfer command/ response (I format):
206
- # 1 2 3 4 5 6 7 8
207
- # 0 | N(S) | P/F | N(R)
208
- S0_R0 = 0b000_0_000_0
209
- S1_R0 = 0b000_0_001_0
210
- S2_R0 = 0b000_0_010_0
211
- S3_R0 = 0b000_0_011_0
212
- S4_R0 = 0b000_0_100_0
213
- S5_R0 = 0b000_0_101_0
214
- S6_R0 = 0b000_0_110_0
215
- S7_R0 = 0b000_0_111_0
216
-
217
- S0_R1 = 0b001_0_000_0
218
- S1_R1 = 0b001_0_001_0
219
- S2_R1 = 0b001_0_010_0
220
- S3_R1 = 0b001_0_011_0
221
- S4_R1 = 0b001_0_100_0
222
- S5_R1 = 0b001_0_101_0
223
- S6_R1 = 0b001_0_110_0
224
- S7_R1 = 0b001_0_111_0
225
-
226
- S0_R2 = 0b010_0_000_0
227
- S1_R2 = 0b010_0_001_0
228
- S2_R2 = 0b010_0_010_0
229
- S3_R2 = 0b010_0_011_0
230
- S4_R2 = 0b010_0_100_0
231
- S5_R2 = 0b010_0_101_0
232
- S6_R2 = 0b010_0_110_0
233
- S7_R2 = 0b010_0_111_0
234
-
235
- S0_R3 = 0b011_0_000_0
236
- S1_R3 = 0b011_0_001_0
237
- S2_R3 = 0b011_0_010_0
238
- S3_R3 = 0b011_0_011_0
239
- S4_R3 = 0b011_0_100_0
240
- S5_R3 = 0b011_0_101_0
241
- S6_R3 = 0b011_0_110_0
242
- S7_R3 = 0b011_0_111_0
243
-
244
- S0_R4 = 0b100_0_000_0
245
- S1_R4 = 0b100_0_001_0
246
- S2_R4 = 0b100_0_010_0
247
- S3_R4 = 0b100_0_011_0
248
- S4_R4 = 0b100_0_100_0
249
- S5_R4 = 0b100_0_101_0
250
- S6_R4 = 0b100_0_110_0
251
- S7_R4 = 0b100_0_111_0
252
-
253
- S0_R5 = 0b101_0_000_0
254
- S1_R5 = 0b101_0_001_0
255
- S2_R5 = 0b101_0_010_0
256
- S3_R5 = 0b101_0_011_0
257
- S4_R5 = 0b101_0_100_0
258
- S5_R5 = 0b101_0_101_0
259
- S6_R5 = 0b101_0_110_0
260
- S7_R5 = 0b101_0_111_0
261
-
262
- S0_R6 = 0b110_0_000_0
263
- S1_R6 = 0b110_0_001_0
264
- S2_R6 = 0b110_0_010_0
265
- S3_R6 = 0b110_0_011_0
266
- S4_R6 = 0b110_0_100_0
267
- S5_R6 = 0b110_0_101_0
268
- S6_R6 = 0b110_0_110_0
269
- S7_R6 = 0b110_0_111_0
270
-
271
- S0_R7 = 0b111_0_000_0
272
- S1_R7 = 0b111_0_001_0
273
- S2_R7 = 0b111_0_010_0
274
- S3_R7 = 0b111_0_011_0
275
- S4_R7 = 0b111_0_100_0
276
- S5_R7 = 0b111_0_101_0
277
- S6_R7 = 0b111_0_110_0
278
- S7_R7 = 0b111_0_111_0
279
-
280
- S0_R0_PF = 0b000_1_000_0
281
- S1_R0_PF = 0b000_1_001_0
282
- S2_R0_PF = 0b000_1_010_0
283
- S3_R0_PF = 0b000_1_011_0
284
- S4_R0_PF = 0b000_1_100_0
285
- S5_R0_PF = 0b000_1_101_0
286
- S6_R0_PF = 0b000_1_110_0
287
- S7_R0_PF = 0b000_1_111_0
288
-
289
- S0_R1_PF = 0b001_1_000_0
290
- S1_R1_PF = 0b001_1_001_0
291
- S2_R1_PF = 0b001_1_010_0
292
- S3_R1_PF = 0b001_1_011_0
293
- S4_R1_PF = 0b001_1_100_0
294
- S5_R1_PF = 0b001_1_101_0
295
- S6_R1_PF = 0b001_1_110_0
296
- S7_R1_PF = 0b001_1_111_0
297
-
298
- S0_R2_PF = 0b010_1_000_0
299
- S1_R2_PF = 0b010_1_001_0
300
- S2_R2_PF = 0b010_1_010_0
301
- S3_R2_PF = 0b010_1_011_0
302
- S4_R2_PF = 0b010_1_100_0
303
- S5_R2_PF = 0b010_1_101_0
304
- S6_R2_PF = 0b010_1_110_0
305
- S7_R2_PF = 0b010_1_111_0
306
-
307
- S0_R3_PF = 0b011_1_000_0
308
- S1_R3_PF = 0b011_1_001_0
309
- S2_R3_PF = 0b011_1_010_0
310
- S3_R3_PF = 0b011_1_011_0
311
- S4_R3_PF = 0b011_1_100_0
312
- S5_R3_PF = 0b011_1_101_0
313
- S6_R3_PF = 0b011_1_110_0
314
- S7_R3_PF = 0b011_1_111_0
315
-
316
- S0_R4_PF = 0b100_1_000_0
317
- S1_R4_PF = 0b100_1_001_0
318
- S2_R4_PF = 0b100_1_010_0
319
- S3_R4_PF = 0b100_1_011_0
320
- S4_R4_PF = 0b100_1_100_0
321
- S5_R4_PF = 0b100_1_101_0
322
- S6_R4_PF = 0b100_1_110_0
323
- S7_R4_PF = 0b100_1_111_0
324
-
325
- S0_R5_PF = 0b101_1_000_0
326
- S1_R5_PF = 0b101_1_001_0
327
- S2_R5_PF = 0b101_1_010_0
328
- S3_R5_PF = 0b101_1_011_0
329
- S4_R5_PF = 0b101_1_100_0
330
- S5_R5_PF = 0b101_1_101_0
331
- S6_R5_PF = 0b101_1_110_0
332
- S7_R5_PF = 0b101_1_111_0
333
-
334
- S0_R6_PF = 0b110_1_000_0
335
- S1_R6_PF = 0b110_1_001_0
336
- S2_R6_PF = 0b110_1_010_0
337
- S3_R6_PF = 0b110_1_011_0
338
- S4_R6_PF = 0b110_1_100_0
339
- S5_R6_PF = 0b110_1_101_0
340
- S6_R6_PF = 0b110_1_110_0
341
- S7_R6_PF = 0b110_1_111_0
342
-
343
- S0_R7_PF = 0b111_1_000_0
344
- S1_R7_PF = 0b111_1_001_0
345
- S2_R7_PF = 0b111_1_010_0
346
- S3_R7_PF = 0b111_1_011_0
347
- S4_R7_PF = 0b111_1_100_0
348
- S5_R7_PF = 0b111_1_101_0
349
- S6_R7_PF = 0b111_1_110_0
350
- S7_R7_PF = 0b111_1_111_0
351
- # Supervisory commands/ responses (S format): S = supervisory function bit
352
- # 1 2 3 4 5 6 7 8
353
- # 1 0 S S P/F | N(R)
354
- RR_R0 = 0b000_0_00_01
355
- """ Receive ready sequence=0 """
356
- RR_R1 = 0b001_0_00_01
357
- """ Receive ready sequence=1 """
358
- RR_R2 = 0b010_0_00_01
359
- """ Receive ready sequence=2 """
360
- RR_R3 = 0b011_0_00_01
361
- """ Receive ready sequence=3 """
362
- RR_R4 = 0b100_0_00_01
363
- """ Receive ready sequence=4 """
364
- RR_R5 = 0b101_0_00_01
365
- """ Receive ready sequence=5 """
366
- RR_R6 = 0b110_0_00_01
367
- """ Receive ready sequence=6 """
368
- RR_R7 = 0b111_0_00_01
369
- """ Receive ready sequence=7 """
370
-
371
- RR_R0_PF = 0b000_1_00_01
372
- """ Receive ready sequence=0 """
373
- RR_R1_PF = 0b001_1_00_01
374
- """ Receive ready sequence=1 """
375
- RR_R2_PF = 0b010_1_00_01
376
- """ Receive ready sequence=2 """
377
- RR_R3_PF = 0b011_1_00_01
378
- """ Receive ready sequence=3 """
379
- RR_R4_PF = 0b100_1_00_01
380
- """ Receive ready sequence=4 """
381
- RR_R5_PF = 0b101_1_00_01
382
- """ Receive ready sequence=5 """
383
- RR_R6_PF = 0b110_1_00_01
384
- """ Receive ready sequence=6 """
385
- RR_R7_PF = 0b111_1_00_01
386
- """ Receive ready sequence=7 """
387
-
388
- RNR_R0 = 0b000_0_01_01
389
- RNR_R1 = 0b001_0_01_01
390
- RNR_R2 = 0b010_0_01_01
391
- RNR_R3 = 0b011_0_01_01
392
- RNR_R4 = 0b100_0_01_01
393
- RNR_R5 = 0b101_0_01_01
394
- RNR_R6 = 0b110_0_01_01
395
- RNR_R7 = 0b111_0_01_01
396
-
397
- RNR_R0_PF = 0b000_1_01_01
398
- RNR_R1_PF = 0b001_1_01_01
399
- RNR_R2_PF = 0b010_1_01_01
400
- RNR_R3_PF = 0b011_1_01_01
401
- RNR_R4_PF = 0b100_1_01_01
402
- RNR_R5_PF = 0b101_1_01_01
403
- RNR_R6_PF = 0b110_1_01_01
404
- RNR_R7_PF = 0b111_1_01_01
405
-
406
- REJ_R0 = 0b000_0_10_01
407
- REJ_R1 = 0b001_0_10_01
408
- REJ_R2 = 0b010_0_10_01
409
- REJ_R3 = 0b011_0_10_01
410
- REJ_R4 = 0b100_0_10_01
411
- REJ_R5 = 0b101_0_10_01
412
- REJ_R6 = 0b110_0_10_01
413
- REJ_R7 = 0b111_0_10_01
414
-
415
- REJ_R0_PF = 0b000_1_10_01
416
- REJ_R1_PF = 0b001_1_10_01
417
- REJ_R2_PF = 0b010_1_10_01
418
- REJ_R3_PF = 0b011_1_10_01
419
- REJ_R4_PF = 0b100_1_10_01
420
- REJ_R5_PF = 0b101_1_10_01
421
- REJ_R6_PF = 0b110_1_10_01
422
- REJ_R7_PF = 0b111_1_10_01
423
-
424
- SREJ_R0 = 0b000_0_11_01
425
- SREJ_R1 = 0b001_0_11_01
426
- SREJ_R2 = 0b010_0_11_01
427
- SREJ_R3 = 0b011_0_11_01
428
- SREJ_R4 = 0b100_0_11_01
429
- SREJ_R5 = 0b101_0_11_01
430
- SREJ_R6 = 0b110_0_11_01
431
- SREJ_R7 = 0b111_0_11_01
432
-
433
- SREJ_R0_PF = 0b000_1_11_01
434
- SREJ_R1_PF = 0b001_1_11_01
435
- SREJ_R2_PF = 0b010_1_11_01
436
- SREJ_R3_PF = 0b011_1_11_01
437
- SREJ_R4_PF = 0b100_1_11_01
438
- SREJ_R5_PF = 0b101_1_11_01
439
- SREJ_R6_PF = 0b110_1_11_01
440
- SREJ_R7_PF = 0b111_1_11_01
441
-
442
- # Unnumbered commands/ responses (U format): M = modifier function bit
443
- # 11_MM_P/F_MMM
444
- UI_PF = 0b000_1_00_11
445
- """ Unnumbered Information with Poll """
446
- UI = 0b000_0_00_11
447
- """ Unnumbered Information with wait """
448
- XID_PF = 0b101_1_11_11
449
- """ Exchange identification with Poll. Used to Request/Report capabilities """
450
- XID = 0b101_0_11_11
451
- """ Exchange identification with wait. Used to Request/Report capabilities """
452
- TEST_PF = 0b111_1_00_11
453
- """ TEST with Poll. Exchange identical information fields for testing """
454
- TEST = 0b111_0_00_11
455
- """ TEST with wait. Exchange identical information fields for testing """
456
- UIH_PF = 0b111_1_11_11
457
- """ Unnumbered Information with Header check with Poll """
458
- UIH = 0b111_0_11_11
459
- """ Unnumbered Information with Header check with wait """
460
-
461
- # command ISO/IEC 13239:2002(E) 5.5.3.3
462
- # 11_MM_P_MMM
463
- SNRM_P = 0b100_1_00_11
464
- """ Set Normal Response Mode with Poll """
465
- SNRM = 0b100_0_00_11
466
- """ Set Normal Response Mode with wait """
467
- SARM_P = 0b000_1_11_11
468
- """ Set Asynchronous Response Mode with Poll """
469
- SARM = 0b000_0_11_11
470
- """ Set Asynchronous Response with wait """
471
- SABM_P = 0b001_1_11_11
472
- """ Set Asynchronous Balanced Mode with Poll """
473
- SABM = 0b001_0_11_11
474
- """ Set Asynchronous Balanced with wait """
475
- DISC_P = 0b010_1_00_11
476
- """ Disconnect with Poll """
477
- DISC = 0b010_0_00_11
478
- """ Disconnect with wait """
479
- SNRME_P = 0b110_1_11_11
480
- """ Set Normal Response Mode Extended with Poll """
481
- SNRME = 0b110_0_11_11
482
- """ Set Normal Response Mode Extended with wait """
483
- SARME_P = 0b010_1_11_11
484
- """ Set Asynchronous Response Mode Extended with Poll """
485
- SARME = 0b010_0_11_11
486
- """ Set Asynchronous Response Mode Extended with wait """
487
- SABME_P = 0b011_1_11_11
488
- """ Set Asynchronous Balanced Mode Extended with Poll """
489
- SABME = 0b011_0_11_11
490
- """ Set Asynchronous Balanced Mode Extended with wait """
491
- UP_P = 0b001_1_00_11
492
- """ Unnumbered Poll with Poll. Used to solicit control information"""
493
- UP = 0b001_0_00_11
494
- """ Unnumbered Poll with wait. Used to solicit control information"""
495
- SIM_P = 0b000_1_01_11
496
- """ Set Initialization Mode with Poll """
497
- SIM = 0b000_0_01_11
498
- """ Set Initialization Mode with wait """
499
- SM_P = 0b110_1_00_11
500
- """ Set Mode with Poll """
501
- SM = 0b110_0_00_11
502
- """ Set Mode with wait """
503
- RSET_P = 0b100_1_11_11
504
- """ ReSET with Poll. Used for recovery. Resets N(R) but not N(S) """
505
- RSET = 0b100_0_11_11
506
- """ ReSET with wait. Used for recovery. Resets N(R) but not N(S) """
507
-
508
-
509
- # responses ISO/IEC 13239:2002(E) 5.5.3.4.
510
- # 11_MM_F_MMM
511
- UA_F = 0b011_1_00_11
512
- """ Unnumbered Acknowledgement Final """
513
- UA = 0b011_0_00_11
514
- """ Unnumbered Acknowledgement """
515
- FRMR_F = 0b100_1_01_11
516
- """ FRaMe Reject Final """
517
- FRMR = 0b100_0_01_11
518
- """ FRaMe Reject """
519
- DM_F = 0b000_1_11_11
520
- """ Disconnected Mode Final """
521
- DM = 0b000_0_11_11
522
- """ Disconnected Mode """
523
- RD_F = 0b010_1_00_11
524
- """ Request Disconnect Final. Solicitation for DISC Command """
525
- RD = 0b010_0_00_11
526
- """ Request Disconnect. Solicitation for DISC Command """
527
- RIM_F = 0b000_1_01_11
528
- """ Request initialization mode Final """
529
- RIM = 0b000_0_01_11
530
- """ Request initialization mode """
531
-
532
- def __add__(self, other):
533
- return Control(self.value + other)
534
-
535
- def __or__(self, other):
536
- return Control(self.value | other)
537
-
538
- def __and__(self, other):
539
- return Control(self.value & other)
540
-
541
- def __str__(self):
542
- return F'{_type[self.value & 0b11]} {self.name}'
543
-
544
- @classmethod
545
- def from_frame(cls, value: bytearray) -> Control:
546
- return Control(value.pop(0))
547
-
548
- @property
549
- def content(self) -> bytes:
550
- return pack('B', self.value)
551
-
552
- def is_info(self) -> bool:
553
- """ check by information type """
554
- return self.value & 0b1 == 0b0
555
-
556
- def is_information(self) -> bool:
557
- """ check by information in frame """
558
- return self.is_info() or self == self.UI or self == self.UI_PF
559
-
560
- def is_supervisory(self) -> bool:
561
- """ check by supervisory type """
562
- return self.value & 0b11 == 0b01
563
-
564
- def is_unnumbered(self) -> bool:
565
- """ check by unnumbered type """
566
- return self.value & 0b11 == 0b11
567
-
568
- def is_receive_ready(self) -> bool:
569
- return self.value & 0b1111 == 0b0001
570
-
571
- def is_receive_not_ready(self) -> bool:
572
- return self.value & 0b1111 == 0b0101
573
-
574
- def is_reject(self) -> bool:
575
- return self.value & 0b1111 == 0b1001
576
-
577
- def is_selective_reject(self) -> bool:
578
- return self.value & 0b1111 == 0b1101
579
-
580
- @cached_property
581
- def is_poll(self) -> bool:
582
- """ 5.4.3 Poll/final (P/F) bit """
583
- return True if not self.is_unnumbered() and bool(self.value & 0b000_1_00_00) else False
584
-
585
- @classmethod
586
- def next_send_sequence(cls, value: Control) -> Control:
587
- return Control(((value & 0xF0 | (value + 0x2) & 0xE) & 0xFF) & 0xFF)
588
- # value &= 0b1111111_0 # make info from other TODO: is it a gurux bug???
589
- # if value.is_info():
590
- # return Control(value & 0b11110001 | (value + 0x2) & 0b00001110)
591
- # else:
592
- # raise ValueError(F'Increase sender supporting only for information type, got {value}')
593
-
594
- @classmethod
595
- def next_receiver_sequence(cls, value: Control) -> Control:
596
- return Control(((value & 0xFF) + 0x20 | 0x10 | value & 0xE) & 0xFF)
597
- # if value.is_info() or value.is_supervisory():
598
- # return Control(value & 0b00011111 | 0x10 | (value + 0x20) & 0b11100000)
599
- # else:
600
- # raise ValueError(F'Increase sender supporting only for information and supervisory type, got {value}')
601
-
602
-
603
- _CCITT = (0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
604
- 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
605
- 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
606
- 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
607
- 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
608
- 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
609
- 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
610
- 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
611
- 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
612
- 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
613
- 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
614
- 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
615
- 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
616
- 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
617
- 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
618
- 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78)
619
-
620
-
621
- class CRC:
622
- __content: bytes
623
-
624
- def __init__(self, content: bytes = None,
625
- message: bytes = None):
626
- if content is not None:
627
- if len(content) != 2:
628
- raise ValueError(F'Wrong CRC length, must be 2, got {len(content)}')
629
- else:
630
- self.__content = content
631
- else:
632
- value = 0xFFFF
633
- for i in message:
634
- value = ((value >> 8) ^ _CCITT[(value ^ i) & 0xFF]) & 0xFFFF
635
- self.__content = pack('H', ~value & 0xFFFF)
636
-
637
- @classmethod
638
- def from_frame(cls, value: bytearray, message: bytes = None) -> CRC:
639
- new = cls(content=bytes(value[:2]))
640
- if message is not None and cls(message=message).content == new.content:
641
- del value[:2]
642
- return new
643
- else:
644
- raise ValueError('Wrong CRC')
645
-
646
- @property
647
- def content(self) -> bytes:
648
- return self.__content
649
-
650
- def __str__(self):
651
- return self.__content.hex(' ')
652
-
653
-
654
- class Info(ABC):
655
-
656
- @property
657
- @abstractmethod
658
- def content(self) -> bytes:
659
- """ return content in bytes """
660
-
661
- @abstractmethod
662
- def __len__(self):
663
- """ return content length """
664
-
665
- @property
666
- @abstractmethod
667
- def info(self) -> bytes:
668
- """ return information in bytes """
669
-
670
-
671
- class Frame:
672
- """ ISO/IEC 13239:2002(E), 4. In HDLC, all transmissions are in frames. Frames may be either in basic frame format or in non-basic frame format. Neither the basic nor the
673
- non-basic frame format structure includes bits inserted for bit-synchronization (i.e., start or stop elements see 4.3.2) or bits or octets inserted for transparency (see 4.3).
674
- Basic and non-basic frame formats can not be used simultaneously on the same media. See Clause 7.5 for the rules for negotiating from the basic frame format to the non-basic
675
- frame format. However, it is possible for different format types of the non-basic frame to exist simultaneously on the same media. """
676
- __FLAG_content: bytes = pack('B', _FLAG)
677
- __format: Format
678
- __destination_address: Address
679
- __source_address: Address
680
- __control: Control
681
- __hcs: CRC | None
682
- __info: bytes
683
- __fcs: CRC
684
-
685
- def __init__(self, content: bytearray = None,
686
- DA: Address = None,
687
- SA: Address = None,
688
- control: Control = None,
689
- info: bytes = None,
690
- is_segmentation: bool = None):
691
- if isinstance(content, bytearray):
692
- if content[0] != _FLAG:
693
- raise ValueError('Wrong start flag')
694
- self.__format = Format(bytes(content[1:3]))
695
- if self.__format.length + 2 > len(content): # 2 is length of flags(7e) in begin and end of frame
696
- raise NotEnoughDataError(F'Frame length not according by it data: got frame with length {len(content)}, but length field is {self.__format.length}')
697
- else:
698
- content.pop(0) # remove start flag
699
- if content[self.__format.length] != _FLAG:
700
- raise ValueError('Wrong length or HDLC end flag')
701
- else:
702
- remaining_frame_data: bytearray = content[2:self.__format.length]
703
- """ for parsing in part """
704
- self.__destination_address = Address.from_frame(remaining_frame_data)
705
- self.__source_address = Address.from_frame(remaining_frame_data)
706
- self.__control = Control.from_frame(remaining_frame_data)
707
- if len(remaining_frame_data) == 2: # info is absence
708
- self.__hcs = None
709
- self.__info = bytes()
710
- else:
711
- self.__hcs = CRC.from_frame(value=remaining_frame_data,
712
- message=self.__header_sequence)
713
- self.__info = bytes(remaining_frame_data[:-2])
714
- self.__fcs = CRC.from_frame(value=remaining_frame_data[-2:],
715
- message=self.__frame_sequence)
716
- del content[:self.__format.length]
717
- else:
718
- self.__destination_address = DA
719
- self.__source_address = SA
720
- self.__control = control
721
- self.__info = info
722
- # Frames that do not have an information field, e.g., as with some supervisory frames, or an information field of zero length do not contain an HCS and an FCS,
723
- # only an FCS. ISO/IEC 13239:2002(E), H.4 Frame format type 3. 7:5 = format + control + HCS? + FCS
724
- if len(self.__info) == 0:
725
- self.__format = Format(is_segmentation=is_segmentation,
726
- length=len(self.__destination_address) + len(self.__source_address) + 5)
727
- self.__hcs = None
728
- else:
729
- self.__format = Format(is_segmentation=is_segmentation,
730
- length=len(self.__destination_address) + len(self.__source_address) + len(self.__info) + 7)
731
- self.__hcs = CRC(message=self.__header_sequence)
732
- self.__fcs = CRC(message=self.__frame_sequence)
733
-
734
- def get_header(self) -> tuple[Address, Address]:
735
- """ return SA, DA for reusing """
736
- return self.__destination_address, self.__source_address
737
-
738
- # todo: make <parse> with Result
739
- @classmethod
740
- def try_from(cls, value: bytearray) -> Frame | None:
741
- """ Search of HDLC start flag and return Frame and value remains for next searching. If wrong frame when return value with out start flag for parsing """
742
- while len(value) != 0 and value[0] != _FLAG: # remove all bytes before flag
743
- value.pop(0)
744
- if len(value) < 9: # where 9 is min length of HDLC frame type-3
745
- return None
746
- else:
747
- try:
748
- return cls(value)
749
- except ValueError as e:
750
- print(F'Wrong Frame: {e.args[0]}')
751
- return None
752
- except NotEnoughDataError as e:
753
- # print(F'Frame Error: {e.args[0]}')
754
- return None
755
- except FormatDataError as e:
756
- print(F'Frame Error: {e.args[0]}')
757
- value.pop(0)
758
- return None
759
-
760
- @staticmethod
761
- def flag() -> int:
762
- """ return flag frame """
763
- return _FLAG
764
-
765
- @property
766
- def __header_sequence(self) -> bytes:
767
- return self.__format.content + self.__destination_address.content + self.__source_address.content + self.__control.content
768
-
769
- @property
770
- def __frame_sequence(self) -> bytes:
771
- if self.__hcs is None:
772
- return self.__header_sequence
773
- else:
774
- return self.__header_sequence + self.__hcs.content + self.__info
775
-
776
- @cached_property
777
- def content(self) -> bytes:
778
- return Frame.__FLAG_content + self.__frame_sequence + self.__fcs.content + Frame.__FLAG_content
779
-
780
- def __str__(self):
781
- return F'{self.__control.name} DA:{self.__destination_address} SA:{self.__source_address} {" Info["+str(len(self.__info))+"]:"+self.__info.hex(" ") if len(self.__info) != 0 else ""}'
782
-
783
- def __len__(self):
784
- return self.__format.length
785
-
786
- def is_for_me(self, DA: Address, SA: Address) -> bool:
787
- """ compare by DA and SA received frame"""
788
- return DA == self.__source_address and SA == self.__destination_address
789
-
790
- @property
791
- def control(self):
792
- return self.__control
793
-
794
- @cached_property
795
- def is_segmentation(self) -> bool:
796
- return self.__format.is_segmentation
797
-
798
- @property
799
- def info(self) -> bytes:
800
- return self.__info
801
-
802
- def is_next(self, other: Frame) -> bool:
803
- """ return TRUE if frame is next information frame of current. Other must be previous. """
804
- return self.__control == Control.next_send_sequence(Control.next_receiver_sequence(other.control))
805
-
806
- def is_next_send(self, other: Frame) -> bool:
807
- """ return TRUE if frame is next information frame of current. Other must be previous. """
808
- return self.__control == Control.next_send_sequence(other.control)
809
-
810
- @staticmethod
811
- def join_info(frames: Deque[Frame]) -> bytearray:
812
- """ TODO: """
813
- while len(frames) != 0:
814
- frame: Frame = frames.popleft()
815
- if frame.control.is_info():
816
- info: bytearray = bytearray(frame.info)
817
- break
818
- else:
819
- print(F'Frame {frame} not handled and deleted')
820
- else:
821
- raise ValueError('Not found information Frame')
822
- while frame.is_segmentation:
823
- if len(frames) == 0:
824
- raise ValueError('Not found end information Frame')
825
- else:
826
- next_frame: Frame = frames.popleft()
827
- if next_frame.control.is_info() and next_frame.is_next_send(frame):
828
- info.extend(next_frame.info)
829
- frame = next_frame
830
- else:
831
- print(F'Frame {frame} not handled and deleted')
832
- return info
833
-
834
-
835
- if __name__ == '__main__':
836
- ad1 = Address(upper_address=0x3f,
837
- lower_address=1)
838
- ad2 = Address(upper_address=0x3f,
839
- lower_address=1)
840
- print(ad1)
841
- comp = ad1 == ad2
842
- comp2 = ad1 is ad2
843
- a = Frame(upper_destination_address=0x3f,
844
- upper_source_address=1,
845
- control=Control(0x10),
846
- info=bytes(),
847
- is_segmentation=False)
848
- head = a.get_header()
849
- a1 = Frame(upper_destination_address=0x3,
850
- upper_source_address=10,
851
- control=Control(0x10),
852
- info=bytes(),
853
- is_segmentation=False,
854
- header=head
855
- )
856
- comp3 = a1.is_for_me(head)
857
- print(a)
858
- # a1 = Frame(upper_destination_address=0x3f,
859
- # upper_source_address=1,
860
- # control=Control(0x32),
861
- # info=bytes(),
862
- # is_segmentation=False)
863
- # print(a1)
864
- # print(a1.is_next(a))
865
-
866
- # data = bytearray.fromhex('7e a0 38 21 02 21 30 84 d4 e6 e7 00 61 29 a1 09 06 07 60 85 74 05 08 01 01 a2 03 02 01 00 a3 05 a1 03 02 01 00 be 10 04 0e 08 00 06 5f 1f 04 00 00 18 18 04 00 00 07 4e 98 7e')
867
- # data = bytearray(b'~~\xa0\x1f!\x02!sV\xf4\x81\x80\x12\x05\x01\x80\x06\x01\x80\x07\x04\x00\x00\x00\x01\x08\x04\x00\x00\x00\x01S;~\xa0\x1f!\x02!sV\xf4\x81\x80\x12\x05\x01\x80\x06\x01\x80\x07\x04\x00\x00\x00\x01\x08\x04\x00\x00\x00\x01S;~')
868
- # data = bytearray.fromhex('7e a8 87 21 02 21 7a fa 2c 07 e4 01 01 03 02 1e ff ff 80 00 00 15 00 00 00 00 db 69 14 81 15 00 00 00 00 00 49 8b f0 15 00 00 00 00 08 99 89 25 15 00 00 00 00 07 a1 9a 16 15 00 00 00 00 00 b2 3e cb 15 00 00 00 00 00 00 00 00 15 00 00 00 00 00 03 7e')
869
- data = bytearray.fromhex('7E A8 01 41 02 21 52 99 A9 E6 E7 00 C4 02 C1 00 00 00 00 01 00 82 02 EA 01 81 9F 02 04 12 00 0F 11 01 09 06 00 00 28 00 00 FF 02 02 01 09 02 03 0F 01 16 01 00 02 03 0F 02 16 01 00 02 03 0F 03 16 01 00 02 03 0F 04 16 01 00 02 03 0F 05 16 01 00 02 03 0F 06 16 01 00 02 03 0F 07 16 01 00 02 03 0F 08 16 01 00 02 03 0F 09 16 01 00 01 04 02 02 0F 01 16 00 02 02 0F 02 16 00 02 02 0F 03 16 00 02 02 0F 04 16 00 02 04 12 00 08 11 00 09 06 00 00 01 00 00 FF 02 02 01 09 02 03 0F 01 16 01 00 02 03 0F 02 16 01 00 02 03 0F 03 16 01 00 02 03 0F 04 16 01 00 02 03 0F 05 16 01 00 02 03 0F 06 16 01 00 02 03 0F 07 16 01 00 02 03 0F 08 16 01 00 02 03 0F 09 16 01 00 01 06 02 02 0F 01 16 01 02 02 0F 02 16 01 02 02 0F 03 16 01 02 02 0F 04 16 01 02 02 0F 05 16 01 02 02 0F 06 16 01 02 1F FC 7E')
870
- # data = bytearray(b'~\xa8\x87!\x02!\x96\x98\x01\xe6\xe7\x00\xc4\x01\xc1\x00\x01\n\x02\t\t\x0c\x07\xe5\x08\x06\x05\x0b\x1e\xff\xff\x80\x00\x00\x15\x00\x00\x00\x00\xda\x85\x9e~')
871
- # data = bytearray(b'~\xa8\x89!\x03\x96\xae)\xe6\xe7\x00\xc4\x01\xc1\x00\x01\x07\x02\x02\x11\x00\x01\x05\x02\x03\t\x04\x00\x00\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x01\x02\x03\t\x04\x01\x00\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x02\x02\x03\t\x04\x0c\x17\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x04\x02\x03\t\x04\x16\x1e\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x04\x02\x03\t\x04\x17\x1e\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x03\x02\x02\x11\x01\x01\x01\x02\x03\t\x04\x01\x00\x00\xff\t\x06\x00\x00\x19Q~')
872
- frame1 = Frame.try_from(data)
873
- print(frame1)
874
- a = Control.SNRM_P
875
- print(a)
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from struct import unpack, pack
4
+ from functools import cached_property
5
+ from typing import Deque
6
+ from enum import IntFlag
7
+
8
+ _FLAG: int = 0x7e
9
+
10
+
11
+ class NotEnoughDataError(Exception):
12
+ """ Not enough data received, need more for parse full Frame """
13
+
14
+
15
+ class FormatDataError(Exception):
16
+ """ Frame format is not Type 3 HDLC """
17
+
18
+
19
+ class Format:
20
+ """ This optional field is present only when using the non-basic frame format. When present, it follows the opening flag sequence. The frame format field is 2 octets in length
21
+ and consists of three subfields referred to as the format type subfield, the segmentation subfield and the frame length subfield. The format of the frame format field is as
22
+ follows, ISO/IEC 13239:2002(E), 4.9 Frame format field, Type 3 :
23
+ Type(4 bits) - Segmentation(1 bit) - Length(11 bit) """
24
+ __content: bytes
25
+
26
+ def __init__(self, content: bytes = None,
27
+ is_segmentation: bool = None,
28
+ length: int = None):
29
+ if content is not None:
30
+ if len(content) != 2:
31
+ raise ValueError(F'Wrong length Frame format type, must be 2, got {len(content)}')
32
+ else:
33
+ self.__content = content
34
+ if self.type != 0xA:
35
+ raise FormatDataError(F'Frame format type not according HDLC Type 3, must be 0xA, got {hex(self.type)}')
36
+ else:
37
+ if length.bit_length() <= 13:
38
+ value = length
39
+ if is_segmentation:
40
+ value |= 0b1010_1_00000000000
41
+ else:
42
+ value |= 0b1010_0_00000000000
43
+ self.__content = pack('>H', value)
44
+ else:
45
+ raise ValueError(F'Frame length overflow, max be 2048, got {length}')
46
+
47
+ @property
48
+ def content(self) -> bytes:
49
+ return self.__content
50
+
51
+ @cached_property
52
+ def type(self) -> int:
53
+ """ Must be 0b1010 in first 4 bits """
54
+ return self.__content[0] >> 4
55
+
56
+ @cached_property
57
+ def length(self) -> int:
58
+ """ return length of frame. Mask 11bit. """
59
+ return unpack('>H', self.__content)[0] & 0b0000_0_111_11111111
60
+
61
+ @cached_property
62
+ def is_segmentation(self) -> bool:
63
+ return bool(self.__content[0] & 0b00001000)
64
+
65
+ def __str__(self):
66
+ return F'Type 3: length-{self.length} {"segmentation" if self.is_segmentation else ""}'
67
+
68
+
69
+ class AddressLength(int):
70
+ AUTO = -1
71
+ __VALID_VALUES = (AUTO, 1, 2, 4)
72
+
73
+ def __new__(cls, *args, **kwargs):
74
+ if args[0] not in cls.__VALID_VALUES:
75
+ raise ValueError(F"got {cls.__name__}={args[0]}, expected {cls.__VALID_VALUES}")
76
+ return super().__new__(cls, *args)
77
+
78
+ @classmethod
79
+ def from_str(cls, value: str):
80
+ if value == "AUTO":
81
+ value = -1
82
+ return cls(int(value))
83
+
84
+ def __str__(self):
85
+ if self == -1:
86
+ return "AUTO"
87
+ else:
88
+ return super().__str__()
89
+
90
+ @classmethod
91
+ def get_values(cls):
92
+ return tuple(str(cls(it)) for it in cls.__VALID_VALUES)
93
+
94
+
95
+ class Address:
96
+ """content: if not None then init from content
97
+ upper_address, lower_address: set address according
98
+ length: forcedly installing length
99
+ """
100
+ __content: bytes
101
+ __MASK: int = 0x3f80
102
+
103
+ def __init__(self, content: bytes = None,
104
+ upper_address: int = None,
105
+ lower_address: int = None,
106
+ length: AddressLength = AddressLength.AUTO):
107
+ if content is not None:
108
+ if len(content) not in (1, 2, 4):
109
+ raise ValueError(F"got length Frame from content {len(content)}, expected 1, 2 or 4")
110
+ else:
111
+ self.__content = content
112
+ else:
113
+ # calculate length
114
+ if not lower_address:
115
+ if (upper_address & self.__MASK) == 0:
116
+ length2 = 1
117
+ else:
118
+ lower_address = 0
119
+ length2 = 4
120
+ else:
121
+ if ((upper_address | lower_address) & self.__MASK) == 0:
122
+ length2 = 2
123
+ else:
124
+ length2 = 4
125
+ if length == AddressLength.AUTO:
126
+ length = length2
127
+ elif length >= length2:
128
+ """setting more or equally is OK"""
129
+ else:
130
+ raise ValueError(F"got {length=}, but with {lower_address=}, {upper_address=} it's not possible")
131
+ if length == 1:
132
+ self.__content = pack('B',
133
+ upper_address << 1 | 1)
134
+ elif lower_address is not None:
135
+ if length == 2:
136
+ self.__content = pack("BB",
137
+ upper_address << 1,
138
+ lower_address << 1 | 1)
139
+ if length == 4:
140
+ self.__content = pack("BBBB",
141
+ upper_address >> 6 & 0b11111110,
142
+ upper_address << 1 & 0b11111110,
143
+ lower_address >> 6 & 0b11111110,
144
+ lower_address << 1 & 0b11111110 | 1)
145
+ else:
146
+ raise ValueError(F"got {length=}, but lower address is absense, expected (0..16383)")
147
+
148
+ @classmethod
149
+ def from_frame(cls, value: bytearray) -> Address:
150
+ for it in (0, 1, 3):
151
+ if value[it] % 2 == 1:
152
+ match it:
153
+ case 0: new = cls(bytes(value[:1])); break
154
+ case 1: new = cls(bytes(value[:2])); break
155
+ case 3: new = cls(bytes(value[:4])); break
156
+ else:
157
+ raise ValueError('HDLC source address wrong, not found end bit')
158
+ del value[:len(new)]
159
+ return new
160
+
161
+ @property
162
+ def content(self) -> bytes:
163
+ return self.__content
164
+
165
+ def __eq__(self, other: Address):
166
+ return self.__content == other.content
167
+
168
+ @cached_property
169
+ def upper(self) -> int:
170
+ """ return of upper address with int type """
171
+ if len(self.__content) in (1, 2):
172
+ return self.__content[0] >> 1
173
+ else:
174
+ return (self.__content[0] >> 1)*128 + (self.__content[1] >> 1)
175
+
176
+ @cached_property
177
+ def lower(self) -> int | None:
178
+ """ return of lower address with int type """
179
+ if len(self.__content) == 1:
180
+ return None
181
+ elif len(self.__content) == 2:
182
+ return self.__content[1] >> 1
183
+ else:
184
+ return (self.__content[2] >> 1)*128 + (self.__content[3] >> 1)
185
+
186
+ def __str__(self):
187
+ return F'{self.upper}{"/"+str(self.lower) if self.lower is not None else ""}'
188
+
189
+ def __len__(self):
190
+ return len(self.__content)
191
+
192
+ def __hash__(self):
193
+ return int.from_bytes(self.__content, "big")
194
+
195
+ def __repr__(self):
196
+ return F"{self.__class__.__name__}(upper_address={self.upper}, lower_address={self.lower})"
197
+
198
+
199
+ _type = ['Information', 'Supervisory', 'Information', 'Unnumbered']
200
+
201
+
202
+ class Control(IntFlag):
203
+ """ ISO/IEC 13239:2002(E). P/F = poll bit -- primary station or combined station command frame transmissions/final bit -- secondary station or combined station response
204
+ frame transmissions (1 = poll/final) """
205
+ # Information transfer command/ response (I format):
206
+ # 1 2 3 4 5 6 7 8
207
+ # 0 | N(S) | P/F | N(R)
208
+ S0_R0 = 0b000_0_000_0
209
+ S1_R0 = 0b000_0_001_0
210
+ S2_R0 = 0b000_0_010_0
211
+ S3_R0 = 0b000_0_011_0
212
+ S4_R0 = 0b000_0_100_0
213
+ S5_R0 = 0b000_0_101_0
214
+ S6_R0 = 0b000_0_110_0
215
+ S7_R0 = 0b000_0_111_0
216
+
217
+ S0_R1 = 0b001_0_000_0
218
+ S1_R1 = 0b001_0_001_0
219
+ S2_R1 = 0b001_0_010_0
220
+ S3_R1 = 0b001_0_011_0
221
+ S4_R1 = 0b001_0_100_0
222
+ S5_R1 = 0b001_0_101_0
223
+ S6_R1 = 0b001_0_110_0
224
+ S7_R1 = 0b001_0_111_0
225
+
226
+ S0_R2 = 0b010_0_000_0
227
+ S1_R2 = 0b010_0_001_0
228
+ S2_R2 = 0b010_0_010_0
229
+ S3_R2 = 0b010_0_011_0
230
+ S4_R2 = 0b010_0_100_0
231
+ S5_R2 = 0b010_0_101_0
232
+ S6_R2 = 0b010_0_110_0
233
+ S7_R2 = 0b010_0_111_0
234
+
235
+ S0_R3 = 0b011_0_000_0
236
+ S1_R3 = 0b011_0_001_0
237
+ S2_R3 = 0b011_0_010_0
238
+ S3_R3 = 0b011_0_011_0
239
+ S4_R3 = 0b011_0_100_0
240
+ S5_R3 = 0b011_0_101_0
241
+ S6_R3 = 0b011_0_110_0
242
+ S7_R3 = 0b011_0_111_0
243
+
244
+ S0_R4 = 0b100_0_000_0
245
+ S1_R4 = 0b100_0_001_0
246
+ S2_R4 = 0b100_0_010_0
247
+ S3_R4 = 0b100_0_011_0
248
+ S4_R4 = 0b100_0_100_0
249
+ S5_R4 = 0b100_0_101_0
250
+ S6_R4 = 0b100_0_110_0
251
+ S7_R4 = 0b100_0_111_0
252
+
253
+ S0_R5 = 0b101_0_000_0
254
+ S1_R5 = 0b101_0_001_0
255
+ S2_R5 = 0b101_0_010_0
256
+ S3_R5 = 0b101_0_011_0
257
+ S4_R5 = 0b101_0_100_0
258
+ S5_R5 = 0b101_0_101_0
259
+ S6_R5 = 0b101_0_110_0
260
+ S7_R5 = 0b101_0_111_0
261
+
262
+ S0_R6 = 0b110_0_000_0
263
+ S1_R6 = 0b110_0_001_0
264
+ S2_R6 = 0b110_0_010_0
265
+ S3_R6 = 0b110_0_011_0
266
+ S4_R6 = 0b110_0_100_0
267
+ S5_R6 = 0b110_0_101_0
268
+ S6_R6 = 0b110_0_110_0
269
+ S7_R6 = 0b110_0_111_0
270
+
271
+ S0_R7 = 0b111_0_000_0
272
+ S1_R7 = 0b111_0_001_0
273
+ S2_R7 = 0b111_0_010_0
274
+ S3_R7 = 0b111_0_011_0
275
+ S4_R7 = 0b111_0_100_0
276
+ S5_R7 = 0b111_0_101_0
277
+ S6_R7 = 0b111_0_110_0
278
+ S7_R7 = 0b111_0_111_0
279
+
280
+ S0_R0_PF = 0b000_1_000_0
281
+ S1_R0_PF = 0b000_1_001_0
282
+ S2_R0_PF = 0b000_1_010_0
283
+ S3_R0_PF = 0b000_1_011_0
284
+ S4_R0_PF = 0b000_1_100_0
285
+ S5_R0_PF = 0b000_1_101_0
286
+ S6_R0_PF = 0b000_1_110_0
287
+ S7_R0_PF = 0b000_1_111_0
288
+
289
+ S0_R1_PF = 0b001_1_000_0
290
+ S1_R1_PF = 0b001_1_001_0
291
+ S2_R1_PF = 0b001_1_010_0
292
+ S3_R1_PF = 0b001_1_011_0
293
+ S4_R1_PF = 0b001_1_100_0
294
+ S5_R1_PF = 0b001_1_101_0
295
+ S6_R1_PF = 0b001_1_110_0
296
+ S7_R1_PF = 0b001_1_111_0
297
+
298
+ S0_R2_PF = 0b010_1_000_0
299
+ S1_R2_PF = 0b010_1_001_0
300
+ S2_R2_PF = 0b010_1_010_0
301
+ S3_R2_PF = 0b010_1_011_0
302
+ S4_R2_PF = 0b010_1_100_0
303
+ S5_R2_PF = 0b010_1_101_0
304
+ S6_R2_PF = 0b010_1_110_0
305
+ S7_R2_PF = 0b010_1_111_0
306
+
307
+ S0_R3_PF = 0b011_1_000_0
308
+ S1_R3_PF = 0b011_1_001_0
309
+ S2_R3_PF = 0b011_1_010_0
310
+ S3_R3_PF = 0b011_1_011_0
311
+ S4_R3_PF = 0b011_1_100_0
312
+ S5_R3_PF = 0b011_1_101_0
313
+ S6_R3_PF = 0b011_1_110_0
314
+ S7_R3_PF = 0b011_1_111_0
315
+
316
+ S0_R4_PF = 0b100_1_000_0
317
+ S1_R4_PF = 0b100_1_001_0
318
+ S2_R4_PF = 0b100_1_010_0
319
+ S3_R4_PF = 0b100_1_011_0
320
+ S4_R4_PF = 0b100_1_100_0
321
+ S5_R4_PF = 0b100_1_101_0
322
+ S6_R4_PF = 0b100_1_110_0
323
+ S7_R4_PF = 0b100_1_111_0
324
+
325
+ S0_R5_PF = 0b101_1_000_0
326
+ S1_R5_PF = 0b101_1_001_0
327
+ S2_R5_PF = 0b101_1_010_0
328
+ S3_R5_PF = 0b101_1_011_0
329
+ S4_R5_PF = 0b101_1_100_0
330
+ S5_R5_PF = 0b101_1_101_0
331
+ S6_R5_PF = 0b101_1_110_0
332
+ S7_R5_PF = 0b101_1_111_0
333
+
334
+ S0_R6_PF = 0b110_1_000_0
335
+ S1_R6_PF = 0b110_1_001_0
336
+ S2_R6_PF = 0b110_1_010_0
337
+ S3_R6_PF = 0b110_1_011_0
338
+ S4_R6_PF = 0b110_1_100_0
339
+ S5_R6_PF = 0b110_1_101_0
340
+ S6_R6_PF = 0b110_1_110_0
341
+ S7_R6_PF = 0b110_1_111_0
342
+
343
+ S0_R7_PF = 0b111_1_000_0
344
+ S1_R7_PF = 0b111_1_001_0
345
+ S2_R7_PF = 0b111_1_010_0
346
+ S3_R7_PF = 0b111_1_011_0
347
+ S4_R7_PF = 0b111_1_100_0
348
+ S5_R7_PF = 0b111_1_101_0
349
+ S6_R7_PF = 0b111_1_110_0
350
+ S7_R7_PF = 0b111_1_111_0
351
+ # Supervisory commands/ responses (S format): S = supervisory function bit
352
+ # 1 2 3 4 5 6 7 8
353
+ # 1 0 S S P/F | N(R)
354
+ RR_R0 = 0b000_0_00_01
355
+ """ Receive ready sequence=0 """
356
+ RR_R1 = 0b001_0_00_01
357
+ """ Receive ready sequence=1 """
358
+ RR_R2 = 0b010_0_00_01
359
+ """ Receive ready sequence=2 """
360
+ RR_R3 = 0b011_0_00_01
361
+ """ Receive ready sequence=3 """
362
+ RR_R4 = 0b100_0_00_01
363
+ """ Receive ready sequence=4 """
364
+ RR_R5 = 0b101_0_00_01
365
+ """ Receive ready sequence=5 """
366
+ RR_R6 = 0b110_0_00_01
367
+ """ Receive ready sequence=6 """
368
+ RR_R7 = 0b111_0_00_01
369
+ """ Receive ready sequence=7 """
370
+
371
+ RR_R0_PF = 0b000_1_00_01
372
+ """ Receive ready sequence=0 """
373
+ RR_R1_PF = 0b001_1_00_01
374
+ """ Receive ready sequence=1 """
375
+ RR_R2_PF = 0b010_1_00_01
376
+ """ Receive ready sequence=2 """
377
+ RR_R3_PF = 0b011_1_00_01
378
+ """ Receive ready sequence=3 """
379
+ RR_R4_PF = 0b100_1_00_01
380
+ """ Receive ready sequence=4 """
381
+ RR_R5_PF = 0b101_1_00_01
382
+ """ Receive ready sequence=5 """
383
+ RR_R6_PF = 0b110_1_00_01
384
+ """ Receive ready sequence=6 """
385
+ RR_R7_PF = 0b111_1_00_01
386
+ """ Receive ready sequence=7 """
387
+
388
+ RNR_R0 = 0b000_0_01_01
389
+ RNR_R1 = 0b001_0_01_01
390
+ RNR_R2 = 0b010_0_01_01
391
+ RNR_R3 = 0b011_0_01_01
392
+ RNR_R4 = 0b100_0_01_01
393
+ RNR_R5 = 0b101_0_01_01
394
+ RNR_R6 = 0b110_0_01_01
395
+ RNR_R7 = 0b111_0_01_01
396
+
397
+ RNR_R0_PF = 0b000_1_01_01
398
+ RNR_R1_PF = 0b001_1_01_01
399
+ RNR_R2_PF = 0b010_1_01_01
400
+ RNR_R3_PF = 0b011_1_01_01
401
+ RNR_R4_PF = 0b100_1_01_01
402
+ RNR_R5_PF = 0b101_1_01_01
403
+ RNR_R6_PF = 0b110_1_01_01
404
+ RNR_R7_PF = 0b111_1_01_01
405
+
406
+ REJ_R0 = 0b000_0_10_01
407
+ REJ_R1 = 0b001_0_10_01
408
+ REJ_R2 = 0b010_0_10_01
409
+ REJ_R3 = 0b011_0_10_01
410
+ REJ_R4 = 0b100_0_10_01
411
+ REJ_R5 = 0b101_0_10_01
412
+ REJ_R6 = 0b110_0_10_01
413
+ REJ_R7 = 0b111_0_10_01
414
+
415
+ REJ_R0_PF = 0b000_1_10_01
416
+ REJ_R1_PF = 0b001_1_10_01
417
+ REJ_R2_PF = 0b010_1_10_01
418
+ REJ_R3_PF = 0b011_1_10_01
419
+ REJ_R4_PF = 0b100_1_10_01
420
+ REJ_R5_PF = 0b101_1_10_01
421
+ REJ_R6_PF = 0b110_1_10_01
422
+ REJ_R7_PF = 0b111_1_10_01
423
+
424
+ SREJ_R0 = 0b000_0_11_01
425
+ SREJ_R1 = 0b001_0_11_01
426
+ SREJ_R2 = 0b010_0_11_01
427
+ SREJ_R3 = 0b011_0_11_01
428
+ SREJ_R4 = 0b100_0_11_01
429
+ SREJ_R5 = 0b101_0_11_01
430
+ SREJ_R6 = 0b110_0_11_01
431
+ SREJ_R7 = 0b111_0_11_01
432
+
433
+ SREJ_R0_PF = 0b000_1_11_01
434
+ SREJ_R1_PF = 0b001_1_11_01
435
+ SREJ_R2_PF = 0b010_1_11_01
436
+ SREJ_R3_PF = 0b011_1_11_01
437
+ SREJ_R4_PF = 0b100_1_11_01
438
+ SREJ_R5_PF = 0b101_1_11_01
439
+ SREJ_R6_PF = 0b110_1_11_01
440
+ SREJ_R7_PF = 0b111_1_11_01
441
+
442
+ # Unnumbered commands/ responses (U format): M = modifier function bit
443
+ # 11_MM_P/F_MMM
444
+ UI_PF = 0b000_1_00_11
445
+ """ Unnumbered Information with Poll """
446
+ UI = 0b000_0_00_11
447
+ """ Unnumbered Information with wait """
448
+ XID_PF = 0b101_1_11_11
449
+ """ Exchange identification with Poll. Used to Request/Report capabilities """
450
+ XID = 0b101_0_11_11
451
+ """ Exchange identification with wait. Used to Request/Report capabilities """
452
+ TEST_PF = 0b111_1_00_11
453
+ """ TEST with Poll. Exchange identical information fields for testing """
454
+ TEST = 0b111_0_00_11
455
+ """ TEST with wait. Exchange identical information fields for testing """
456
+ UIH_PF = 0b111_1_11_11
457
+ """ Unnumbered Information with Header check with Poll """
458
+ UIH = 0b111_0_11_11
459
+ """ Unnumbered Information with Header check with wait """
460
+
461
+ # command ISO/IEC 13239:2002(E) 5.5.3.3
462
+ # 11_MM_P_MMM
463
+ SNRM_P = 0b100_1_00_11
464
+ """ Set Normal Response Mode with Poll """
465
+ SNRM = 0b100_0_00_11
466
+ """ Set Normal Response Mode with wait """
467
+ SARM_P = 0b000_1_11_11
468
+ """ Set Asynchronous Response Mode with Poll """
469
+ SARM = 0b000_0_11_11
470
+ """ Set Asynchronous Response with wait """
471
+ SABM_P = 0b001_1_11_11
472
+ """ Set Asynchronous Balanced Mode with Poll """
473
+ SABM = 0b001_0_11_11
474
+ """ Set Asynchronous Balanced with wait """
475
+ DISC_P = 0b010_1_00_11
476
+ """ Disconnect with Poll """
477
+ DISC = 0b010_0_00_11
478
+ """ Disconnect with wait """
479
+ SNRME_P = 0b110_1_11_11
480
+ """ Set Normal Response Mode Extended with Poll """
481
+ SNRME = 0b110_0_11_11
482
+ """ Set Normal Response Mode Extended with wait """
483
+ SARME_P = 0b010_1_11_11
484
+ """ Set Asynchronous Response Mode Extended with Poll """
485
+ SARME = 0b010_0_11_11
486
+ """ Set Asynchronous Response Mode Extended with wait """
487
+ SABME_P = 0b011_1_11_11
488
+ """ Set Asynchronous Balanced Mode Extended with Poll """
489
+ SABME = 0b011_0_11_11
490
+ """ Set Asynchronous Balanced Mode Extended with wait """
491
+ UP_P = 0b001_1_00_11
492
+ """ Unnumbered Poll with Poll. Used to solicit control information"""
493
+ UP = 0b001_0_00_11
494
+ """ Unnumbered Poll with wait. Used to solicit control information"""
495
+ SIM_P = 0b000_1_01_11
496
+ """ Set Initialization Mode with Poll """
497
+ SIM = 0b000_0_01_11
498
+ """ Set Initialization Mode with wait """
499
+ SM_P = 0b110_1_00_11
500
+ """ Set Mode with Poll """
501
+ SM = 0b110_0_00_11
502
+ """ Set Mode with wait """
503
+ RSET_P = 0b100_1_11_11
504
+ """ ReSET with Poll. Used for recovery. Resets N(R) but not N(S) """
505
+ RSET = 0b100_0_11_11
506
+ """ ReSET with wait. Used for recovery. Resets N(R) but not N(S) """
507
+
508
+
509
+ # responses ISO/IEC 13239:2002(E) 5.5.3.4.
510
+ # 11_MM_F_MMM
511
+ UA_F = 0b011_1_00_11
512
+ """ Unnumbered Acknowledgement Final """
513
+ UA = 0b011_0_00_11
514
+ """ Unnumbered Acknowledgement """
515
+ FRMR_F = 0b100_1_01_11
516
+ """ FRaMe Reject Final """
517
+ FRMR = 0b100_0_01_11
518
+ """ FRaMe Reject """
519
+ DM_F = 0b000_1_11_11
520
+ """ Disconnected Mode Final """
521
+ DM = 0b000_0_11_11
522
+ """ Disconnected Mode """
523
+ RD_F = 0b010_1_00_11
524
+ """ Request Disconnect Final. Solicitation for DISC Command """
525
+ RD = 0b010_0_00_11
526
+ """ Request Disconnect. Solicitation for DISC Command """
527
+ RIM_F = 0b000_1_01_11
528
+ """ Request initialization mode Final """
529
+ RIM = 0b000_0_01_11
530
+ """ Request initialization mode """
531
+
532
+ def __add__(self, other):
533
+ return Control(self.value + other)
534
+
535
+ def __or__(self, other):
536
+ return Control(self.value | other)
537
+
538
+ def __and__(self, other):
539
+ return Control(self.value & other)
540
+
541
+ def __str__(self):
542
+ return F'{_type[self.value & 0b11]} {self.name}'
543
+
544
+ @classmethod
545
+ def from_frame(cls, value: bytearray) -> Control:
546
+ return Control(value.pop(0))
547
+
548
+ @property
549
+ def content(self) -> bytes:
550
+ return pack('B', self.value)
551
+
552
+ def is_info(self) -> bool:
553
+ """ check by information type """
554
+ return self.value & 0b1 == 0b0
555
+
556
+ def is_information(self) -> bool:
557
+ """ check by information in frame """
558
+ return self.is_info() or self == self.UI or self == self.UI_PF
559
+
560
+ def is_supervisory(self) -> bool:
561
+ """ check by supervisory type """
562
+ return self.value & 0b11 == 0b01
563
+
564
+ def is_unnumbered(self) -> bool:
565
+ """ check by unnumbered type """
566
+ return self.value & 0b11 == 0b11
567
+
568
+ def is_receive_ready(self) -> bool:
569
+ return self.value & 0b1111 == 0b0001
570
+
571
+ def is_receive_not_ready(self) -> bool:
572
+ return self.value & 0b1111 == 0b0101
573
+
574
+ def is_reject(self) -> bool:
575
+ return self.value & 0b1111 == 0b1001
576
+
577
+ def is_selective_reject(self) -> bool:
578
+ return self.value & 0b1111 == 0b1101
579
+
580
+ @cached_property
581
+ def is_poll(self) -> bool:
582
+ """ 5.4.3 Poll/final (P/F) bit """
583
+ return True if not self.is_unnumbered() and bool(self.value & 0b000_1_00_00) else False
584
+
585
+ @classmethod
586
+ def next_send_sequence(cls, value: Control) -> Control:
587
+ return Control(((value & 0xF0 | (value + 0x2) & 0xE) & 0xFF) & 0xFF)
588
+ # value &= 0b1111111_0 # make info from other TODO: is it a gurux bug???
589
+ # if value.is_info():
590
+ # return Control(value & 0b11110001 | (value + 0x2) & 0b00001110)
591
+ # else:
592
+ # raise ValueError(F'Increase sender supporting only for information type, got {value}')
593
+
594
+ @classmethod
595
+ def next_receiver_sequence(cls, value: Control) -> Control:
596
+ return Control(((value & 0xFF) + 0x20 | 0x10 | value & 0xE) & 0xFF)
597
+ # if value.is_info() or value.is_supervisory():
598
+ # return Control(value & 0b00011111 | 0x10 | (value + 0x20) & 0b11100000)
599
+ # else:
600
+ # raise ValueError(F'Increase sender supporting only for information and supervisory type, got {value}')
601
+
602
+
603
+ _CCITT = (0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
604
+ 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
605
+ 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
606
+ 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
607
+ 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
608
+ 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
609
+ 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
610
+ 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
611
+ 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
612
+ 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
613
+ 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
614
+ 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
615
+ 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
616
+ 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
617
+ 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
618
+ 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78)
619
+
620
+
621
+ class CRC:
622
+ __content: bytes
623
+
624
+ def __init__(self, content: bytes = None,
625
+ message: bytes = None):
626
+ if content is not None:
627
+ if len(content) != 2:
628
+ raise ValueError(F'Wrong CRC length, must be 2, got {len(content)}')
629
+ else:
630
+ self.__content = content
631
+ else:
632
+ value = 0xFFFF
633
+ for i in message:
634
+ value = ((value >> 8) ^ _CCITT[(value ^ i) & 0xFF]) & 0xFFFF
635
+ self.__content = pack('H', ~value & 0xFFFF)
636
+
637
+ @classmethod
638
+ def from_frame(cls, value: bytearray, message: bytes = None) -> CRC:
639
+ new = cls(content=bytes(value[:2]))
640
+ if message is not None and cls(message=message).content == new.content:
641
+ del value[:2]
642
+ return new
643
+ else:
644
+ raise ValueError('Wrong CRC')
645
+
646
+ @property
647
+ def content(self) -> bytes:
648
+ return self.__content
649
+
650
+ def __str__(self):
651
+ return self.__content.hex(' ')
652
+
653
+
654
+ class Info(ABC):
655
+
656
+ @property
657
+ @abstractmethod
658
+ def content(self) -> bytes:
659
+ """ return content in bytes """
660
+
661
+ @abstractmethod
662
+ def __len__(self):
663
+ """ return content length """
664
+
665
+ @property
666
+ @abstractmethod
667
+ def info(self) -> bytes:
668
+ """ return information in bytes """
669
+
670
+
671
+ class Frame:
672
+ """ ISO/IEC 13239:2002(E), 4. In HDLC, all transmissions are in frames. Frames may be either in basic frame format or in non-basic frame format. Neither the basic nor the
673
+ non-basic frame format structure includes bits inserted for bit-synchronization (i.e., start or stop elements see 4.3.2) or bits or octets inserted for transparency (see 4.3).
674
+ Basic and non-basic frame formats can not be used simultaneously on the same media. See Clause 7.5 for the rules for negotiating from the basic frame format to the non-basic
675
+ frame format. However, it is possible for different format types of the non-basic frame to exist simultaneously on the same media. """
676
+ __FLAG_content: bytes = pack('B', _FLAG)
677
+ __format: Format
678
+ __destination_address: Address
679
+ __source_address: Address
680
+ __control: Control
681
+ __hcs: CRC | None
682
+ __info: bytes
683
+ __fcs: CRC
684
+
685
+ def __init__(self, content: bytearray = None,
686
+ DA: Address = None,
687
+ SA: Address = None,
688
+ control: Control = None,
689
+ info: bytes = None,
690
+ is_segmentation: bool = None):
691
+ if isinstance(content, bytearray):
692
+ if content[0] != _FLAG:
693
+ raise ValueError('Wrong start flag')
694
+ self.__format = Format(bytes(content[1:3]))
695
+ if self.__format.length + 2 > len(content): # 2 is length of flags(7e) in begin and end of frame
696
+ raise NotEnoughDataError(F'Frame length not according by it data: got frame with length {len(content)}, but length field is {self.__format.length}')
697
+ else:
698
+ content.pop(0) # remove start flag
699
+ if content[self.__format.length] != _FLAG:
700
+ raise ValueError('Wrong length or HDLC end flag')
701
+ else:
702
+ remaining_frame_data: bytearray = content[2:self.__format.length]
703
+ """ for parsing in part """
704
+ self.__destination_address = Address.from_frame(remaining_frame_data)
705
+ self.__source_address = Address.from_frame(remaining_frame_data)
706
+ self.__control = Control.from_frame(remaining_frame_data)
707
+ if len(remaining_frame_data) == 2: # info is absence
708
+ self.__hcs = None
709
+ self.__info = bytes()
710
+ else:
711
+ self.__hcs = CRC.from_frame(value=remaining_frame_data,
712
+ message=self.__header_sequence)
713
+ self.__info = bytes(remaining_frame_data[:-2])
714
+ self.__fcs = CRC.from_frame(value=remaining_frame_data[-2:],
715
+ message=self.__frame_sequence)
716
+ del content[:self.__format.length]
717
+ else:
718
+ self.__destination_address = DA
719
+ self.__source_address = SA
720
+ self.__control = control
721
+ self.__info = info
722
+ # Frames that do not have an information field, e.g., as with some supervisory frames, or an information field of zero length do not contain an HCS and an FCS,
723
+ # only an FCS. ISO/IEC 13239:2002(E), H.4 Frame format type 3. 7:5 = format + control + HCS? + FCS
724
+ if len(self.__info) == 0:
725
+ self.__format = Format(is_segmentation=is_segmentation,
726
+ length=len(self.__destination_address) + len(self.__source_address) + 5)
727
+ self.__hcs = None
728
+ else:
729
+ self.__format = Format(is_segmentation=is_segmentation,
730
+ length=len(self.__destination_address) + len(self.__source_address) + len(self.__info) + 7)
731
+ self.__hcs = CRC(message=self.__header_sequence)
732
+ self.__fcs = CRC(message=self.__frame_sequence)
733
+
734
+ def get_header(self) -> tuple[Address, Address]:
735
+ """ return SA, DA for reusing """
736
+ return self.__destination_address, self.__source_address
737
+
738
+ # todo: make <parse> with Result
739
+ @classmethod
740
+ def try_from(cls, value: bytearray) -> Frame | None:
741
+ """ Search of HDLC start flag and return Frame and value remains for next searching. If wrong frame when return value with out start flag for parsing """
742
+ while len(value) != 0 and value[0] != _FLAG: # remove all bytes before flag
743
+ value.pop(0)
744
+ if len(value) < 9: # where 9 is min length of HDLC frame type-3
745
+ return None
746
+ else:
747
+ try:
748
+ return cls(value)
749
+ except ValueError as e:
750
+ print(F'Wrong Frame: {e.args[0]}')
751
+ return None
752
+ except NotEnoughDataError as e:
753
+ # print(F'Frame Error: {e.args[0]}')
754
+ return None
755
+ except FormatDataError as e:
756
+ print(F'Frame Error: {e.args[0]}')
757
+ value.pop(0)
758
+ return None
759
+
760
+ @staticmethod
761
+ def flag() -> int:
762
+ """ return flag frame """
763
+ return _FLAG
764
+
765
+ @property
766
+ def __header_sequence(self) -> bytes:
767
+ return self.__format.content + self.__destination_address.content + self.__source_address.content + self.__control.content
768
+
769
+ @property
770
+ def __frame_sequence(self) -> bytes:
771
+ if self.__hcs is None:
772
+ return self.__header_sequence
773
+ else:
774
+ return self.__header_sequence + self.__hcs.content + self.__info
775
+
776
+ @cached_property
777
+ def content(self) -> bytes:
778
+ return Frame.__FLAG_content + self.__frame_sequence + self.__fcs.content + Frame.__FLAG_content
779
+
780
+ def __str__(self):
781
+ return F'{self.__control.name} DA:{self.__destination_address} SA:{self.__source_address} {" Info["+str(len(self.__info))+"]:"+self.__info.hex(" ") if len(self.__info) != 0 else ""}'
782
+
783
+ def __len__(self):
784
+ return self.__format.length
785
+
786
+ def is_for_me(self, DA: Address, SA: Address) -> bool:
787
+ """ compare by DA and SA received frame"""
788
+ return DA == self.__source_address and SA == self.__destination_address
789
+
790
+ @property
791
+ def control(self):
792
+ return self.__control
793
+
794
+ @cached_property
795
+ def is_segmentation(self) -> bool:
796
+ return self.__format.is_segmentation
797
+
798
+ @property
799
+ def info(self) -> bytes:
800
+ return self.__info
801
+
802
+ def is_next(self, other: Frame) -> bool:
803
+ """ return TRUE if frame is next information frame of current. Other must be previous. """
804
+ return self.__control == Control.next_send_sequence(Control.next_receiver_sequence(other.control))
805
+
806
+ def is_next_send(self, other: Frame) -> bool:
807
+ """ return TRUE if frame is next information frame of current. Other must be previous. """
808
+ return self.__control == Control.next_send_sequence(other.control)
809
+
810
+ @staticmethod
811
+ def join_info(frames: Deque[Frame]) -> bytearray:
812
+ """ TODO: """
813
+ while len(frames) != 0:
814
+ frame: Frame = frames.popleft()
815
+ if frame.control.is_info():
816
+ info: bytearray = bytearray(frame.info)
817
+ break
818
+ else:
819
+ print(F'Frame {frame} not handled and deleted')
820
+ else:
821
+ raise ValueError('Not found information Frame')
822
+ while frame.is_segmentation:
823
+ if len(frames) == 0:
824
+ raise ValueError('Not found end information Frame')
825
+ else:
826
+ next_frame: Frame = frames.popleft()
827
+ if next_frame.control.is_info() and next_frame.is_next_send(frame):
828
+ info.extend(next_frame.info)
829
+ frame = next_frame
830
+ else:
831
+ print(F'Frame {frame} not handled and deleted')
832
+ return info
833
+
834
+
835
+ if __name__ == '__main__':
836
+ ad1 = Address(upper_address=0x3f,
837
+ lower_address=1)
838
+ ad2 = Address(upper_address=0x3f,
839
+ lower_address=1)
840
+ print(ad1)
841
+ comp = ad1 == ad2
842
+ comp2 = ad1 is ad2
843
+ a = Frame(upper_destination_address=0x3f,
844
+ upper_source_address=1,
845
+ control=Control(0x10),
846
+ info=bytes(),
847
+ is_segmentation=False)
848
+ head = a.get_header()
849
+ a1 = Frame(upper_destination_address=0x3,
850
+ upper_source_address=10,
851
+ control=Control(0x10),
852
+ info=bytes(),
853
+ is_segmentation=False,
854
+ header=head
855
+ )
856
+ comp3 = a1.is_for_me(head)
857
+ print(a)
858
+ # a1 = Frame(upper_destination_address=0x3f,
859
+ # upper_source_address=1,
860
+ # control=Control(0x32),
861
+ # info=bytes(),
862
+ # is_segmentation=False)
863
+ # print(a1)
864
+ # print(a1.is_next(a))
865
+
866
+ # data = bytearray.fromhex('7e a0 38 21 02 21 30 84 d4 e6 e7 00 61 29 a1 09 06 07 60 85 74 05 08 01 01 a2 03 02 01 00 a3 05 a1 03 02 01 00 be 10 04 0e 08 00 06 5f 1f 04 00 00 18 18 04 00 00 07 4e 98 7e')
867
+ # data = bytearray(b'~~\xa0\x1f!\x02!sV\xf4\x81\x80\x12\x05\x01\x80\x06\x01\x80\x07\x04\x00\x00\x00\x01\x08\x04\x00\x00\x00\x01S;~\xa0\x1f!\x02!sV\xf4\x81\x80\x12\x05\x01\x80\x06\x01\x80\x07\x04\x00\x00\x00\x01\x08\x04\x00\x00\x00\x01S;~')
868
+ # data = bytearray.fromhex('7e a8 87 21 02 21 7a fa 2c 07 e4 01 01 03 02 1e ff ff 80 00 00 15 00 00 00 00 db 69 14 81 15 00 00 00 00 00 49 8b f0 15 00 00 00 00 08 99 89 25 15 00 00 00 00 07 a1 9a 16 15 00 00 00 00 00 b2 3e cb 15 00 00 00 00 00 00 00 00 15 00 00 00 00 00 03 7e')
869
+ data = bytearray.fromhex('7E A8 01 41 02 21 52 99 A9 E6 E7 00 C4 02 C1 00 00 00 00 01 00 82 02 EA 01 81 9F 02 04 12 00 0F 11 01 09 06 00 00 28 00 00 FF 02 02 01 09 02 03 0F 01 16 01 00 02 03 0F 02 16 01 00 02 03 0F 03 16 01 00 02 03 0F 04 16 01 00 02 03 0F 05 16 01 00 02 03 0F 06 16 01 00 02 03 0F 07 16 01 00 02 03 0F 08 16 01 00 02 03 0F 09 16 01 00 01 04 02 02 0F 01 16 00 02 02 0F 02 16 00 02 02 0F 03 16 00 02 02 0F 04 16 00 02 04 12 00 08 11 00 09 06 00 00 01 00 00 FF 02 02 01 09 02 03 0F 01 16 01 00 02 03 0F 02 16 01 00 02 03 0F 03 16 01 00 02 03 0F 04 16 01 00 02 03 0F 05 16 01 00 02 03 0F 06 16 01 00 02 03 0F 07 16 01 00 02 03 0F 08 16 01 00 02 03 0F 09 16 01 00 01 06 02 02 0F 01 16 01 02 02 0F 02 16 01 02 02 0F 03 16 01 02 02 0F 04 16 01 02 02 0F 05 16 01 02 02 0F 06 16 01 02 1F FC 7E')
870
+ # data = bytearray(b'~\xa8\x87!\x02!\x96\x98\x01\xe6\xe7\x00\xc4\x01\xc1\x00\x01\n\x02\t\t\x0c\x07\xe5\x08\x06\x05\x0b\x1e\xff\xff\x80\x00\x00\x15\x00\x00\x00\x00\xda\x85\x9e~')
871
+ # data = bytearray(b'~\xa8\x89!\x03\x96\xae)\xe6\xe7\x00\xc4\x01\xc1\x00\x01\x07\x02\x02\x11\x00\x01\x05\x02\x03\t\x04\x00\x00\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x01\x02\x03\t\x04\x01\x00\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x02\x02\x03\t\x04\x0c\x17\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x04\x02\x03\t\x04\x16\x1e\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x04\x02\x03\t\x04\x17\x1e\x00\xff\t\x06\x00\x00\n\x00d\xff\x12\x00\x03\x02\x02\x11\x01\x01\x01\x02\x03\t\x04\x01\x00\x00\xff\t\x06\x00\x00\x19Q~')
872
+ frame1 = Frame.try_from(data)
873
+ print(frame1)
874
+ a = Control.SNRM_P
875
+ print(a)