pydnp3-stepfunc 1.6.0.3__cp313-cp313-manylinux1_x86_64.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.
- dnp3/__init__.py +138 -0
- dnp3/_build_ffi.py +166 -0
- dnp3/_ffi.py +64 -0
- dnp3/channel.py +240 -0
- dnp3/handler.py +509 -0
- dnp3/logging.py +93 -0
- dnp3/master.py +539 -0
- dnp3/outstation.py +400 -0
- dnp3/runtime.py +57 -0
- dnp3/types.py +517 -0
- dnp3/vendor/dnp3.h +8253 -0
- dnp3/vendor/libdnp3_ffi.so +0 -0
- pydnp3_stepfunc-1.6.0.3.dist-info/METADATA +238 -0
- pydnp3_stepfunc-1.6.0.3.dist-info/RECORD +17 -0
- pydnp3_stepfunc-1.6.0.3.dist-info/WHEEL +5 -0
- pydnp3_stepfunc-1.6.0.3.dist-info/licenses/LICENSE +21 -0
- pydnp3_stepfunc-1.6.0.3.dist-info/top_level.txt +1 -0
dnp3/types.py
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DNP3 data types, enums, and flags.
|
|
3
|
+
|
|
4
|
+
Provides Pythonic wrappers around the C FFI enum and struct types.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from enum import IntEnum
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional
|
|
10
|
+
import time as _time
|
|
11
|
+
|
|
12
|
+
from ._ffi import ffi, lib
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# --- Enums ---
|
|
16
|
+
|
|
17
|
+
class ParamError(IntEnum):
|
|
18
|
+
OK = 0
|
|
19
|
+
INVALID_TIMEOUT = 1
|
|
20
|
+
NULL_PARAMETER = 2
|
|
21
|
+
STRING_NOT_UTF8 = 3
|
|
22
|
+
NO_SUPPORT = 4
|
|
23
|
+
ASSOCIATION_DOES_NOT_EXIST = 5
|
|
24
|
+
ASSOCIATION_DUPLICATE_ADDRESS = 6
|
|
25
|
+
INVALID_SOCKET_ADDRESS = 7
|
|
26
|
+
INVALID_DNP3_ADDRESS = 8
|
|
27
|
+
INVALID_BUFFER_SIZE = 9
|
|
28
|
+
ADDRESS_FILTER_CONFLICT = 10
|
|
29
|
+
SERVER_ALREADY_STARTED = 11
|
|
30
|
+
SERVER_BIND_ERROR = 12
|
|
31
|
+
MASTER_ALREADY_SHUTDOWN = 13
|
|
32
|
+
RUNTIME_CREATION_FAILURE = 14
|
|
33
|
+
RUNTIME_DESTROYED = 15
|
|
34
|
+
RUNTIME_CANNOT_BLOCK_WITHIN_ASYNC = 16
|
|
35
|
+
LOGGING_ALREADY_CONFIGURED = 17
|
|
36
|
+
POINT_DOES_NOT_EXIST = 18
|
|
37
|
+
INVALID_PEER_CERTIFICATE = 19
|
|
38
|
+
INVALID_LOCAL_CERTIFICATE = 20
|
|
39
|
+
INVALID_PRIVATE_KEY = 21
|
|
40
|
+
INVALID_DNS_NAME = 22
|
|
41
|
+
OTHER_TLS_ERROR = 23
|
|
42
|
+
WRONG_CHANNEL_TYPE = 24
|
|
43
|
+
CONSUMED = 25
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CommandStatus(IntEnum):
|
|
47
|
+
SUCCESS = 0
|
|
48
|
+
TIMEOUT = 1
|
|
49
|
+
NO_SELECT = 2
|
|
50
|
+
FORMAT_ERROR = 3
|
|
51
|
+
NOT_SUPPORTED = 4
|
|
52
|
+
ALREADY_ACTIVE = 5
|
|
53
|
+
HARDWARE_ERROR = 6
|
|
54
|
+
LOCAL = 7
|
|
55
|
+
TOO_MANY_OPS = 8
|
|
56
|
+
NOT_AUTHORIZED = 9
|
|
57
|
+
AUTOMATION_INHIBIT = 10
|
|
58
|
+
PROCESSING_LIMITED = 11
|
|
59
|
+
OUT_OF_RANGE = 12
|
|
60
|
+
DOWNSTREAM_LOCAL = 13
|
|
61
|
+
ALREADY_COMPLETE = 14
|
|
62
|
+
BLOCKED = 15
|
|
63
|
+
CANCELED = 16
|
|
64
|
+
BLOCKED_OTHER_MASTER = 17
|
|
65
|
+
DOWNSTREAM_FAIL = 18
|
|
66
|
+
NON_PARTICIPATING = 19
|
|
67
|
+
UNKNOWN = 20
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TripCloseCode(IntEnum):
|
|
71
|
+
NUL = 0
|
|
72
|
+
CLOSE = 1
|
|
73
|
+
TRIP = 2
|
|
74
|
+
RESERVED = 3
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class OpType(IntEnum):
|
|
78
|
+
NUL = 0
|
|
79
|
+
PULSE_ON = 1
|
|
80
|
+
PULSE_OFF = 2
|
|
81
|
+
LATCH_ON = 3
|
|
82
|
+
LATCH_OFF = 4
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class DoubleBit(IntEnum):
|
|
86
|
+
INTERMEDIATE = 0
|
|
87
|
+
DETERMINED_OFF = 1
|
|
88
|
+
DETERMINED_ON = 2
|
|
89
|
+
INDETERMINATE = 3
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TimeQuality(IntEnum):
|
|
93
|
+
SYNCHRONIZED = 0
|
|
94
|
+
UNSYNCHRONIZED = 1
|
|
95
|
+
INVALID = 2
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class CommandMode(IntEnum):
|
|
99
|
+
DIRECT_OPERATE = 0
|
|
100
|
+
SELECT_BEFORE_OPERATE = 1
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TimeSyncMode(IntEnum):
|
|
104
|
+
LAN = 0
|
|
105
|
+
NON_LAN = 1
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class LinkErrorMode(IntEnum):
|
|
109
|
+
DISCARD = 0
|
|
110
|
+
CLOSE = 1
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class AppDecodeLevel(IntEnum):
|
|
114
|
+
NOTHING = 0
|
|
115
|
+
HEADER = 1
|
|
116
|
+
OBJECT_HEADERS = 2
|
|
117
|
+
OBJECT_VALUES = 3
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TransportDecodeLevel(IntEnum):
|
|
121
|
+
NOTHING = 0
|
|
122
|
+
HEADER = 1
|
|
123
|
+
PAYLOAD = 2
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class LinkDecodeLevel(IntEnum):
|
|
127
|
+
NOTHING = 0
|
|
128
|
+
HEADER = 1
|
|
129
|
+
PAYLOAD = 2
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class PhysDecodeLevel(IntEnum):
|
|
133
|
+
NOTHING = 0
|
|
134
|
+
LENGTH = 1
|
|
135
|
+
DATA = 2
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class FunctionCode(IntEnum):
|
|
139
|
+
CONFIRM = 0
|
|
140
|
+
READ = 1
|
|
141
|
+
WRITE = 2
|
|
142
|
+
SELECT = 3
|
|
143
|
+
OPERATE = 4
|
|
144
|
+
DIRECT_OPERATE = 5
|
|
145
|
+
DIRECT_OPERATE_NO_ACK = 6
|
|
146
|
+
IMMEDIATE_FREEZE = 7
|
|
147
|
+
IMMEDIATE_FREEZE_NO_ACK = 8
|
|
148
|
+
FREEZE_AND_CLEAR = 9
|
|
149
|
+
FREEZE_AND_CLEAR_NO_ACK = 10
|
|
150
|
+
FREEZE_AT_TIME = 11
|
|
151
|
+
FREEZE_AT_TIME_NO_ACK = 12
|
|
152
|
+
COLD_RESTART = 13
|
|
153
|
+
WARM_RESTART = 14
|
|
154
|
+
INITIALIZE_DATA = 15
|
|
155
|
+
INITIALIZE_APPLICATION = 16
|
|
156
|
+
START_APPLICATION = 17
|
|
157
|
+
STOP_APPLICATION = 18
|
|
158
|
+
SAVE_CONFIGURATION = 19
|
|
159
|
+
ENABLE_UNSOLICITED = 20
|
|
160
|
+
DISABLE_UNSOLICITED = 21
|
|
161
|
+
ASSIGN_CLASS = 22
|
|
162
|
+
DELAY_MEASURE = 23
|
|
163
|
+
RECORD_CURRENT_TIME = 24
|
|
164
|
+
OPEN_FILE = 25
|
|
165
|
+
CLOSE_FILE = 26
|
|
166
|
+
DELETE_FILE = 27
|
|
167
|
+
GET_FILE_INFO = 28
|
|
168
|
+
AUTHENTICATE_FILE = 29
|
|
169
|
+
ABORT_FILE = 30
|
|
170
|
+
RESPONSE = 129
|
|
171
|
+
UNSOLICITED_RESPONSE = 130
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class ClientState(IntEnum):
|
|
175
|
+
DISABLED = 0
|
|
176
|
+
CONNECTING = 1
|
|
177
|
+
CONNECTED = 2
|
|
178
|
+
WAIT_AFTER_FAILED_CONNECT = 3
|
|
179
|
+
WAIT_AFTER_DISCONNECT = 4
|
|
180
|
+
SHUTDOWN = 5
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class ReadType(IntEnum):
|
|
184
|
+
STARTUP_INTEGRITY = 0
|
|
185
|
+
UNSOLICITED = 1
|
|
186
|
+
SINGLE_POLL = 2
|
|
187
|
+
PERIODIC_POLL = 3
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class TaskType(IntEnum):
|
|
191
|
+
USER_READ = 0
|
|
192
|
+
PERIODIC_POLL = 1
|
|
193
|
+
STARTUP_INTEGRITY = 2
|
|
194
|
+
AUTO_EVENT_SCAN = 3
|
|
195
|
+
COMMAND = 4
|
|
196
|
+
CLEAR_RESTART_BIT = 5
|
|
197
|
+
ENABLE_UNSOLICITED = 6
|
|
198
|
+
DISABLE_UNSOLICITED = 7
|
|
199
|
+
TIME_SYNC = 8
|
|
200
|
+
RESTART = 9
|
|
201
|
+
FILE_READ = 10
|
|
202
|
+
GET_FILE_INFO = 11
|
|
203
|
+
READ_DIRECTORY = 12
|
|
204
|
+
FILE_AUTH = 13
|
|
205
|
+
FILE_OPEN = 14
|
|
206
|
+
FILE_WRITE = 15
|
|
207
|
+
FILE_CLOSE = 16
|
|
208
|
+
GENERIC_EMPTY_RESPONSE = 17
|
|
209
|
+
WRITE_DEAD_BANDS = 18
|
|
210
|
+
LINK_STATUS = 19
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class AutoTimeSync(IntEnum):
|
|
214
|
+
NONE = 0
|
|
215
|
+
LAN = 1
|
|
216
|
+
NON_LAN = 2
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _build_variation_enum():
|
|
220
|
+
"""Build Variation enum dynamically from C library constants."""
|
|
221
|
+
members = {}
|
|
222
|
+
for attr_name in dir(lib):
|
|
223
|
+
if attr_name.startswith("DNP3_VARIATION_GROUP"):
|
|
224
|
+
name = attr_name.replace("DNP3_VARIATION_", "")
|
|
225
|
+
members[name] = getattr(lib, attr_name)
|
|
226
|
+
return IntEnum("Variation", members)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
Variation = _build_variation_enum()
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Convenience group/variation mappings
|
|
233
|
+
BINARY_INPUT = Variation["GROUP1_VAR0"]
|
|
234
|
+
BINARY_OUTPUT = Variation["GROUP10_VAR0"]
|
|
235
|
+
ANALOG_INPUT = Variation["GROUP30_VAR0"]
|
|
236
|
+
ANALOG_OUTPUT = Variation["GROUP40_VAR0"]
|
|
237
|
+
COUNTER = Variation["GROUP20_VAR0"]
|
|
238
|
+
FROZEN_COUNTER = Variation["GROUP21_VAR0"]
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# --- Data classes ---
|
|
242
|
+
|
|
243
|
+
@dataclass
|
|
244
|
+
class Flags:
|
|
245
|
+
value: int
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def online(self) -> bool:
|
|
249
|
+
return bool(self.value & 0x01)
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def restart(self) -> bool:
|
|
253
|
+
return bool(self.value & 0x02)
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def comm_lost(self) -> bool:
|
|
257
|
+
return bool(self.value & 0x04)
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def remote_forced(self) -> bool:
|
|
261
|
+
return bool(self.value & 0x08)
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def local_forced(self) -> bool:
|
|
265
|
+
return bool(self.value & 0x10)
|
|
266
|
+
|
|
267
|
+
def to_ffi(self):
|
|
268
|
+
return {"value": self.value}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@dataclass
|
|
272
|
+
class Timestamp:
|
|
273
|
+
value: int # milliseconds since UNIX epoch
|
|
274
|
+
quality: TimeQuality
|
|
275
|
+
|
|
276
|
+
@classmethod
|
|
277
|
+
def now(cls) -> "Timestamp":
|
|
278
|
+
return cls(int(_time.time() * 1000), TimeQuality.SYNCHRONIZED)
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def invalid(cls) -> "Timestamp":
|
|
282
|
+
return cls(0, TimeQuality.INVALID)
|
|
283
|
+
|
|
284
|
+
def to_ffi(self):
|
|
285
|
+
return {"value": self.value, "quality": self.quality.value}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@dataclass
|
|
289
|
+
class BinaryInput:
|
|
290
|
+
index: int
|
|
291
|
+
value: bool
|
|
292
|
+
flags: Flags
|
|
293
|
+
time: Timestamp
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def from_ffi(cls, c_obj) -> "BinaryInput":
|
|
297
|
+
return cls(
|
|
298
|
+
index=c_obj.index,
|
|
299
|
+
value=bool(c_obj.value),
|
|
300
|
+
flags=Flags(c_obj.flags.value),
|
|
301
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@dataclass
|
|
306
|
+
class DoubleBitBinaryInput:
|
|
307
|
+
index: int
|
|
308
|
+
value: DoubleBit
|
|
309
|
+
flags: Flags
|
|
310
|
+
time: Timestamp
|
|
311
|
+
|
|
312
|
+
@classmethod
|
|
313
|
+
def from_ffi(cls, c_obj) -> "DoubleBitBinaryInput":
|
|
314
|
+
return cls(
|
|
315
|
+
index=c_obj.index,
|
|
316
|
+
value=DoubleBit(c_obj.value),
|
|
317
|
+
flags=Flags(c_obj.flags.value),
|
|
318
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@dataclass
|
|
323
|
+
class BinaryOutputStatus:
|
|
324
|
+
index: int
|
|
325
|
+
value: bool
|
|
326
|
+
flags: Flags
|
|
327
|
+
time: Timestamp
|
|
328
|
+
|
|
329
|
+
@classmethod
|
|
330
|
+
def from_ffi(cls, c_obj) -> "BinaryOutputStatus":
|
|
331
|
+
return cls(
|
|
332
|
+
index=c_obj.index,
|
|
333
|
+
value=bool(c_obj.value),
|
|
334
|
+
flags=Flags(c_obj.flags.value),
|
|
335
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@dataclass
|
|
340
|
+
class Counter:
|
|
341
|
+
index: int
|
|
342
|
+
value: int
|
|
343
|
+
flags: Flags
|
|
344
|
+
time: Timestamp
|
|
345
|
+
|
|
346
|
+
@classmethod
|
|
347
|
+
def from_ffi(cls, c_obj) -> "Counter":
|
|
348
|
+
return cls(
|
|
349
|
+
index=c_obj.index,
|
|
350
|
+
value=c_obj.value,
|
|
351
|
+
flags=Flags(c_obj.flags.value),
|
|
352
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@dataclass
|
|
357
|
+
class FrozenCounter:
|
|
358
|
+
index: int
|
|
359
|
+
value: int
|
|
360
|
+
flags: Flags
|
|
361
|
+
time: Timestamp
|
|
362
|
+
|
|
363
|
+
@classmethod
|
|
364
|
+
def from_ffi(cls, c_obj) -> "FrozenCounter":
|
|
365
|
+
return cls(
|
|
366
|
+
index=c_obj.index,
|
|
367
|
+
value=c_obj.value,
|
|
368
|
+
flags=Flags(c_obj.flags.value),
|
|
369
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
@dataclass
|
|
374
|
+
class AnalogInput:
|
|
375
|
+
index: int
|
|
376
|
+
value: float
|
|
377
|
+
flags: Flags
|
|
378
|
+
time: Timestamp
|
|
379
|
+
|
|
380
|
+
@classmethod
|
|
381
|
+
def from_ffi(cls, c_obj) -> "AnalogInput":
|
|
382
|
+
return cls(
|
|
383
|
+
index=c_obj.index,
|
|
384
|
+
value=c_obj.value,
|
|
385
|
+
flags=Flags(c_obj.flags.value),
|
|
386
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@dataclass
|
|
391
|
+
class AnalogOutputStatus:
|
|
392
|
+
index: int
|
|
393
|
+
value: float
|
|
394
|
+
flags: Flags
|
|
395
|
+
time: Timestamp
|
|
396
|
+
|
|
397
|
+
@classmethod
|
|
398
|
+
def from_ffi(cls, c_obj) -> "AnalogOutputStatus":
|
|
399
|
+
return cls(
|
|
400
|
+
index=c_obj.index,
|
|
401
|
+
value=c_obj.value,
|
|
402
|
+
flags=Flags(c_obj.flags.value),
|
|
403
|
+
time=Timestamp(c_obj.time.value, TimeQuality(c_obj.time.quality)),
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
@dataclass
|
|
408
|
+
class ControlCode:
|
|
409
|
+
tcc: TripCloseCode = TripCloseCode.NUL
|
|
410
|
+
clear: bool = False
|
|
411
|
+
op_type: OpType = OpType.LATCH_ON
|
|
412
|
+
|
|
413
|
+
def to_ffi(self):
|
|
414
|
+
return {
|
|
415
|
+
"tcc": self.tcc.value,
|
|
416
|
+
"clear": self.clear,
|
|
417
|
+
"queue": False,
|
|
418
|
+
"op_type": self.op_type.value,
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@dataclass
|
|
423
|
+
class Group12Var1:
|
|
424
|
+
"""Control Relay Output Block (CROB)."""
|
|
425
|
+
code: ControlCode
|
|
426
|
+
count: int = 1
|
|
427
|
+
on_time: int = 1000
|
|
428
|
+
off_time: int = 1000
|
|
429
|
+
|
|
430
|
+
def to_ffi(self):
|
|
431
|
+
return {
|
|
432
|
+
"code": self.code.to_ffi(),
|
|
433
|
+
"count": self.count,
|
|
434
|
+
"on_time": self.on_time,
|
|
435
|
+
"off_time": self.off_time,
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
@dataclass
|
|
440
|
+
class DecodeLevel:
|
|
441
|
+
application: AppDecodeLevel = AppDecodeLevel.NOTHING
|
|
442
|
+
transport: TransportDecodeLevel = TransportDecodeLevel.NOTHING
|
|
443
|
+
link: LinkDecodeLevel = LinkDecodeLevel.NOTHING
|
|
444
|
+
physical: PhysDecodeLevel = PhysDecodeLevel.NOTHING
|
|
445
|
+
|
|
446
|
+
def to_ffi(self):
|
|
447
|
+
return {
|
|
448
|
+
"application": self.application.value,
|
|
449
|
+
"transport": self.transport.value,
|
|
450
|
+
"link": self.link.value,
|
|
451
|
+
"physical": self.physical.value,
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
@dataclass
|
|
456
|
+
class ConnectStrategy:
|
|
457
|
+
min_connect_delay: int = 1000 # ms
|
|
458
|
+
max_connect_delay: int = 10000 # ms
|
|
459
|
+
reconnect_delay: int = 1000 # ms
|
|
460
|
+
|
|
461
|
+
def to_ffi(self):
|
|
462
|
+
return {
|
|
463
|
+
"min_connect_delay": self.min_connect_delay,
|
|
464
|
+
"max_connect_delay": self.max_connect_delay,
|
|
465
|
+
"reconnect_delay": self.reconnect_delay,
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@dataclass
|
|
470
|
+
class IIN1:
|
|
471
|
+
broadcast: bool = False
|
|
472
|
+
class_1_events: bool = False
|
|
473
|
+
class_2_events: bool = False
|
|
474
|
+
class_3_events: bool = False
|
|
475
|
+
need_time: bool = False
|
|
476
|
+
local_control: bool = False
|
|
477
|
+
device_trouble: bool = False
|
|
478
|
+
device_restart: bool = False
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
@dataclass
|
|
482
|
+
class IIN2:
|
|
483
|
+
no_func_code_support: bool = False
|
|
484
|
+
object_unknown: bool = False
|
|
485
|
+
parameter_error: bool = False
|
|
486
|
+
event_buffer_overflow: bool = False
|
|
487
|
+
already_executing: bool = False
|
|
488
|
+
config_corrupt: bool = False
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@dataclass
|
|
492
|
+
class IIN:
|
|
493
|
+
iin1: IIN1
|
|
494
|
+
iin2: IIN2
|
|
495
|
+
|
|
496
|
+
@classmethod
|
|
497
|
+
def from_ffi(cls, c_iin):
|
|
498
|
+
return cls(
|
|
499
|
+
iin1=IIN1(
|
|
500
|
+
broadcast=bool(c_iin.iin1.broadcast),
|
|
501
|
+
class_1_events=bool(c_iin.iin1.class_1_events),
|
|
502
|
+
class_2_events=bool(c_iin.iin1.class_2_events),
|
|
503
|
+
class_3_events=bool(c_iin.iin1.class_3_events),
|
|
504
|
+
need_time=bool(c_iin.iin1.need_time),
|
|
505
|
+
local_control=bool(c_iin.iin1.local_control),
|
|
506
|
+
device_trouble=bool(c_iin.iin1.device_trouble),
|
|
507
|
+
device_restart=bool(c_iin.iin1.device_restart),
|
|
508
|
+
),
|
|
509
|
+
iin2=IIN2(
|
|
510
|
+
no_func_code_support=bool(c_iin.iin2.no_func_code_support),
|
|
511
|
+
object_unknown=bool(c_iin.iin2.object_unknown),
|
|
512
|
+
parameter_error=bool(c_iin.iin2.parameter_error),
|
|
513
|
+
event_buffer_overflow=bool(c_iin.iin2.event_buffer_overflow),
|
|
514
|
+
already_executing=bool(c_iin.iin2.already_executing),
|
|
515
|
+
config_corrupt=bool(c_iin.iin2.config_corrupt),
|
|
516
|
+
),
|
|
517
|
+
)
|