DLMS-SPODES 0.87.12__py3-none-any.whl → 0.87.15__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/__class_init__.py +3 -3
- DLMS_SPODES/cosem_interface_classes/__init__.py +1 -1
- DLMS_SPODES/cosem_interface_classes/a_parameter.py +20 -20
- DLMS_SPODES/cosem_interface_classes/activity_calendar.py +254 -254
- DLMS_SPODES/cosem_interface_classes/arbitrator.py +105 -105
- DLMS_SPODES/cosem_interface_classes/association_ln/abstract.py +34 -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 +485 -485
- DLMS_SPODES/cosem_interface_classes/association_ln/ver1.py +133 -133
- DLMS_SPODES/cosem_interface_classes/association_ln/ver2.py +36 -36
- DLMS_SPODES/cosem_interface_classes/association_ln/ver3.py +4 -4
- DLMS_SPODES/cosem_interface_classes/association_sn/ver0.py +12 -12
- DLMS_SPODES/cosem_interface_classes/attr_indexes.py +12 -12
- DLMS_SPODES/cosem_interface_classes/clock.py +131 -131
- DLMS_SPODES/cosem_interface_classes/collection.py +2122 -2122
- DLMS_SPODES/cosem_interface_classes/cosem_interface_class.py +583 -583
- DLMS_SPODES/cosem_interface_classes/data.py +21 -21
- DLMS_SPODES/cosem_interface_classes/demand_register/ver0.py +59 -59
- DLMS_SPODES/cosem_interface_classes/disconnect_control.py +74 -74
- DLMS_SPODES/cosem_interface_classes/extended_register.py +27 -27
- DLMS_SPODES/cosem_interface_classes/gprs_modem_setup.py +43 -43
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver0.py +103 -103
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver1.py +40 -40
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver2.py +9 -9
- DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver0.py +11 -11
- DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver1.py +53 -53
- DLMS_SPODES/cosem_interface_classes/iec_local_port_setup.py +11 -11
- DLMS_SPODES/cosem_interface_classes/image_transfer/image_transfer_status.py +15 -15
- DLMS_SPODES/cosem_interface_classes/image_transfer/ver0.py +126 -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 +487 -487
- DLMS_SPODES/cosem_interface_classes/implementations/profile_generic.py +83 -83
- DLMS_SPODES/cosem_interface_classes/ipv4_setup.py +72 -72
- DLMS_SPODES/cosem_interface_classes/limiter.py +111 -111
- DLMS_SPODES/cosem_interface_classes/ln_pattern.py +333 -333
- DLMS_SPODES/cosem_interface_classes/modem_configuration/ver0.py +65 -65
- DLMS_SPODES/cosem_interface_classes/modem_configuration/ver1.py +39 -39
- DLMS_SPODES/cosem_interface_classes/ntp_setup/ver0.py +67 -67
- DLMS_SPODES/cosem_interface_classes/obis.py +23 -23
- DLMS_SPODES/cosem_interface_classes/overview.py +197 -197
- DLMS_SPODES/cosem_interface_classes/parameter.py +547 -547
- DLMS_SPODES/cosem_interface_classes/parameters.py +172 -172
- DLMS_SPODES/cosem_interface_classes/profile_generic/ver0.py +122 -122
- DLMS_SPODES/cosem_interface_classes/profile_generic/ver1.py +277 -277
- DLMS_SPODES/cosem_interface_classes/push_setup/ver0.py +12 -12
- DLMS_SPODES/cosem_interface_classes/push_setup/ver1.py +10 -10
- DLMS_SPODES/cosem_interface_classes/push_setup/ver2.py +166 -166
- DLMS_SPODES/cosem_interface_classes/register.py +45 -45
- DLMS_SPODES/cosem_interface_classes/register_activation/ver0.py +80 -80
- DLMS_SPODES/cosem_interface_classes/register_monitor.py +46 -46
- DLMS_SPODES/cosem_interface_classes/reports.py +70 -70
- DLMS_SPODES/cosem_interface_classes/schedule.py +176 -176
- DLMS_SPODES/cosem_interface_classes/script_table.py +87 -87
- DLMS_SPODES/cosem_interface_classes/security_setup/ver0.py +68 -68
- DLMS_SPODES/cosem_interface_classes/security_setup/ver1.py +158 -158
- DLMS_SPODES/cosem_interface_classes/single_action_schedule.py +50 -50
- DLMS_SPODES/cosem_interface_classes/special_days_table.py +84 -84
- DLMS_SPODES/cosem_interface_classes/tcp_udp_setup.py +42 -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 +465 -463
- DLMS_SPODES/settings.py +551 -551
- DLMS_SPODES/types/choices.py +142 -142
- DLMS_SPODES/types/common_data_types.py +2401 -2399
- 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 +11 -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/useful_types.py +677 -677
- {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/METADATA +30 -30
- dlms_spodes-0.87.15.dist-info/RECORD +117 -0
- {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/WHEEL +1 -1
- dlms_spodes-0.87.12.dist-info/RECORD +0 -117
- {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.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)
|