pypck 0.8.12__py3-none-any.whl → 0.9.2__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.
- pypck/__init__.py +0 -2
- pypck/connection.py +12 -31
- pypck/lcn_defs.py +22 -0
- pypck/module.py +416 -230
- pypck-0.9.2.dist-info/METADATA +65 -0
- pypck-0.9.2.dist-info/RECORD +13 -0
- pypck/request_handlers.py +0 -694
- pypck/timeout_retry.py +0 -110
- pypck-0.8.12.dist-info/METADATA +0 -145
- pypck-0.8.12.dist-info/RECORD +0 -15
- {pypck-0.8.12.dist-info → pypck-0.9.2.dist-info}/WHEEL +0 -0
- {pypck-0.8.12.dist-info → pypck-0.9.2.dist-info}/licenses/LICENSE +0 -0
- {pypck-0.8.12.dist-info → pypck-0.9.2.dist-info}/top_level.txt +0 -0
pypck/request_handlers.py
DELETED
|
@@ -1,694 +0,0 @@
|
|
|
1
|
-
"""Handlers for requests."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import asyncio
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
-
|
|
8
|
-
from pypck import inputs, lcn_defs
|
|
9
|
-
from pypck.helpers import TaskRegistry
|
|
10
|
-
from pypck.lcn_addr import LcnAddr
|
|
11
|
-
from pypck.pck_commands import PckGenerator
|
|
12
|
-
from pypck.timeout_retry import TimeoutRetryHandler
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from pypck.module import ModuleConnection
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class RequestHandler:
|
|
19
|
-
"""Base RequestHandler class."""
|
|
20
|
-
|
|
21
|
-
def __init__(
|
|
22
|
-
self,
|
|
23
|
-
addr_conn: ModuleConnection,
|
|
24
|
-
num_tries: int = 3,
|
|
25
|
-
timeout: float = 1.5,
|
|
26
|
-
):
|
|
27
|
-
"""Initialize class instance."""
|
|
28
|
-
self.addr_conn = addr_conn
|
|
29
|
-
|
|
30
|
-
self.trh = TimeoutRetryHandler(self.task_registry, num_tries, timeout)
|
|
31
|
-
self.trh.set_timeout_callback(self.timeout)
|
|
32
|
-
|
|
33
|
-
# callback
|
|
34
|
-
addr_conn.register_for_inputs(self.process_input)
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def task_registry(self) -> TaskRegistry:
|
|
38
|
-
"""Get the task registry."""
|
|
39
|
-
return self.addr_conn.task_registry
|
|
40
|
-
|
|
41
|
-
async def request(self) -> Any:
|
|
42
|
-
"""Request information from module."""
|
|
43
|
-
raise NotImplementedError()
|
|
44
|
-
|
|
45
|
-
def process_input(self, inp: inputs.Input) -> None:
|
|
46
|
-
"""Create a task to process the input object concurrently."""
|
|
47
|
-
self.task_registry.create_task(self.async_process_input(inp))
|
|
48
|
-
|
|
49
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
50
|
-
"""Process incoming input object.
|
|
51
|
-
|
|
52
|
-
Method to handle incoming commands for this request handler.
|
|
53
|
-
"""
|
|
54
|
-
raise NotImplementedError()
|
|
55
|
-
|
|
56
|
-
async def timeout(self, failed: bool = False) -> None:
|
|
57
|
-
"""Is called on serial request timeout."""
|
|
58
|
-
raise NotImplementedError()
|
|
59
|
-
|
|
60
|
-
async def cancel(self) -> None:
|
|
61
|
-
"""Cancel request."""
|
|
62
|
-
await self.trh.cancel()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class SerialRequestHandler(RequestHandler):
|
|
66
|
-
"""Request handler to request serial number information from module."""
|
|
67
|
-
|
|
68
|
-
def __init__(
|
|
69
|
-
self,
|
|
70
|
-
addr_conn: ModuleConnection,
|
|
71
|
-
num_tries: int = 3,
|
|
72
|
-
timeout: float = 1.5,
|
|
73
|
-
software_serial: int | None = None,
|
|
74
|
-
):
|
|
75
|
-
"""Initialize class instance."""
|
|
76
|
-
self.hardware_serial = -1
|
|
77
|
-
self.manu = -1
|
|
78
|
-
if software_serial is None:
|
|
79
|
-
software_serial = -1
|
|
80
|
-
self.software_serial = software_serial
|
|
81
|
-
self.hardware_type = lcn_defs.HardwareType.UNKNOWN
|
|
82
|
-
|
|
83
|
-
# events
|
|
84
|
-
self.serial_known = asyncio.Event()
|
|
85
|
-
|
|
86
|
-
super().__init__(addr_conn, num_tries, timeout)
|
|
87
|
-
|
|
88
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
89
|
-
"""Process incoming input object.
|
|
90
|
-
|
|
91
|
-
Method to handle incoming commands for this specific request handler.
|
|
92
|
-
"""
|
|
93
|
-
if isinstance(inp, inputs.ModSn):
|
|
94
|
-
self.hardware_serial = inp.hardware_serial
|
|
95
|
-
self.manu = inp.manu
|
|
96
|
-
self.software_serial = inp.software_serial
|
|
97
|
-
self.hardware_type = inp.hardware_type
|
|
98
|
-
|
|
99
|
-
self.serial_known.set()
|
|
100
|
-
await self.cancel()
|
|
101
|
-
|
|
102
|
-
async def timeout(self, failed: bool = False) -> None:
|
|
103
|
-
"""Is called on serial request timeout."""
|
|
104
|
-
if not failed:
|
|
105
|
-
await self.addr_conn.send_command(False, PckGenerator.request_serial())
|
|
106
|
-
else:
|
|
107
|
-
self.serial_known.set()
|
|
108
|
-
|
|
109
|
-
async def request(self) -> dict[str, int | lcn_defs.HardwareType]:
|
|
110
|
-
"""Request serial number."""
|
|
111
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
112
|
-
self.serial_known.clear()
|
|
113
|
-
self.trh.activate()
|
|
114
|
-
await self.serial_known.wait()
|
|
115
|
-
return self.serials
|
|
116
|
-
|
|
117
|
-
@property
|
|
118
|
-
def serials(self) -> dict[str, int | lcn_defs.HardwareType]:
|
|
119
|
-
"""Return serial numbers of a module."""
|
|
120
|
-
return {
|
|
121
|
-
"hardware_serial": self.hardware_serial,
|
|
122
|
-
"manu": self.manu,
|
|
123
|
-
"software_serial": self.software_serial,
|
|
124
|
-
"hardware_type": self.hardware_type,
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class NameRequestHandler(RequestHandler):
|
|
129
|
-
"""Request handler to request name of a module."""
|
|
130
|
-
|
|
131
|
-
def __init__(
|
|
132
|
-
self,
|
|
133
|
-
addr_conn: ModuleConnection,
|
|
134
|
-
num_tries: int = 3,
|
|
135
|
-
timeout: float = 1.5,
|
|
136
|
-
):
|
|
137
|
-
"""Initialize class instance."""
|
|
138
|
-
self._name: list[str | None] = [None] * 2
|
|
139
|
-
self.name_known = asyncio.Event()
|
|
140
|
-
|
|
141
|
-
super().__init__(addr_conn, num_tries, timeout)
|
|
142
|
-
|
|
143
|
-
self.trhs = []
|
|
144
|
-
for block_id in range(2):
|
|
145
|
-
trh = TimeoutRetryHandler(self.task_registry, num_tries, timeout)
|
|
146
|
-
trh.set_timeout_callback(self.timeout, block_id=block_id)
|
|
147
|
-
self.trhs.append(trh)
|
|
148
|
-
|
|
149
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
150
|
-
"""Process incoming input object.
|
|
151
|
-
|
|
152
|
-
Method to handle incoming commands for this specific request handler.
|
|
153
|
-
"""
|
|
154
|
-
if isinstance(inp, inputs.ModNameComment):
|
|
155
|
-
command = inp.command
|
|
156
|
-
block_id = inp.block_id
|
|
157
|
-
text = inp.text
|
|
158
|
-
|
|
159
|
-
if command == "N":
|
|
160
|
-
self._name[block_id] = f"{text:10s}"
|
|
161
|
-
await self.cancel(block_id)
|
|
162
|
-
if None not in self._name:
|
|
163
|
-
self.name_known.set()
|
|
164
|
-
await self.cancel()
|
|
165
|
-
|
|
166
|
-
# pylint: disable=arguments-differ
|
|
167
|
-
async def timeout(self, failed: bool = False, block_id: int = 0) -> None:
|
|
168
|
-
"""Is called on name request timeout."""
|
|
169
|
-
if not failed:
|
|
170
|
-
await self.addr_conn.send_command(
|
|
171
|
-
False, PckGenerator.request_name(block_id)
|
|
172
|
-
)
|
|
173
|
-
else:
|
|
174
|
-
self.name_known.set()
|
|
175
|
-
|
|
176
|
-
async def request(self) -> str:
|
|
177
|
-
"""Request name from a module."""
|
|
178
|
-
self._name = [None] * 2
|
|
179
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
180
|
-
self.name_known.clear()
|
|
181
|
-
for trh in self.trhs:
|
|
182
|
-
trh.activate()
|
|
183
|
-
await self.name_known.wait()
|
|
184
|
-
return self.name
|
|
185
|
-
|
|
186
|
-
# pylint: disable=arguments-differ
|
|
187
|
-
async def cancel(self, block_id: int | None = None) -> None:
|
|
188
|
-
"""Cancel name request task."""
|
|
189
|
-
if block_id is None: # cancel all
|
|
190
|
-
for trh in self.trhs:
|
|
191
|
-
await trh.cancel()
|
|
192
|
-
else:
|
|
193
|
-
await self.trhs[block_id].cancel()
|
|
194
|
-
|
|
195
|
-
@property
|
|
196
|
-
def name(self) -> str:
|
|
197
|
-
"""Return stored name."""
|
|
198
|
-
return "".join([block for block in self._name if block]).strip()
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
class CommentRequestHandler(RequestHandler):
|
|
202
|
-
"""Request handler to request comment of a module."""
|
|
203
|
-
|
|
204
|
-
def __init__(
|
|
205
|
-
self,
|
|
206
|
-
addr_conn: ModuleConnection,
|
|
207
|
-
num_tries: int = 3,
|
|
208
|
-
timeout: float = 1.5,
|
|
209
|
-
):
|
|
210
|
-
"""Initialize class instance."""
|
|
211
|
-
self._comment: list[str | None] = [None] * 3
|
|
212
|
-
self.comment_known = asyncio.Event()
|
|
213
|
-
|
|
214
|
-
super().__init__(addr_conn, num_tries, timeout)
|
|
215
|
-
|
|
216
|
-
self.trhs = []
|
|
217
|
-
for block_id in range(3):
|
|
218
|
-
trh = TimeoutRetryHandler(self.task_registry, num_tries, timeout)
|
|
219
|
-
trh.set_timeout_callback(self.timeout, block_id=block_id)
|
|
220
|
-
self.trhs.append(trh)
|
|
221
|
-
|
|
222
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
223
|
-
"""Process incoming input object.
|
|
224
|
-
|
|
225
|
-
Method to handle incoming commands for this specific request handler.
|
|
226
|
-
"""
|
|
227
|
-
if isinstance(inp, inputs.ModNameComment):
|
|
228
|
-
command = inp.command
|
|
229
|
-
block_id = inp.block_id
|
|
230
|
-
text = inp.text
|
|
231
|
-
|
|
232
|
-
if command == "K":
|
|
233
|
-
self._comment[block_id] = f"{text:12s}"
|
|
234
|
-
await self.cancel(block_id)
|
|
235
|
-
if None not in self._comment:
|
|
236
|
-
self.comment_known.set()
|
|
237
|
-
await self.cancel()
|
|
238
|
-
|
|
239
|
-
# pylint: disable=arguments-differ
|
|
240
|
-
async def timeout(self, failed: bool = False, block_id: int = 0) -> None:
|
|
241
|
-
"""Is called on comment request timeout."""
|
|
242
|
-
if not failed:
|
|
243
|
-
await self.addr_conn.send_command(
|
|
244
|
-
False, PckGenerator.request_comment(block_id)
|
|
245
|
-
)
|
|
246
|
-
else:
|
|
247
|
-
self.comment_known.set()
|
|
248
|
-
|
|
249
|
-
async def request(self) -> str:
|
|
250
|
-
"""Request comments from a module."""
|
|
251
|
-
self._comment = [None] * 3
|
|
252
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
253
|
-
self.comment_known.clear()
|
|
254
|
-
for trh in self.trhs:
|
|
255
|
-
trh.activate()
|
|
256
|
-
await self.comment_known.wait()
|
|
257
|
-
return self.comment
|
|
258
|
-
|
|
259
|
-
# pylint: disable=arguments-differ
|
|
260
|
-
async def cancel(self, block_id: int | None = None) -> None:
|
|
261
|
-
"""Cancel comment request task."""
|
|
262
|
-
if block_id is None: # cancel all
|
|
263
|
-
for trh in self.trhs:
|
|
264
|
-
await trh.cancel()
|
|
265
|
-
else:
|
|
266
|
-
await self.trhs[block_id].cancel()
|
|
267
|
-
|
|
268
|
-
@property
|
|
269
|
-
def comment(self) -> str:
|
|
270
|
-
"""Return stored comment."""
|
|
271
|
-
return "".join([block for block in self._comment if block]).strip()
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
class OemTextRequestHandler(RequestHandler):
|
|
275
|
-
"""Request handler to request OEM text of a module."""
|
|
276
|
-
|
|
277
|
-
def __init__(
|
|
278
|
-
self,
|
|
279
|
-
addr_conn: ModuleConnection,
|
|
280
|
-
num_tries: int = 3,
|
|
281
|
-
timeout: float = 1.5,
|
|
282
|
-
):
|
|
283
|
-
"""Initialize class instance."""
|
|
284
|
-
self._oem_text: list[str | None] = [None] * 4
|
|
285
|
-
self.oem_text_known = asyncio.Event()
|
|
286
|
-
|
|
287
|
-
super().__init__(addr_conn, num_tries, timeout)
|
|
288
|
-
|
|
289
|
-
self.trhs = []
|
|
290
|
-
for block_id in range(4):
|
|
291
|
-
trh = TimeoutRetryHandler(self.task_registry, num_tries, timeout)
|
|
292
|
-
trh.set_timeout_callback(self.timeout, block_id=block_id)
|
|
293
|
-
self.trhs.append(trh)
|
|
294
|
-
|
|
295
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
296
|
-
"""Process incoming input object.
|
|
297
|
-
|
|
298
|
-
Method to handle incoming commands for this specific request handler.
|
|
299
|
-
"""
|
|
300
|
-
if isinstance(inp, inputs.ModNameComment):
|
|
301
|
-
command = inp.command
|
|
302
|
-
block_id = inp.block_id
|
|
303
|
-
text = inp.text
|
|
304
|
-
|
|
305
|
-
if command == "O":
|
|
306
|
-
self._oem_text[block_id] = f"{text:12s}"
|
|
307
|
-
await self.cancel(block_id)
|
|
308
|
-
if None not in self._oem_text:
|
|
309
|
-
self.oem_text_known.set()
|
|
310
|
-
await self.cancel()
|
|
311
|
-
|
|
312
|
-
# pylint: disable=arguments-differ
|
|
313
|
-
async def timeout(self, failed: bool = False, block_id: int = 0) -> None:
|
|
314
|
-
"""Is called on OEM text request timeout."""
|
|
315
|
-
if not failed:
|
|
316
|
-
await self.addr_conn.send_command(
|
|
317
|
-
False, PckGenerator.request_oem_text(block_id)
|
|
318
|
-
)
|
|
319
|
-
else:
|
|
320
|
-
self.oem_text_known.set()
|
|
321
|
-
|
|
322
|
-
async def request(self) -> list[str]:
|
|
323
|
-
"""Request OEM text from a module."""
|
|
324
|
-
self._oem_text = [None] * 4
|
|
325
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
326
|
-
self.oem_text_known.clear()
|
|
327
|
-
for trh in self.trhs:
|
|
328
|
-
trh.activate()
|
|
329
|
-
await self.oem_text_known.wait()
|
|
330
|
-
return self.oem_text
|
|
331
|
-
|
|
332
|
-
# pylint: disable=arguments-differ
|
|
333
|
-
async def cancel(self, block_id: int | None = None) -> None:
|
|
334
|
-
"""Cancel OEM text request task."""
|
|
335
|
-
if block_id is None: # cancel all
|
|
336
|
-
for trh in self.trhs:
|
|
337
|
-
await trh.cancel()
|
|
338
|
-
else:
|
|
339
|
-
await self.trhs[block_id].cancel()
|
|
340
|
-
|
|
341
|
-
@property
|
|
342
|
-
def oem_text(self) -> list[str]:
|
|
343
|
-
"""Return stored OEM text."""
|
|
344
|
-
return [block.strip() if block else "" for block in self._oem_text]
|
|
345
|
-
# return {'block{}'.format(idx):text
|
|
346
|
-
# for idx, text in enumerate(self._oem_text)}
|
|
347
|
-
|
|
348
|
-
# return ''.join([block for block in self._oem_text if block])
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
class GroupMembershipStaticRequestHandler(RequestHandler):
|
|
352
|
-
"""Request handler to request static group membership of a module."""
|
|
353
|
-
|
|
354
|
-
def __init__(
|
|
355
|
-
self,
|
|
356
|
-
addr_conn: ModuleConnection,
|
|
357
|
-
num_tries: int = 3,
|
|
358
|
-
timeout: float = 1.5,
|
|
359
|
-
):
|
|
360
|
-
"""Initialize class instance."""
|
|
361
|
-
self.groups: set[LcnAddr] = set()
|
|
362
|
-
self.groups_known = asyncio.Event()
|
|
363
|
-
|
|
364
|
-
super().__init__(addr_conn, num_tries, timeout)
|
|
365
|
-
|
|
366
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
367
|
-
"""Process incoming input object.
|
|
368
|
-
|
|
369
|
-
Method to handle incoming commands for this specific request handler.
|
|
370
|
-
"""
|
|
371
|
-
if isinstance(inp, inputs.ModStatusGroups):
|
|
372
|
-
if not inp.dynamic: # static
|
|
373
|
-
self.groups.update(inp.groups)
|
|
374
|
-
self.groups_known.set()
|
|
375
|
-
await self.cancel()
|
|
376
|
-
|
|
377
|
-
async def timeout(self, failed: bool = False) -> None:
|
|
378
|
-
"""Is called on static group membership request timeout."""
|
|
379
|
-
if not failed:
|
|
380
|
-
await self.addr_conn.send_command(
|
|
381
|
-
False, PckGenerator.request_group_membership_static()
|
|
382
|
-
)
|
|
383
|
-
else:
|
|
384
|
-
self.groups_known.set()
|
|
385
|
-
|
|
386
|
-
async def request(self) -> set[LcnAddr]:
|
|
387
|
-
"""Request static group membership from a module."""
|
|
388
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
389
|
-
self.groups_known.clear()
|
|
390
|
-
self.trh.activate()
|
|
391
|
-
await self.groups_known.wait()
|
|
392
|
-
return self.groups
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
class GroupMembershipDynamicRequestHandler(RequestHandler):
|
|
396
|
-
"""Request handler to request static group membership of a module."""
|
|
397
|
-
|
|
398
|
-
def __init__(
|
|
399
|
-
self,
|
|
400
|
-
addr_conn: ModuleConnection,
|
|
401
|
-
num_tries: int = 3,
|
|
402
|
-
timeout: float = 1.5,
|
|
403
|
-
):
|
|
404
|
-
"""Initialize class instance."""
|
|
405
|
-
self.groups: set[LcnAddr] = set()
|
|
406
|
-
self.groups_known = asyncio.Event()
|
|
407
|
-
|
|
408
|
-
super().__init__(addr_conn, num_tries, timeout)
|
|
409
|
-
|
|
410
|
-
async def async_process_input(self, inp: inputs.Input) -> None:
|
|
411
|
-
"""Process incoming input object.
|
|
412
|
-
|
|
413
|
-
Method to handle incoming commands for this specific request handler.
|
|
414
|
-
"""
|
|
415
|
-
if isinstance(inp, inputs.ModStatusGroups):
|
|
416
|
-
if inp.dynamic: # dynamic
|
|
417
|
-
self.groups.update(inp.groups)
|
|
418
|
-
self.groups_known.set()
|
|
419
|
-
await self.cancel()
|
|
420
|
-
|
|
421
|
-
async def timeout(self, failed: bool = False) -> None:
|
|
422
|
-
"""Is called on dynamic group membership request timeout."""
|
|
423
|
-
if not failed:
|
|
424
|
-
await self.addr_conn.send_command(
|
|
425
|
-
False, PckGenerator.request_group_membership_dynamic()
|
|
426
|
-
)
|
|
427
|
-
else:
|
|
428
|
-
self.groups_known.set()
|
|
429
|
-
|
|
430
|
-
async def request(self) -> set[LcnAddr]:
|
|
431
|
-
"""Request dynamic group membership from a module."""
|
|
432
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
433
|
-
self.groups_known.clear()
|
|
434
|
-
self.trh.activate()
|
|
435
|
-
await self.groups_known.wait()
|
|
436
|
-
return self.groups
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
class StatusRequestsHandler:
|
|
440
|
-
"""Manages all status requests for variables, software version, ..."""
|
|
441
|
-
|
|
442
|
-
def __init__(self, addr_conn: ModuleConnection):
|
|
443
|
-
"""Construct StatusRequestHandler instance."""
|
|
444
|
-
self.addr_conn = addr_conn
|
|
445
|
-
self.settings = addr_conn.conn.settings
|
|
446
|
-
|
|
447
|
-
self.last_requested_var_without_type_in_response = lcn_defs.Var.UNKNOWN
|
|
448
|
-
self.last_var_lock = asyncio.Lock()
|
|
449
|
-
|
|
450
|
-
# Output-port request status (0..3)
|
|
451
|
-
self.request_status_outputs = []
|
|
452
|
-
for output_port in range(4):
|
|
453
|
-
trh = TimeoutRetryHandler(
|
|
454
|
-
self.task_registry,
|
|
455
|
-
-1,
|
|
456
|
-
self.settings["MAX_STATUS_EVENTBASED_VALUEAGE"],
|
|
457
|
-
)
|
|
458
|
-
trh.set_timeout_callback(self.request_status_outputs_timeout, output_port)
|
|
459
|
-
self.request_status_outputs.append(trh)
|
|
460
|
-
|
|
461
|
-
# Relay request status (all 8)
|
|
462
|
-
self.request_status_relays = TimeoutRetryHandler(
|
|
463
|
-
self.task_registry, -1, self.settings["MAX_STATUS_EVENTBASED_VALUEAGE"]
|
|
464
|
-
)
|
|
465
|
-
self.request_status_relays.set_timeout_callback(
|
|
466
|
-
self.request_status_relays_timeout
|
|
467
|
-
)
|
|
468
|
-
|
|
469
|
-
# Motor positions request status (1, 2 and 3, 4)
|
|
470
|
-
self.request_status_motor_positions = []
|
|
471
|
-
for motor_pair in range(2):
|
|
472
|
-
trh = TimeoutRetryHandler(
|
|
473
|
-
self.task_registry, -1, self.settings["MAX_STATUS_POLLED_VALUEAGE"]
|
|
474
|
-
)
|
|
475
|
-
trh.set_timeout_callback(
|
|
476
|
-
self.request_status_motor_positions_timeout, motor_pair
|
|
477
|
-
)
|
|
478
|
-
self.request_status_motor_positions.append(trh)
|
|
479
|
-
|
|
480
|
-
# Binary-sensors request status (all 8)
|
|
481
|
-
self.request_status_bin_sensors = TimeoutRetryHandler(
|
|
482
|
-
self.task_registry, -1, self.settings["MAX_STATUS_EVENTBASED_VALUEAGE"]
|
|
483
|
-
)
|
|
484
|
-
self.request_status_bin_sensors.set_timeout_callback(
|
|
485
|
-
self.request_status_bin_sensors_timeout
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
# Variables request status.
|
|
489
|
-
# Lazy initialization: Will be filled once the firmware version is
|
|
490
|
-
# known.
|
|
491
|
-
self.request_status_vars = {}
|
|
492
|
-
for var in lcn_defs.Var:
|
|
493
|
-
if var != lcn_defs.Var.UNKNOWN:
|
|
494
|
-
self.request_status_vars[var] = TimeoutRetryHandler(
|
|
495
|
-
self.task_registry,
|
|
496
|
-
-1,
|
|
497
|
-
self.settings["MAX_STATUS_EVENTBASED_VALUEAGE"],
|
|
498
|
-
)
|
|
499
|
-
self.request_status_vars[var].set_timeout_callback(
|
|
500
|
-
self.request_status_var_timeout, var=var
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
# LEDs and logic-operations request status (all 12+4).
|
|
504
|
-
self.request_status_leds_and_logic_ops = TimeoutRetryHandler(
|
|
505
|
-
self.task_registry, -1, self.settings["MAX_STATUS_POLLED_VALUEAGE"]
|
|
506
|
-
)
|
|
507
|
-
self.request_status_leds_and_logic_ops.set_timeout_callback(
|
|
508
|
-
self.request_status_leds_and_logic_ops_timeout
|
|
509
|
-
)
|
|
510
|
-
|
|
511
|
-
# Key lock-states request status (all tables, A-D).
|
|
512
|
-
self.request_status_locked_keys = TimeoutRetryHandler(
|
|
513
|
-
self.task_registry, -1, self.settings["MAX_STATUS_POLLED_VALUEAGE"]
|
|
514
|
-
)
|
|
515
|
-
self.request_status_locked_keys.set_timeout_callback(
|
|
516
|
-
self.request_status_locked_keys_timeout
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
@property
|
|
520
|
-
def task_registry(self) -> TaskRegistry:
|
|
521
|
-
"""Get the task registry."""
|
|
522
|
-
return self.addr_conn.task_registry
|
|
523
|
-
|
|
524
|
-
def preprocess_modstatusvar(self, inp: inputs.ModStatusVar) -> inputs.Input:
|
|
525
|
-
"""Fill typeless response with last requested variable type."""
|
|
526
|
-
if inp.orig_var == lcn_defs.Var.UNKNOWN:
|
|
527
|
-
# Response without type (%Msssaaa.wwwww)
|
|
528
|
-
inp.var = self.last_requested_var_without_type_in_response
|
|
529
|
-
|
|
530
|
-
self.last_requested_var_without_type_in_response = lcn_defs.Var.UNKNOWN
|
|
531
|
-
|
|
532
|
-
if self.last_var_lock.locked():
|
|
533
|
-
self.last_var_lock.release()
|
|
534
|
-
else:
|
|
535
|
-
# Response with variable type (%Msssaaa.Avvvwww)
|
|
536
|
-
inp.var = inp.orig_var
|
|
537
|
-
|
|
538
|
-
return inp
|
|
539
|
-
|
|
540
|
-
async def request_status_outputs_timeout(
|
|
541
|
-
self, failed: bool = False, output_port: int = 0
|
|
542
|
-
) -> None:
|
|
543
|
-
"""Is called on output status request timeout."""
|
|
544
|
-
if not failed:
|
|
545
|
-
await self.addr_conn.send_command(
|
|
546
|
-
False, PckGenerator.request_output_status(output_port)
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
async def request_status_relays_timeout(self, failed: bool = False) -> None:
|
|
550
|
-
"""Is called on relay status request timeout."""
|
|
551
|
-
if not failed:
|
|
552
|
-
await self.addr_conn.send_command(
|
|
553
|
-
False, PckGenerator.request_relays_status()
|
|
554
|
-
)
|
|
555
|
-
|
|
556
|
-
async def request_status_motor_positions_timeout(
|
|
557
|
-
self, failed: bool = False, motor_pair: int = 0
|
|
558
|
-
) -> None:
|
|
559
|
-
"""Is called on motor position status request timeout."""
|
|
560
|
-
if not failed:
|
|
561
|
-
await self.addr_conn.send_command(
|
|
562
|
-
False, PckGenerator.request_motor_position_status(motor_pair)
|
|
563
|
-
)
|
|
564
|
-
|
|
565
|
-
async def request_status_bin_sensors_timeout(self, failed: bool = False) -> None:
|
|
566
|
-
"""Is called on binary sensor status request timeout."""
|
|
567
|
-
if not failed:
|
|
568
|
-
await self.addr_conn.send_command(
|
|
569
|
-
False, PckGenerator.request_bin_sensors_status()
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
async def request_status_var_timeout(
|
|
573
|
-
self, failed: bool = False, var: lcn_defs.Var | None = None
|
|
574
|
-
) -> None:
|
|
575
|
-
"""Is called on variable status request timeout."""
|
|
576
|
-
assert var is not None
|
|
577
|
-
# Detect if we can send immediately or if we have to wait for a
|
|
578
|
-
# "typeless" response first
|
|
579
|
-
has_type_in_response = lcn_defs.Var.has_type_in_response(
|
|
580
|
-
var, self.addr_conn.software_serial
|
|
581
|
-
)
|
|
582
|
-
if not has_type_in_response:
|
|
583
|
-
# Use the chance to remove a failed "typeless variable" request
|
|
584
|
-
try:
|
|
585
|
-
await asyncio.wait_for(self.last_var_lock.acquire(), timeout=3.0)
|
|
586
|
-
except asyncio.TimeoutError:
|
|
587
|
-
pass
|
|
588
|
-
self.last_requested_var_without_type_in_response = var
|
|
589
|
-
|
|
590
|
-
# Send variable request
|
|
591
|
-
await self.addr_conn.send_command(
|
|
592
|
-
False,
|
|
593
|
-
PckGenerator.request_var_status(var, self.addr_conn.software_serial),
|
|
594
|
-
)
|
|
595
|
-
|
|
596
|
-
async def request_status_leds_and_logic_ops_timeout(
|
|
597
|
-
self, failed: bool = False
|
|
598
|
-
) -> None:
|
|
599
|
-
"""Is called on leds/logical ops status request timeout."""
|
|
600
|
-
if not failed:
|
|
601
|
-
await self.addr_conn.send_command(
|
|
602
|
-
False, PckGenerator.request_leds_and_logic_ops()
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
async def request_status_locked_keys_timeout(self, failed: bool = False) -> None:
|
|
606
|
-
"""Is called on locked keys status request timeout."""
|
|
607
|
-
if not failed:
|
|
608
|
-
await self.addr_conn.send_command(
|
|
609
|
-
False, PckGenerator.request_key_lock_status()
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
async def activate(self, item: Any, option: Any = None) -> None:
|
|
613
|
-
"""Activate status requests for given item."""
|
|
614
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
615
|
-
# handle variables independently
|
|
616
|
-
if (item in lcn_defs.Var) and (item != lcn_defs.Var.UNKNOWN):
|
|
617
|
-
# wait until we know the software version
|
|
618
|
-
await self.addr_conn.serial_known
|
|
619
|
-
if self.addr_conn.software_serial >= 0x170206:
|
|
620
|
-
timeout = self.settings["MAX_STATUS_EVENTBASED_VALUEAGE"]
|
|
621
|
-
else:
|
|
622
|
-
timeout = self.settings["MAX_STATUS_POLLED_VALUEAGE"]
|
|
623
|
-
self.request_status_vars[item].set_timeout(timeout)
|
|
624
|
-
self.request_status_vars[item].activate()
|
|
625
|
-
elif item in lcn_defs.OutputPort:
|
|
626
|
-
self.request_status_outputs[item.value].activate()
|
|
627
|
-
elif item in lcn_defs.RelayPort:
|
|
628
|
-
self.request_status_relays.activate()
|
|
629
|
-
elif item in lcn_defs.MotorPort:
|
|
630
|
-
self.request_status_relays.activate()
|
|
631
|
-
if option == lcn_defs.MotorPositioningMode.BS4:
|
|
632
|
-
self.request_status_motor_positions[item.value // 2].activate()
|
|
633
|
-
elif item in lcn_defs.BinSensorPort:
|
|
634
|
-
self.request_status_bin_sensors.activate()
|
|
635
|
-
elif item in lcn_defs.LedPort:
|
|
636
|
-
self.request_status_leds_and_logic_ops.activate()
|
|
637
|
-
elif item in lcn_defs.Key:
|
|
638
|
-
self.request_status_locked_keys.activate()
|
|
639
|
-
|
|
640
|
-
async def cancel(self, item: Any) -> None:
|
|
641
|
-
"""Cancel status request for given item."""
|
|
642
|
-
# handle variables independently
|
|
643
|
-
if (item in lcn_defs.Var) and (item != lcn_defs.Var.UNKNOWN):
|
|
644
|
-
await self.request_status_vars[item].cancel()
|
|
645
|
-
self.last_requested_var_without_type_in_response = lcn_defs.Var.UNKNOWN
|
|
646
|
-
elif item in lcn_defs.OutputPort:
|
|
647
|
-
await self.request_status_outputs[item.value].cancel()
|
|
648
|
-
elif item in lcn_defs.RelayPort:
|
|
649
|
-
await self.request_status_relays.cancel()
|
|
650
|
-
elif item in lcn_defs.MotorPort:
|
|
651
|
-
await self.request_status_relays.cancel()
|
|
652
|
-
await self.request_status_motor_positions[item.value // 2].cancel()
|
|
653
|
-
elif item in lcn_defs.BinSensorPort:
|
|
654
|
-
await self.request_status_bin_sensors.cancel()
|
|
655
|
-
elif item in lcn_defs.LedPort:
|
|
656
|
-
await self.request_status_leds_and_logic_ops.cancel()
|
|
657
|
-
elif item in lcn_defs.Key:
|
|
658
|
-
await self.request_status_locked_keys.cancel()
|
|
659
|
-
|
|
660
|
-
async def activate_all(self, activate_s0: bool = False) -> None:
|
|
661
|
-
"""Activate all status requests."""
|
|
662
|
-
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
663
|
-
var_s0s = lcn_defs.Var.s0s()
|
|
664
|
-
for item in (
|
|
665
|
-
list(lcn_defs.OutputPort)
|
|
666
|
-
+ list(lcn_defs.RelayPort)
|
|
667
|
-
+ list(lcn_defs.BinSensorPort)
|
|
668
|
-
+ list(lcn_defs.LedPort)
|
|
669
|
-
+ list(lcn_defs.Key)
|
|
670
|
-
+ list(lcn_defs.Var)
|
|
671
|
-
):
|
|
672
|
-
if isinstance(item, lcn_defs.Var) and item == lcn_defs.Var.UNKNOWN:
|
|
673
|
-
continue
|
|
674
|
-
if (
|
|
675
|
-
(not activate_s0)
|
|
676
|
-
and isinstance(item, lcn_defs.Var)
|
|
677
|
-
and (item in var_s0s)
|
|
678
|
-
):
|
|
679
|
-
continue
|
|
680
|
-
await self.activate(item)
|
|
681
|
-
|
|
682
|
-
async def cancel_all(self) -> None:
|
|
683
|
-
"""Cancel all status requests."""
|
|
684
|
-
for item in (
|
|
685
|
-
list(lcn_defs.OutputPort)
|
|
686
|
-
+ list(lcn_defs.RelayPort)
|
|
687
|
-
+ list(lcn_defs.BinSensorPort)
|
|
688
|
-
+ list(lcn_defs.LedPort)
|
|
689
|
-
+ list(lcn_defs.Key)
|
|
690
|
-
+ list(lcn_defs.Var)
|
|
691
|
-
):
|
|
692
|
-
if isinstance(item, lcn_defs.Var) and item == lcn_defs.Var.UNKNOWN:
|
|
693
|
-
continue
|
|
694
|
-
await self.cancel(item)
|