pypck 0.8.9__tar.gz → 0.8.11__tar.gz
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-0.8.9/pypck.egg-info → pypck-0.8.11}/PKG-INFO +1 -1
- pypck-0.8.11/VERSION +1 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/connection.py +4 -4
- {pypck-0.8.9 → pypck-0.8.11}/pypck/helpers.py +3 -3
- {pypck-0.8.9 → pypck-0.8.11}/pypck/lcn_defs.py +46 -44
- pypck-0.8.11/pypck/py.typed +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/request_handlers.py +2 -1
- {pypck-0.8.9 → pypck-0.8.11/pypck.egg-info}/PKG-INFO +1 -1
- {pypck-0.8.9 → pypck-0.8.11}/pypck.egg-info/SOURCES.txt +2 -0
- {pypck-0.8.9 → pypck-0.8.11}/pyproject.toml +3 -0
- {pypck-0.8.9 → pypck-0.8.11}/tests/test_commands.py +20 -16
- pypck-0.8.11/tests/test_connection.py +155 -0
- {pypck-0.8.9 → pypck-0.8.11}/tests/test_dyn_text.py +13 -12
- pypck-0.8.11/tests/test_input.py +46 -0
- {pypck-0.8.9 → pypck-0.8.11}/tests/test_messages.py +6 -1
- {pypck-0.8.9 → pypck-0.8.11}/tests/test_vars.py +4 -2
- pypck-0.8.9/VERSION +0 -1
- pypck-0.8.9/tests/test_connection.py +0 -420
- {pypck-0.8.9 → pypck-0.8.11}/LICENSE +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/README.md +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/__init__.py +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/inputs.py +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/lcn_addr.py +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/module.py +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/pck_commands.py +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck/timeout_retry.py +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck.egg-info/dependency_links.txt +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck.egg-info/not-zip-safe +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/pypck.egg-info/top_level.txt +0 -0
- {pypck-0.8.9 → pypck-0.8.11}/setup.cfg +0 -0
pypck-0.8.11/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.8.11
|
|
@@ -13,7 +13,7 @@ from pypck import inputs, lcn_defs
|
|
|
13
13
|
from pypck.helpers import TaskRegistry
|
|
14
14
|
from pypck.lcn_addr import LcnAddr
|
|
15
15
|
from pypck.lcn_defs import LcnEvent
|
|
16
|
-
from pypck.module import
|
|
16
|
+
from pypck.module import GroupConnection, ModuleConnection
|
|
17
17
|
from pypck.pck_commands import PckGenerator
|
|
18
18
|
|
|
19
19
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -243,7 +243,7 @@ class PchkConnectionManager:
|
|
|
243
243
|
if isinstance(exc, (ConnectionRefusedError, OSError)):
|
|
244
244
|
raise PchkConnectionRefusedError()
|
|
245
245
|
else:
|
|
246
|
-
raise
|
|
246
|
+
raise exc
|
|
247
247
|
|
|
248
248
|
if pending:
|
|
249
249
|
for awaitable in pending:
|
|
@@ -405,8 +405,8 @@ class PchkConnectionManager:
|
|
|
405
405
|
|
|
406
406
|
def get_address_conn(
|
|
407
407
|
self, addr: LcnAddr, request_serials: bool = True
|
|
408
|
-
) ->
|
|
409
|
-
"""Create and/or return
|
|
408
|
+
) -> ModuleConnection | GroupConnection:
|
|
409
|
+
"""Create and/or return a connection to the given module or group."""
|
|
410
410
|
if addr.is_group:
|
|
411
411
|
return self.get_group_conn(addr)
|
|
412
412
|
return self.get_module_conn(addr, request_serials)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Helper functions for pypck."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
from collections.abc import
|
|
4
|
+
from collections.abc import Coroutine
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
|
|
@@ -30,9 +30,9 @@ class TaskRegistry:
|
|
|
30
30
|
if task in self.tasks:
|
|
31
31
|
self.tasks.remove(task)
|
|
32
32
|
|
|
33
|
-
def create_task(self, coro:
|
|
33
|
+
def create_task(self, coro: Coroutine[Any, Any, Any]) -> "asyncio.Task[None]":
|
|
34
34
|
"""Create a task and store a reference in the task registry."""
|
|
35
|
-
task = asyncio.create_task(coro)
|
|
35
|
+
task: asyncio.Task[Any] = asyncio.create_task(coro)
|
|
36
36
|
task.add_done_callback(self.remove_task)
|
|
37
37
|
self.tasks.append(task)
|
|
38
38
|
return task
|
|
@@ -360,6 +360,44 @@ class Var(Enum):
|
|
|
360
360
|
S0INPUT3 = auto()
|
|
361
361
|
S0INPUT4 = auto() # LCN-BU4LJVarValue
|
|
362
362
|
|
|
363
|
+
@classmethod
|
|
364
|
+
def variables(cls) -> list[Var]:
|
|
365
|
+
"""Return a list of all variable types."""
|
|
366
|
+
return [
|
|
367
|
+
cls.VAR1ORTVAR,
|
|
368
|
+
cls.VAR2ORR1VAR,
|
|
369
|
+
cls.VAR3ORR2VAR,
|
|
370
|
+
cls.VAR4,
|
|
371
|
+
cls.VAR5,
|
|
372
|
+
cls.VAR6,
|
|
373
|
+
cls.VAR7,
|
|
374
|
+
cls.VAR8,
|
|
375
|
+
cls.VAR9,
|
|
376
|
+
cls.VAR10,
|
|
377
|
+
cls.VAR11,
|
|
378
|
+
cls.VAR12,
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
@classmethod
|
|
382
|
+
def set_points(cls) -> list[Var]:
|
|
383
|
+
"""Return a list of all set-point variable types."""
|
|
384
|
+
return [cls.R1VARSETPOINT, cls.R2VARSETPOINT]
|
|
385
|
+
|
|
386
|
+
@classmethod
|
|
387
|
+
def thresholds(cls) -> list[list[Var]]:
|
|
388
|
+
"""Return a list of all threshold variable types."""
|
|
389
|
+
return [
|
|
390
|
+
[cls.THRS1, cls.THRS2, cls.THRS3, cls.THRS4, cls.THRS5],
|
|
391
|
+
[cls.THRS2_1, cls.THRS2_2, cls.THRS2_3, cls.THRS2_4],
|
|
392
|
+
[cls.THRS3_1, cls.THRS3_2, cls.THRS3_3, cls.THRS3_4],
|
|
393
|
+
[cls.THRS4_1, cls.THRS4_2, cls.THRS4_3, cls.THRS4_4],
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
@classmethod
|
|
397
|
+
def s0s(cls) -> list[Var]:
|
|
398
|
+
"""Return a list of all S0-input variable types."""
|
|
399
|
+
return [cls.S0INPUT1, cls.S0INPUT2, cls.S0INPUT3, cls.S0INPUT4]
|
|
400
|
+
|
|
363
401
|
@staticmethod
|
|
364
402
|
def var_id_to_var(var_id: int) -> Var:
|
|
365
403
|
"""Translate a given id into a variable type.
|
|
@@ -369,9 +407,9 @@ class Var(Enum):
|
|
|
369
407
|
:returns: The translated variable enum.
|
|
370
408
|
:rtype: Var
|
|
371
409
|
"""
|
|
372
|
-
if (var_id < 0) or (var_id >= len(Var.variables)):
|
|
410
|
+
if (var_id < 0) or (var_id >= len(Var.variables())):
|
|
373
411
|
raise ValueError("Bad var_id.")
|
|
374
|
-
return Var.variables[var_id]
|
|
412
|
+
return Var.variables()[var_id]
|
|
375
413
|
|
|
376
414
|
@staticmethod
|
|
377
415
|
def set_point_id_to_var(set_point_id: int) -> Var:
|
|
@@ -382,9 +420,9 @@ class Var(Enum):
|
|
|
382
420
|
:return: The translated var
|
|
383
421
|
:rtype: Var
|
|
384
422
|
"""
|
|
385
|
-
if (set_point_id < 0) or (set_point_id >= len(Var.set_points)):
|
|
423
|
+
if (set_point_id < 0) or (set_point_id >= len(Var.set_points())):
|
|
386
424
|
raise ValueError("Bad set_point_id.")
|
|
387
|
-
return Var.set_points[set_point_id]
|
|
425
|
+
return Var.set_points()[set_point_id]
|
|
388
426
|
|
|
389
427
|
@staticmethod
|
|
390
428
|
def thrs_id_to_var(register_id: int, thrs_id: int) -> Var:
|
|
@@ -399,12 +437,12 @@ class Var(Enum):
|
|
|
399
437
|
"""
|
|
400
438
|
if (
|
|
401
439
|
(register_id < 0)
|
|
402
|
-
or (register_id >= len(Var.thresholds))
|
|
440
|
+
or (register_id >= len(Var.thresholds()))
|
|
403
441
|
or (thrs_id < 0)
|
|
404
442
|
or (thrs_id >= (5 if (register_id == 0) else 4))
|
|
405
443
|
):
|
|
406
444
|
raise ValueError("Bad register_id and/or thrs_id.")
|
|
407
|
-
return Var.thresholds[register_id][thrs_id]
|
|
445
|
+
return Var.thresholds()[register_id][thrs_id]
|
|
408
446
|
|
|
409
447
|
@staticmethod
|
|
410
448
|
def s0_id_to_var(s0_id: int) -> Var:
|
|
@@ -415,9 +453,9 @@ class Var(Enum):
|
|
|
415
453
|
:return: The translated var
|
|
416
454
|
:rtype: Var
|
|
417
455
|
"""
|
|
418
|
-
if (s0_id < 0) or (s0_id >= len(Var.s0s)):
|
|
456
|
+
if (s0_id < 0) or (s0_id >= len(Var.s0s())):
|
|
419
457
|
raise ValueError("Bad s0_id.")
|
|
420
|
-
return Var.s0s[s0_id]
|
|
458
|
+
return Var.s0s()[s0_id]
|
|
421
459
|
|
|
422
460
|
@staticmethod
|
|
423
461
|
def to_var_id(var: Var) -> int:
|
|
@@ -658,42 +696,6 @@ class Var(Enum):
|
|
|
658
696
|
return (not lock_state) and (software_serial < 0x170206)
|
|
659
697
|
|
|
660
698
|
|
|
661
|
-
# Helper list to get var by numeric id.
|
|
662
|
-
Var.variables = [ # type: ignore
|
|
663
|
-
Var.VAR1ORTVAR,
|
|
664
|
-
Var.VAR2ORR1VAR,
|
|
665
|
-
Var.VAR3ORR2VAR,
|
|
666
|
-
Var.VAR4,
|
|
667
|
-
Var.VAR5,
|
|
668
|
-
Var.VAR6,
|
|
669
|
-
Var.VAR7,
|
|
670
|
-
Var.VAR8,
|
|
671
|
-
Var.VAR9,
|
|
672
|
-
Var.VAR10,
|
|
673
|
-
Var.VAR11,
|
|
674
|
-
Var.VAR12,
|
|
675
|
-
]
|
|
676
|
-
|
|
677
|
-
# Helper list to get set-point var by numeric id.
|
|
678
|
-
Var.set_points = [Var.R1VARSETPOINT, Var.R2VARSETPOINT] # type: ignore
|
|
679
|
-
|
|
680
|
-
# Helper list to get threshold var by numeric id.
|
|
681
|
-
Var.thresholds = [ # type: ignore
|
|
682
|
-
[Var.THRS1, Var.THRS2, Var.THRS3, Var.THRS4, Var.THRS5],
|
|
683
|
-
[Var.THRS2_1, Var.THRS2_2, Var.THRS2_3, Var.THRS2_4],
|
|
684
|
-
[Var.THRS3_1, Var.THRS3_2, Var.THRS3_3, Var.THRS3_4],
|
|
685
|
-
[Var.THRS4_1, Var.THRS4_2, Var.THRS4_3, Var.THRS4_4],
|
|
686
|
-
]
|
|
687
|
-
|
|
688
|
-
# Helper list to get S0-input var by numeric id.
|
|
689
|
-
Var.s0s = [ # type: ignore
|
|
690
|
-
Var.S0INPUT1,
|
|
691
|
-
Var.S0INPUT2,
|
|
692
|
-
Var.S0INPUT3,
|
|
693
|
-
Var.S0INPUT4,
|
|
694
|
-
]
|
|
695
|
-
|
|
696
|
-
|
|
697
699
|
class VarUnit(Enum):
|
|
698
700
|
"""Measurement units used with LCN variables."""
|
|
699
701
|
|
|
File without changes
|
|
@@ -660,6 +660,7 @@ class StatusRequestsHandler:
|
|
|
660
660
|
async def activate_all(self, activate_s0: bool = False) -> None:
|
|
661
661
|
"""Activate all status requests."""
|
|
662
662
|
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
663
|
+
var_s0s = lcn_defs.Var.s0s()
|
|
663
664
|
for item in (
|
|
664
665
|
list(lcn_defs.OutputPort)
|
|
665
666
|
+ list(lcn_defs.RelayPort)
|
|
@@ -673,7 +674,7 @@ class StatusRequestsHandler:
|
|
|
673
674
|
if (
|
|
674
675
|
(not activate_s0)
|
|
675
676
|
and isinstance(item, lcn_defs.Var)
|
|
676
|
-
and (item in
|
|
677
|
+
and (item in var_s0s)
|
|
677
678
|
):
|
|
678
679
|
continue
|
|
679
680
|
await self.activate(item)
|
|
@@ -11,6 +11,7 @@ pypck/lcn_addr.py
|
|
|
11
11
|
pypck/lcn_defs.py
|
|
12
12
|
pypck/module.py
|
|
13
13
|
pypck/pck_commands.py
|
|
14
|
+
pypck/py.typed
|
|
14
15
|
pypck/request_handlers.py
|
|
15
16
|
pypck/timeout_retry.py
|
|
16
17
|
pypck.egg-info/PKG-INFO
|
|
@@ -21,5 +22,6 @@ pypck.egg-info/top_level.txt
|
|
|
21
22
|
tests/test_commands.py
|
|
22
23
|
tests/test_connection.py
|
|
23
24
|
tests/test_dyn_text.py
|
|
25
|
+
tests/test_input.py
|
|
24
26
|
tests/test_messages.py
|
|
25
27
|
tests/test_vars.py
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Tests for command generation directed at bus modules and groups."""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
import pytest
|
|
4
6
|
|
|
5
7
|
from pypck.lcn_addr import LcnAddr
|
|
@@ -24,7 +26,7 @@ from pypck.pck_commands import PckGenerator
|
|
|
24
26
|
|
|
25
27
|
NEW_VAR_SW_AGE = 0x170206
|
|
26
28
|
|
|
27
|
-
COMMANDS = {
|
|
29
|
+
COMMANDS: dict[str | bytes, Any] = {
|
|
28
30
|
# Host commands
|
|
29
31
|
**{
|
|
30
32
|
f"^ping{counter:d}": (PckGenerator.ping, counter)
|
|
@@ -93,7 +95,7 @@ COMMANDS = {
|
|
|
93
95
|
var,
|
|
94
96
|
NEW_VAR_SW_AGE,
|
|
95
97
|
)
|
|
96
|
-
for var in Var.variables
|
|
98
|
+
for var in Var.variables()
|
|
97
99
|
},
|
|
98
100
|
**{
|
|
99
101
|
f"MWS{Var.to_set_point_id(var) + 1:03d}": (
|
|
@@ -101,7 +103,7 @@ COMMANDS = {
|
|
|
101
103
|
var,
|
|
102
104
|
NEW_VAR_SW_AGE,
|
|
103
105
|
)
|
|
104
|
-
for var in Var.set_points
|
|
106
|
+
for var in Var.set_points()
|
|
105
107
|
},
|
|
106
108
|
**{
|
|
107
109
|
f"MWC{Var.to_s0_id(var) + 1:03d}": (
|
|
@@ -109,7 +111,7 @@ COMMANDS = {
|
|
|
109
111
|
var,
|
|
110
112
|
NEW_VAR_SW_AGE,
|
|
111
113
|
)
|
|
112
|
-
for var in Var.s0s
|
|
114
|
+
for var in Var.s0s()
|
|
113
115
|
},
|
|
114
116
|
**{
|
|
115
117
|
f"SE{Var.to_thrs_register_id(var) + 1:03d}": (
|
|
@@ -117,7 +119,7 @@ COMMANDS = {
|
|
|
117
119
|
var,
|
|
118
120
|
NEW_VAR_SW_AGE,
|
|
119
121
|
)
|
|
120
|
-
for reg in Var.thresholds
|
|
122
|
+
for reg in Var.thresholds()
|
|
121
123
|
for var in reg
|
|
122
124
|
},
|
|
123
125
|
# Variable status (legacy commands)
|
|
@@ -128,7 +130,7 @@ COMMANDS = {
|
|
|
128
130
|
"MWSB": (PckGenerator.request_var_status, Var.R2VARSETPOINT, NEW_VAR_SW_AGE - 1),
|
|
129
131
|
**{
|
|
130
132
|
"SL1": (PckGenerator.request_var_status, var, NEW_VAR_SW_AGE - 1)
|
|
131
|
-
for var in Var.thresholds[0]
|
|
133
|
+
for var in Var.thresholds()[0]
|
|
132
134
|
},
|
|
133
135
|
# Output manipulation
|
|
134
136
|
**{
|
|
@@ -305,7 +307,7 @@ COMMANDS = {
|
|
|
305
307
|
# Variable manipulation
|
|
306
308
|
**{
|
|
307
309
|
f"X2{var.value | 0x40:03d}016225": (PckGenerator.update_status_var, var, 4321)
|
|
308
|
-
for var in Var.variables
|
|
310
|
+
for var in Var.variables()
|
|
309
311
|
},
|
|
310
312
|
"X2030044129": (PckGenerator.var_abs, Var.R1VARSETPOINT, 4201),
|
|
311
313
|
"X2030108129": (PckGenerator.var_abs, Var.R2VARSETPOINT, 4201),
|
|
@@ -314,7 +316,7 @@ COMMANDS = {
|
|
|
314
316
|
"ZS30000": (PckGenerator.var_reset, Var.TVAR, 0x170205),
|
|
315
317
|
**{
|
|
316
318
|
f"Z-{var.value + 1:03d}4090": (PckGenerator.var_reset, var, 0x170206)
|
|
317
|
-
for var in Var.variables
|
|
319
|
+
for var in Var.variables()
|
|
318
320
|
},
|
|
319
321
|
"ZA23423": (PckGenerator.var_rel, Var.TVAR, RelVarRef.CURRENT, 23423, 0x170205),
|
|
320
322
|
"ZS23423": (PckGenerator.var_rel, Var.TVAR, RelVarRef.CURRENT, -23423, 0x170205),
|
|
@@ -326,7 +328,7 @@ COMMANDS = {
|
|
|
326
328
|
-3000,
|
|
327
329
|
0x170206,
|
|
328
330
|
)
|
|
329
|
-
for var in Var.variables
|
|
331
|
+
for var in Var.variables()
|
|
330
332
|
if var != Var.TVAR
|
|
331
333
|
},
|
|
332
334
|
**{
|
|
@@ -337,7 +339,7 @@ COMMANDS = {
|
|
|
337
339
|
-500,
|
|
338
340
|
sw_age,
|
|
339
341
|
)
|
|
340
|
-
for nvar, var in enumerate(Var.set_points)
|
|
342
|
+
for nvar, var in enumerate(Var.set_points())
|
|
341
343
|
for nref, ref in enumerate(RelVarRef)
|
|
342
344
|
for sw_age in (0x170206, 0x170205)
|
|
343
345
|
},
|
|
@@ -349,14 +351,14 @@ COMMANDS = {
|
|
|
349
351
|
500,
|
|
350
352
|
sw_age,
|
|
351
353
|
)
|
|
352
|
-
for nvar, var in enumerate(Var.set_points)
|
|
354
|
+
for nvar, var in enumerate(Var.set_points())
|
|
353
355
|
for nref, ref in enumerate(RelVarRef)
|
|
354
356
|
for sw_age in (0x170206, 0x170205)
|
|
355
357
|
},
|
|
356
358
|
**{
|
|
357
359
|
f"SS{('R', 'E')[nref]}0500SR{r + 1}{i + 1}": (
|
|
358
360
|
PckGenerator.var_rel,
|
|
359
|
-
Var.thresholds[r][i],
|
|
361
|
+
Var.thresholds()[r][i],
|
|
360
362
|
ref,
|
|
361
363
|
-500,
|
|
362
364
|
0x170206,
|
|
@@ -368,7 +370,7 @@ COMMANDS = {
|
|
|
368
370
|
**{
|
|
369
371
|
f"SS{('R', 'E')[nref]}0500AR{r + 1}{i + 1}": (
|
|
370
372
|
PckGenerator.var_rel,
|
|
371
|
-
Var.thresholds[r][i],
|
|
373
|
+
Var.thresholds()[r][i],
|
|
372
374
|
ref,
|
|
373
375
|
500,
|
|
374
376
|
0x170206,
|
|
@@ -380,7 +382,7 @@ COMMANDS = {
|
|
|
380
382
|
**{
|
|
381
383
|
f"SS{('R', 'E')[nref]}0500S{1 << (4 - i):05b}": (
|
|
382
384
|
PckGenerator.var_rel,
|
|
383
|
-
Var.thresholds[0][i],
|
|
385
|
+
Var.thresholds()[0][i],
|
|
384
386
|
ref,
|
|
385
387
|
-500,
|
|
386
388
|
0x170205,
|
|
@@ -391,7 +393,7 @@ COMMANDS = {
|
|
|
391
393
|
**{
|
|
392
394
|
f"SS{('R', 'E')[nref]}0500A{1 << (4 - i):05b}": (
|
|
393
395
|
PckGenerator.var_rel,
|
|
394
|
-
Var.thresholds[0][i],
|
|
396
|
+
Var.thresholds()[0][i],
|
|
395
397
|
ref,
|
|
396
398
|
500,
|
|
397
399
|
0x170205,
|
|
@@ -562,6 +564,8 @@ COMMANDS = {
|
|
|
562
564
|
|
|
563
565
|
|
|
564
566
|
@pytest.mark.parametrize("expected, command", COMMANDS.items())
|
|
565
|
-
def test_command_generation_single_mod_noack(
|
|
567
|
+
def test_command_generation_single_mod_noack(
|
|
568
|
+
expected: str, command: tuple[Any, ...]
|
|
569
|
+
) -> None:
|
|
566
570
|
"""Test if InputMod parses message correctly."""
|
|
567
571
|
assert expected == command[0](*command[1:])
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Connection tests."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from unittest.mock import AsyncMock, Mock, call, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from pypck import inputs
|
|
9
|
+
from pypck.connection import (
|
|
10
|
+
PchkAuthenticationError,
|
|
11
|
+
PchkConnectionFailedError,
|
|
12
|
+
PchkConnectionManager,
|
|
13
|
+
PchkConnectionRefusedError,
|
|
14
|
+
PchkLicenseError,
|
|
15
|
+
)
|
|
16
|
+
from pypck.lcn_defs import LcnEvent
|
|
17
|
+
from pypck.pck_commands import PckGenerator
|
|
18
|
+
|
|
19
|
+
from .conftest import HOST, PASSWORD, PORT, USERNAME, MockPchkConnectionManager
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def test_close_without_connect(pypck_client: MockPchkConnectionManager) -> None:
|
|
23
|
+
"""Test closing of PchkConnectionManager without connecting."""
|
|
24
|
+
await pypck_client.async_close()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@patch.object(PchkConnectionManager, "open_connection")
|
|
28
|
+
@patch.object(PchkConnectionManager, "scan_segment_couplers")
|
|
29
|
+
async def test_async_connect(
|
|
30
|
+
mock_scan_segment_couplers: AsyncMock,
|
|
31
|
+
mock_open_connection: AsyncMock,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Test successful connection."""
|
|
34
|
+
pypck_client = PchkConnectionManager(HOST, PORT, USERNAME, PASSWORD)
|
|
35
|
+
connect_task = asyncio.create_task(pypck_client.async_connect())
|
|
36
|
+
await asyncio.sleep(0)
|
|
37
|
+
|
|
38
|
+
pypck_client.license_error_future.set_result(True)
|
|
39
|
+
pypck_client.authentication_completed_future.set_result(True)
|
|
40
|
+
pypck_client.segment_scan_completed_event.set()
|
|
41
|
+
|
|
42
|
+
await connect_task
|
|
43
|
+
|
|
44
|
+
mock_scan_segment_couplers.assert_awaited()
|
|
45
|
+
mock_open_connection.assert_awaited()
|
|
46
|
+
assert pypck_client.is_ready()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@patch.object(PchkConnectionManager, "ping")
|
|
50
|
+
@patch.object(PchkConnectionManager, "open_connection")
|
|
51
|
+
@patch.object(PchkConnectionManager, "scan_segment_couplers")
|
|
52
|
+
@patch.object(PchkConnectionManager, "send_command")
|
|
53
|
+
async def test_successful_connection_procedure(
|
|
54
|
+
mock_send_command: AsyncMock,
|
|
55
|
+
mock_scan_segment_couplers: AsyncMock,
|
|
56
|
+
mock_open_connection: AsyncMock,
|
|
57
|
+
mock_ping: AsyncMock,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Test successful connection procedure."""
|
|
60
|
+
pypck_client = PchkConnectionManager(HOST, PORT, USERNAME, PASSWORD)
|
|
61
|
+
connect_task = asyncio.create_task(pypck_client.async_connect())
|
|
62
|
+
await asyncio.sleep(0)
|
|
63
|
+
|
|
64
|
+
await pypck_client.async_process_input(inputs.AuthUsername())
|
|
65
|
+
mock_send_command.assert_awaited_with(USERNAME, to_host=True)
|
|
66
|
+
|
|
67
|
+
await pypck_client.async_process_input(inputs.AuthPassword())
|
|
68
|
+
mock_send_command.assert_awaited_with(PASSWORD, to_host=True)
|
|
69
|
+
|
|
70
|
+
await pypck_client.async_process_input(inputs.AuthOk())
|
|
71
|
+
mock_send_command.assert_awaited_with(PckGenerator.set_dec_mode(), to_host=True)
|
|
72
|
+
assert pypck_client.authentication_completed_future.result()
|
|
73
|
+
|
|
74
|
+
await pypck_client.async_process_input(inputs.DecModeSet())
|
|
75
|
+
mock_send_command.assert_awaited_with(
|
|
76
|
+
PckGenerator.set_operation_mode(
|
|
77
|
+
pypck_client.dim_mode, pypck_client.status_mode
|
|
78
|
+
),
|
|
79
|
+
to_host=True,
|
|
80
|
+
)
|
|
81
|
+
assert pypck_client.license_error_future.result()
|
|
82
|
+
|
|
83
|
+
await connect_task
|
|
84
|
+
|
|
85
|
+
mock_open_connection.assert_awaited()
|
|
86
|
+
mock_scan_segment_couplers.assert_awaited()
|
|
87
|
+
mock_ping.assert_awaited()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@pytest.mark.parametrize("side_effect", [ConnectionRefusedError, OSError])
|
|
91
|
+
async def test_connection_error(side_effect: ConnectionRefusedError | OSError) -> None:
|
|
92
|
+
"""Test connection error."""
|
|
93
|
+
with (
|
|
94
|
+
patch.object(PchkConnectionManager, "open_connection", side_effect=side_effect),
|
|
95
|
+
pytest.raises(PchkConnectionRefusedError),
|
|
96
|
+
):
|
|
97
|
+
pypck_client = PchkConnectionManager(HOST, PORT, USERNAME, PASSWORD)
|
|
98
|
+
await pypck_client.async_connect()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@patch.object(PchkConnectionManager, "open_connection")
|
|
102
|
+
async def test_authentication_error(mock_open_connection: AsyncMock) -> None:
|
|
103
|
+
"""Test wrong login credentials."""
|
|
104
|
+
pypck_client = PchkConnectionManager(HOST, PORT, USERNAME, PASSWORD)
|
|
105
|
+
connect_task = asyncio.create_task(pypck_client.async_connect())
|
|
106
|
+
await asyncio.sleep(0)
|
|
107
|
+
await pypck_client.async_process_input(inputs.AuthFailed())
|
|
108
|
+
|
|
109
|
+
with (
|
|
110
|
+
pytest.raises(PchkAuthenticationError),
|
|
111
|
+
):
|
|
112
|
+
await connect_task
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@patch.object(PchkConnectionManager, "open_connection")
|
|
116
|
+
async def test_license_error(mock_open_connection: AsyncMock) -> None:
|
|
117
|
+
"""Test wrong login credentials."""
|
|
118
|
+
pypck_client = PchkConnectionManager(HOST, PORT, USERNAME, PASSWORD)
|
|
119
|
+
connect_task = asyncio.create_task(pypck_client.async_connect())
|
|
120
|
+
await asyncio.sleep(0)
|
|
121
|
+
await pypck_client.async_process_input(inputs.LicenseError())
|
|
122
|
+
|
|
123
|
+
with (
|
|
124
|
+
pytest.raises(PchkLicenseError),
|
|
125
|
+
):
|
|
126
|
+
await connect_task
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@patch.object(PchkConnectionManager, "open_connection")
|
|
130
|
+
async def test_timeout_error(mock_open_connection: AsyncMock) -> None:
|
|
131
|
+
"""Test timeout when connecting."""
|
|
132
|
+
with pytest.raises(PchkConnectionFailedError):
|
|
133
|
+
pypck_client = PchkConnectionManager(HOST, PORT, USERNAME, PASSWORD)
|
|
134
|
+
await pypck_client.async_connect(timeout=0)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def test_lcn_connected(pypck_client: MockPchkConnectionManager) -> None:
|
|
138
|
+
"""Test lcn connected events."""
|
|
139
|
+
event_callback = Mock()
|
|
140
|
+
pypck_client.register_for_events(event_callback)
|
|
141
|
+
await pypck_client.async_connect()
|
|
142
|
+
|
|
143
|
+
# bus disconnected
|
|
144
|
+
await pypck_client.async_process_input(inputs.LcnConnState(is_lcn_connected=False))
|
|
145
|
+
assert not pypck_client.is_lcn_connected
|
|
146
|
+
event_callback.assert_has_calls(
|
|
147
|
+
(call(LcnEvent.BUS_CONNECTION_STATUS_CHANGED), call(LcnEvent.BUS_DISCONNECTED))
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# bus connected
|
|
151
|
+
await pypck_client.async_process_input(inputs.LcnConnState(is_lcn_connected=True))
|
|
152
|
+
assert pypck_client.is_lcn_connected
|
|
153
|
+
event_callback.assert_has_calls(
|
|
154
|
+
(call(LcnEvent.BUS_CONNECTION_STATUS_CHANGED), call(LcnEvent.BUS_CONNECTED))
|
|
155
|
+
)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Module connection tests."""
|
|
2
2
|
|
|
3
|
-
from unittest.mock import patch
|
|
4
|
-
|
|
5
3
|
import pytest
|
|
4
|
+
|
|
6
5
|
from pypck.lcn_addr import LcnAddr
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
from .conftest import MockPchkConnectionManager
|
|
8
8
|
|
|
9
9
|
TEST_VECTORS = {
|
|
10
10
|
# empty
|
|
@@ -55,19 +55,20 @@ TEST_VECTORS = {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
@pytest.mark.asyncio
|
|
59
58
|
@pytest.mark.parametrize("text, parts", TEST_VECTORS.items())
|
|
60
|
-
async def test_dyn_text(
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
async def test_dyn_text(
|
|
60
|
+
pypck_client: MockPchkConnectionManager,
|
|
61
|
+
text: str,
|
|
62
|
+
parts: tuple[bytes, bytes, bytes, bytes, bytes],
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Tests for dynamic text."""
|
|
63
65
|
module = pypck_client.get_address_conn(LcnAddr(0, 10, False))
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
await module.dyn_text(3, text)
|
|
67
|
+
await module.dyn_text(3, text)
|
|
67
68
|
|
|
68
|
-
send_command.assert_awaited()
|
|
69
|
-
await_args = (call.args for call in send_command.await_args_list)
|
|
69
|
+
module.send_command.assert_awaited() # type: ignore[attr-defined]
|
|
70
|
+
await_args = (call.args for call in module.send_command.await_args_list) # type: ignore[attr-defined]
|
|
70
71
|
_, commands = zip(*await_args)
|
|
71
72
|
|
|
72
73
|
for i, part in enumerate(parts):
|
|
73
|
-
assert f"GTDT4{i+1:d}".encode() + part in commands
|
|
74
|
+
assert f"GTDT4{i + 1:d}".encode() + part in commands
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Test the data flow for Input objects."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
|
|
5
|
+
from pypck.inputs import Input, ModInput
|
|
6
|
+
from pypck.lcn_addr import LcnAddr
|
|
7
|
+
from pypck.module import ModuleConnection
|
|
8
|
+
|
|
9
|
+
from .conftest import MockPchkConnectionManager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def test_message_to_input(pypck_client: MockPchkConnectionManager) -> None:
|
|
13
|
+
"""Test data flow from message to input."""
|
|
14
|
+
inp = Input()
|
|
15
|
+
message = "dummy_message"
|
|
16
|
+
with patch.object(
|
|
17
|
+
pypck_client, "async_process_input"
|
|
18
|
+
) as pypck_client_process_input:
|
|
19
|
+
with patch("pypck.inputs.InputParser.parse", return_value=[inp]) as inp_parse:
|
|
20
|
+
await pypck_client.process_message(message)
|
|
21
|
+
|
|
22
|
+
inp_parse.assert_called_with(message)
|
|
23
|
+
pypck_client_process_input.assert_awaited_with(inp)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def test_physical_to_logical_segment_id(
|
|
27
|
+
pypck_client: MockPchkConnectionManager,
|
|
28
|
+
) -> None:
|
|
29
|
+
"""Test conversion from logical to physical segment id."""
|
|
30
|
+
pypck_client.local_seg_id = 20
|
|
31
|
+
module = pypck_client.get_address_conn(LcnAddr(20, 7, False))
|
|
32
|
+
assert isinstance(module, ModuleConnection)
|
|
33
|
+
with (
|
|
34
|
+
patch("tests.conftest.MockPchkConnectionManager.is_ready", return_value=True),
|
|
35
|
+
patch.object(module, "async_process_input") as module_process_input,
|
|
36
|
+
):
|
|
37
|
+
inp = ModInput(LcnAddr(20, 7, False))
|
|
38
|
+
await pypck_client.async_process_input(inp)
|
|
39
|
+
|
|
40
|
+
inp = ModInput(LcnAddr(0, 7, False))
|
|
41
|
+
await pypck_client.async_process_input(inp)
|
|
42
|
+
|
|
43
|
+
inp = ModInput(LcnAddr(4, 7, False))
|
|
44
|
+
await pypck_client.async_process_input(inp)
|
|
45
|
+
|
|
46
|
+
assert module_process_input.await_count == 3
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Tests for input message parsing for bus messages."""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
import pytest
|
|
4
6
|
|
|
5
7
|
from pypck.inputs import (
|
|
@@ -348,7 +350,10 @@ MESSAGES = {
|
|
|
348
350
|
|
|
349
351
|
|
|
350
352
|
@pytest.mark.parametrize("message, expected", MESSAGES.items())
|
|
351
|
-
def test_message_parsing_mod_inputs(
|
|
353
|
+
def test_message_parsing_mod_inputs(
|
|
354
|
+
message: str,
|
|
355
|
+
expected: list[tuple[Any, ...]],
|
|
356
|
+
) -> None:
|
|
352
357
|
"""Test if InputMod parses message correctly."""
|
|
353
358
|
inputs = InputParser.parse(message)
|
|
354
359
|
assert len(inputs) == len(expected)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Tests for variable value and unit handling."""
|
|
2
2
|
|
|
3
3
|
import math
|
|
4
|
+
|
|
4
5
|
import pytest
|
|
6
|
+
|
|
5
7
|
from pypck.lcn_defs import VarUnit, VarValue
|
|
6
8
|
|
|
7
9
|
VARIABLE_TEST_VALUES = (
|
|
@@ -108,7 +110,7 @@ CALIBRATION_TEST_VECTORS = (
|
|
|
108
110
|
|
|
109
111
|
|
|
110
112
|
@pytest.mark.parametrize("unit, native, expected", ROUNDTRIP_TEST_VECTORS)
|
|
111
|
-
def test_roundtrip(unit, native, expected):
|
|
113
|
+
def test_roundtrip(unit: VarUnit, native: int, expected: VarValue) -> None:
|
|
112
114
|
"""Test that variable conversion roundtrips."""
|
|
113
115
|
assert (
|
|
114
116
|
expected
|
|
@@ -119,6 +121,6 @@ def test_roundtrip(unit, native, expected):
|
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
@pytest.mark.parametrize("unit, native, value", CALIBRATION_TEST_VECTORS)
|
|
122
|
-
def test_calibration(unit, native, value):
|
|
124
|
+
def test_calibration(unit: VarUnit, native: int, value: int | float) -> None:
|
|
123
125
|
"""Test proper calibration of variable conversion."""
|
|
124
126
|
assert value == VarValue.to_var_unit(VarValue.from_native(native), unit)
|
pypck-0.8.9/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.8.9
|
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
"""Connection tests."""
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import json
|
|
5
|
-
from unittest.mock import Mock, call
|
|
6
|
-
|
|
7
|
-
import pytest
|
|
8
|
-
|
|
9
|
-
from pypck.connection import (
|
|
10
|
-
PchkAuthenticationError,
|
|
11
|
-
PchkConnectionFailedError,
|
|
12
|
-
PchkConnectionRefusedError,
|
|
13
|
-
PchkLicenseError,
|
|
14
|
-
)
|
|
15
|
-
from pypck.lcn_addr import LcnAddr
|
|
16
|
-
from pypck.lcn_defs import LcnEvent
|
|
17
|
-
from pypck.module import ModuleConnection
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@pytest.mark.asyncio
|
|
21
|
-
async def test_close_without_connect(pypck_client):
|
|
22
|
-
"""Test closing of PchkConnectionManager without connecting."""
|
|
23
|
-
await pypck_client.async_close()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@pytest.mark.asyncio
|
|
27
|
-
async def test_authenticate(pchk_server, pypck_client):
|
|
28
|
-
"""Test authentication procedure."""
|
|
29
|
-
await pypck_client.async_connect()
|
|
30
|
-
assert pypck_client.is_ready()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@pytest.mark.asyncio
|
|
34
|
-
async def test_port_error(pchk_server, pypck_client):
|
|
35
|
-
"""Test wrong port."""
|
|
36
|
-
pypck_client.port = 55555
|
|
37
|
-
with pytest.raises(PchkConnectionRefusedError):
|
|
38
|
-
await pypck_client.async_connect()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@pytest.mark.asyncio
|
|
42
|
-
async def test_authentication_error(pchk_server, pypck_client):
|
|
43
|
-
"""Test wrong login credentials."""
|
|
44
|
-
pypck_client.password = "wrong_password"
|
|
45
|
-
with pytest.raises(PchkAuthenticationError):
|
|
46
|
-
await pypck_client.async_connect()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@pytest.mark.asyncio
|
|
50
|
-
async def test_license_error(pchk_server, pypck_client):
|
|
51
|
-
"""Test license error."""
|
|
52
|
-
pchk_server.set_license_error(True)
|
|
53
|
-
|
|
54
|
-
with pytest.raises(PchkLicenseError):
|
|
55
|
-
await pypck_client.async_connect()
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@pytest.mark.asyncio
|
|
59
|
-
async def test_timeout_error(pchk_server, pypck_client):
|
|
60
|
-
"""Test timeout when connecting."""
|
|
61
|
-
with pytest.raises(PchkConnectionFailedError):
|
|
62
|
-
await pypck_client.async_connect(timeout=0)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@pytest.mark.asyncio
|
|
66
|
-
async def test_lcn_connected(pchk_server, pypck_client):
|
|
67
|
-
"""Test lcn disconnected event."""
|
|
68
|
-
event_callback = Mock()
|
|
69
|
-
pypck_client.register_for_events(event_callback)
|
|
70
|
-
await pypck_client.async_connect()
|
|
71
|
-
await pchk_server.send_message("$io:#LCN:connected")
|
|
72
|
-
await pypck_client.received("$io:#LCN:connected")
|
|
73
|
-
|
|
74
|
-
event_callback.assert_has_calls(
|
|
75
|
-
[
|
|
76
|
-
call(LcnEvent.BUS_CONNECTION_STATUS_CHANGED),
|
|
77
|
-
call(LcnEvent.BUS_CONNECTED),
|
|
78
|
-
]
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@pytest.mark.asyncio
|
|
83
|
-
async def test_lcn_disconnected(pchk_server, pypck_client):
|
|
84
|
-
"""Test lcn disconnected event."""
|
|
85
|
-
event_callback = Mock()
|
|
86
|
-
pypck_client.register_for_events(event_callback)
|
|
87
|
-
await pypck_client.async_connect()
|
|
88
|
-
await pchk_server.send_message("$io:#LCN:disconnected")
|
|
89
|
-
await pypck_client.received("$io:#LCN:disconnected")
|
|
90
|
-
|
|
91
|
-
event_callback.assert_has_calls(
|
|
92
|
-
[call(LcnEvent.BUS_CONNECTION_STATUS_CHANGED), call(LcnEvent.BUS_DISCONNECTED)]
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@pytest.mark.asyncio
|
|
97
|
-
async def test_connection_lost(pchk_server, pypck_client):
|
|
98
|
-
"""Test pchk server connection close."""
|
|
99
|
-
event_callback = Mock()
|
|
100
|
-
pypck_client.register_for_events(event_callback)
|
|
101
|
-
await pypck_client.async_connect()
|
|
102
|
-
|
|
103
|
-
await pchk_server.stop()
|
|
104
|
-
# ensure that pypck_client is about to be closed
|
|
105
|
-
await pypck_client.wait_closed()
|
|
106
|
-
|
|
107
|
-
event_callback.assert_has_calls([call(LcnEvent.CONNECTION_LOST)])
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@pytest.mark.asyncio
|
|
111
|
-
async def test_multiple_connections(
|
|
112
|
-
pchk_server, pypck_client, pchk_server_2, pypck_client_2
|
|
113
|
-
):
|
|
114
|
-
"""Test that two independent connections can coexists."""
|
|
115
|
-
await pypck_client_2.async_connect()
|
|
116
|
-
|
|
117
|
-
event_callback = Mock()
|
|
118
|
-
pypck_client.register_for_events(event_callback)
|
|
119
|
-
await pypck_client.async_connect()
|
|
120
|
-
|
|
121
|
-
await pchk_server.stop()
|
|
122
|
-
await pypck_client.wait_closed()
|
|
123
|
-
|
|
124
|
-
event_callback.assert_has_calls([call(LcnEvent.CONNECTION_LOST)])
|
|
125
|
-
|
|
126
|
-
assert len(pypck_client.task_registry.tasks) == 0
|
|
127
|
-
assert len(pypck_client_2.task_registry.tasks) > 0
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@pytest.mark.asyncio
|
|
131
|
-
async def test_segment_coupler_search(pchk_server, pypck_client):
|
|
132
|
-
"""Test segment coupler search."""
|
|
133
|
-
await pypck_client.async_connect()
|
|
134
|
-
await pypck_client.scan_segment_couplers(3, 0)
|
|
135
|
-
|
|
136
|
-
assert await pchk_server.received(">G003003.SK")
|
|
137
|
-
assert await pchk_server.received(">G003003.SK")
|
|
138
|
-
assert await pchk_server.received(">G003003.SK")
|
|
139
|
-
|
|
140
|
-
assert pypck_client.is_ready()
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
@pytest.mark.asyncio
|
|
144
|
-
async def test_segment_coupler_response(pchk_server, pypck_client):
|
|
145
|
-
"""Test segment coupler response."""
|
|
146
|
-
await pypck_client.async_connect()
|
|
147
|
-
|
|
148
|
-
assert pypck_client.local_seg_id == 0
|
|
149
|
-
|
|
150
|
-
await pchk_server.send_message("=M000005.SK020")
|
|
151
|
-
await pchk_server.send_message("=M021021.SK021")
|
|
152
|
-
await pchk_server.send_message("=M022010.SK022")
|
|
153
|
-
assert await pypck_client.received("=M000005.SK020")
|
|
154
|
-
assert await pypck_client.received("=M021021.SK021")
|
|
155
|
-
assert await pypck_client.received("=M022010.SK022")
|
|
156
|
-
|
|
157
|
-
assert pypck_client.local_seg_id == 20
|
|
158
|
-
assert set(pypck_client.segment_coupler_ids) == {20, 21, 22}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@pytest.mark.asyncio
|
|
162
|
-
async def test_module_scan(pchk_server, pypck_client):
|
|
163
|
-
"""Test module scan."""
|
|
164
|
-
await pypck_client.async_connect()
|
|
165
|
-
await pypck_client.scan_modules(3, 0)
|
|
166
|
-
|
|
167
|
-
assert await pchk_server.received(">G000003!LEER")
|
|
168
|
-
assert await pchk_server.received(">G000003!LEER")
|
|
169
|
-
assert await pchk_server.received(">G000003!LEER")
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
@pytest.mark.asyncio
|
|
173
|
-
async def test_module_sn_response(pchk_server, pypck_client):
|
|
174
|
-
"""Test module scan."""
|
|
175
|
-
await pypck_client.async_connect()
|
|
176
|
-
module = pypck_client.get_address_conn(LcnAddr(0, 7, False))
|
|
177
|
-
|
|
178
|
-
message = "=M000007.SN1AB20A123401FW190B11HW015"
|
|
179
|
-
await pchk_server.send_message(message)
|
|
180
|
-
assert await pypck_client.received(message)
|
|
181
|
-
|
|
182
|
-
assert await module.serial_known
|
|
183
|
-
assert module.hardware_serial == 0x1AB20A1234
|
|
184
|
-
assert module.manu == 1
|
|
185
|
-
assert module.software_serial == 0x190B11
|
|
186
|
-
assert module.hardware_type.value == 15
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@pytest.mark.asyncio
|
|
190
|
-
async def test_send_command_to_server(pchk_server, pypck_client):
|
|
191
|
-
"""Test sending a command to the PCHK server."""
|
|
192
|
-
await pypck_client.async_connect()
|
|
193
|
-
message = ">M000007.PIN003"
|
|
194
|
-
await pypck_client.send_command(message)
|
|
195
|
-
assert await pchk_server.received(message)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
@pytest.mark.asyncio
|
|
199
|
-
async def test_ping(pchk_server, pypck_client):
|
|
200
|
-
"""Test if pings are send."""
|
|
201
|
-
await pypck_client.async_connect()
|
|
202
|
-
assert await pchk_server.received("^ping0")
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
@pytest.mark.asyncio
|
|
206
|
-
async def test_add_address_connections(pypck_client):
|
|
207
|
-
"""Test if new address connections are added on request."""
|
|
208
|
-
lcn_addr = LcnAddr(0, 10, False)
|
|
209
|
-
assert lcn_addr not in pypck_client.address_conns
|
|
210
|
-
|
|
211
|
-
addr_conn = pypck_client.get_address_conn(lcn_addr)
|
|
212
|
-
assert isinstance(addr_conn, ModuleConnection)
|
|
213
|
-
|
|
214
|
-
assert lcn_addr in pypck_client.address_conns
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
@pytest.mark.asyncio
|
|
218
|
-
async def test_add_address_connections_by_message(pchk_server, pypck_client):
|
|
219
|
-
"""Test if new address connections are added by received message."""
|
|
220
|
-
await pypck_client.async_connect()
|
|
221
|
-
lcn_addr = LcnAddr(0, 10, False)
|
|
222
|
-
assert lcn_addr not in pypck_client.address_conns
|
|
223
|
-
|
|
224
|
-
message = ":M000010A1050"
|
|
225
|
-
await pchk_server.send_message(message)
|
|
226
|
-
assert await pypck_client.received(message)
|
|
227
|
-
|
|
228
|
-
assert lcn_addr in pypck_client.address_conns
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
@pytest.mark.asyncio
|
|
232
|
-
async def test_groups_static_membership_discovery(pchk_server, pypck_client):
|
|
233
|
-
"""Test module scan."""
|
|
234
|
-
await pypck_client.async_connect()
|
|
235
|
-
module = pypck_client.get_address_conn(LcnAddr(0, 10, False))
|
|
236
|
-
|
|
237
|
-
task = asyncio.create_task(module.request_static_groups())
|
|
238
|
-
assert await pchk_server.received(">M000010.GP")
|
|
239
|
-
await pchk_server.send_message("=M000010.GP012011200051")
|
|
240
|
-
assert await task == {
|
|
241
|
-
LcnAddr(0, 11, True),
|
|
242
|
-
LcnAddr(0, 200, True),
|
|
243
|
-
LcnAddr(0, 51, True),
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
@pytest.mark.asyncio
|
|
248
|
-
async def test_groups_dynamic_membership_discovery(pchk_server, pypck_client):
|
|
249
|
-
"""Test module scan."""
|
|
250
|
-
await pypck_client.async_connect()
|
|
251
|
-
module = pypck_client.get_address_conn(LcnAddr(0, 10, False))
|
|
252
|
-
|
|
253
|
-
task = asyncio.create_task(module.request_dynamic_groups())
|
|
254
|
-
assert await pchk_server.received(">M000010.GD")
|
|
255
|
-
await pchk_server.send_message("=M000010.GD008011200051")
|
|
256
|
-
assert await task == {
|
|
257
|
-
LcnAddr(0, 11, True),
|
|
258
|
-
LcnAddr(0, 200, True),
|
|
259
|
-
LcnAddr(0, 51, True),
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
@pytest.mark.asyncio
|
|
264
|
-
async def test_groups_membership_discovery(pchk_server, pypck_client):
|
|
265
|
-
"""Test module scan."""
|
|
266
|
-
await pypck_client.async_connect()
|
|
267
|
-
module = pypck_client.get_address_conn(LcnAddr(0, 10, False))
|
|
268
|
-
|
|
269
|
-
task = asyncio.create_task(module.request_groups())
|
|
270
|
-
assert await pchk_server.received(">M000010.GP")
|
|
271
|
-
await pchk_server.send_message("=M000010.GP012011200051")
|
|
272
|
-
assert await pchk_server.received(">M000010.GD")
|
|
273
|
-
await pchk_server.send_message("=M000010.GD008015100052")
|
|
274
|
-
assert await task == {
|
|
275
|
-
LcnAddr(0, 11, True),
|
|
276
|
-
LcnAddr(0, 200, True),
|
|
277
|
-
LcnAddr(0, 51, True),
|
|
278
|
-
LcnAddr(0, 15, True),
|
|
279
|
-
LcnAddr(0, 100, True),
|
|
280
|
-
LcnAddr(0, 52, True),
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
@pytest.mark.asyncio
|
|
285
|
-
async def test_multiple_serial_requests(pchk_server, pypck_client):
|
|
286
|
-
"""Test module scan."""
|
|
287
|
-
await pypck_client.async_connect()
|
|
288
|
-
|
|
289
|
-
pypck_client.get_address_conn(LcnAddr(0, 10, False))
|
|
290
|
-
pypck_client.get_address_conn(LcnAddr(0, 11, False))
|
|
291
|
-
pypck_client.get_address_conn(LcnAddr(0, 12, False))
|
|
292
|
-
|
|
293
|
-
assert await pchk_server.received(">M000010.SN")
|
|
294
|
-
assert await pchk_server.received(">M000011.SN")
|
|
295
|
-
assert await pchk_server.received(">M000012.SN")
|
|
296
|
-
|
|
297
|
-
message = "=M000010.SN1AB20A123401FW190B11HW015"
|
|
298
|
-
await pchk_server.send_message(message)
|
|
299
|
-
assert await pypck_client.received(message)
|
|
300
|
-
|
|
301
|
-
await pypck_client.async_close()
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
@pytest.mark.asyncio
|
|
305
|
-
async def test_dump_modules_no_segement_couplers(pchk_server, pypck_client):
|
|
306
|
-
"""Test module information dumping."""
|
|
307
|
-
await pypck_client.async_connect()
|
|
308
|
-
|
|
309
|
-
for msg in (
|
|
310
|
-
"=M000007.SN1AB20A123401FW190B11HW015",
|
|
311
|
-
"=M000008.SN1BB20A123401FW1A0B11HW015",
|
|
312
|
-
"=M000007.GP012011200051",
|
|
313
|
-
"=M000008.GP012011220051",
|
|
314
|
-
"=M000007.GD008015100052",
|
|
315
|
-
"=M000008.GD008015120052",
|
|
316
|
-
):
|
|
317
|
-
await pchk_server.send_message(msg)
|
|
318
|
-
assert await pypck_client.received(msg)
|
|
319
|
-
|
|
320
|
-
dump = pypck_client.dump_modules()
|
|
321
|
-
json.dumps(dump)
|
|
322
|
-
|
|
323
|
-
assert dump == {
|
|
324
|
-
"0": {
|
|
325
|
-
"7": {
|
|
326
|
-
"segment": 0,
|
|
327
|
-
"address": 7,
|
|
328
|
-
"is_local_segment": True,
|
|
329
|
-
"serials": {
|
|
330
|
-
"hardware_serial": "1AB20A1234",
|
|
331
|
-
"manu": "01",
|
|
332
|
-
"software_serial": "190B11",
|
|
333
|
-
"hardware_type": "15",
|
|
334
|
-
"hardware_name": "LCN-SH-Plus",
|
|
335
|
-
},
|
|
336
|
-
"name": "",
|
|
337
|
-
"comment": "",
|
|
338
|
-
"oem_text": ["", "", "", ""],
|
|
339
|
-
"groups": {"static": [11, 51, 200], "dynamic": [15, 52, 100]},
|
|
340
|
-
},
|
|
341
|
-
"8": {
|
|
342
|
-
"segment": 0,
|
|
343
|
-
"address": 8,
|
|
344
|
-
"is_local_segment": True,
|
|
345
|
-
"serials": {
|
|
346
|
-
"hardware_serial": "1BB20A1234",
|
|
347
|
-
"manu": "01",
|
|
348
|
-
"software_serial": "1A0B11",
|
|
349
|
-
"hardware_type": "15",
|
|
350
|
-
"hardware_name": "LCN-SH-Plus",
|
|
351
|
-
},
|
|
352
|
-
"name": "",
|
|
353
|
-
"comment": "",
|
|
354
|
-
"oem_text": ["", "", "", ""],
|
|
355
|
-
"groups": {"static": [11, 51, 220], "dynamic": [15, 52, 120]},
|
|
356
|
-
},
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
@pytest.mark.asyncio
|
|
362
|
-
async def test_dump_modules_multi_segment(pchk_server, pypck_client):
|
|
363
|
-
"""Test module information dumping."""
|
|
364
|
-
await pypck_client.async_connect()
|
|
365
|
-
|
|
366
|
-
# Populate the bus topology information
|
|
367
|
-
for msg in (
|
|
368
|
-
"=M000007.SK020",
|
|
369
|
-
"=M022008.SK022",
|
|
370
|
-
"=M000007.SN1AB20A123401FW190B11HW015",
|
|
371
|
-
"=M022008.SN1BB20A123401FW1A0B11HW015",
|
|
372
|
-
"=M000007.GP012011200051",
|
|
373
|
-
"=M022008.GP012011220051",
|
|
374
|
-
"=M000007.GD008015100052",
|
|
375
|
-
"=M022008.GD008015120052",
|
|
376
|
-
):
|
|
377
|
-
await pchk_server.send_message(msg)
|
|
378
|
-
assert await pypck_client.received(msg)
|
|
379
|
-
|
|
380
|
-
dump = pypck_client.dump_modules()
|
|
381
|
-
json.dumps(dump)
|
|
382
|
-
|
|
383
|
-
assert dump == {
|
|
384
|
-
"20": {
|
|
385
|
-
"7": {
|
|
386
|
-
"segment": 20,
|
|
387
|
-
"address": 7,
|
|
388
|
-
"is_local_segment": True,
|
|
389
|
-
"serials": {
|
|
390
|
-
"hardware_serial": "1AB20A1234",
|
|
391
|
-
"manu": "01",
|
|
392
|
-
"software_serial": "190B11",
|
|
393
|
-
"hardware_type": "15",
|
|
394
|
-
"hardware_name": "LCN-SH-Plus",
|
|
395
|
-
},
|
|
396
|
-
"name": "",
|
|
397
|
-
"comment": "",
|
|
398
|
-
"oem_text": ["", "", "", ""],
|
|
399
|
-
"groups": {"static": [11, 51, 200], "dynamic": [15, 52, 100]},
|
|
400
|
-
},
|
|
401
|
-
},
|
|
402
|
-
"22": {
|
|
403
|
-
"8": {
|
|
404
|
-
"segment": 22,
|
|
405
|
-
"address": 8,
|
|
406
|
-
"is_local_segment": False,
|
|
407
|
-
"serials": {
|
|
408
|
-
"hardware_serial": "1BB20A1234",
|
|
409
|
-
"manu": "01",
|
|
410
|
-
"software_serial": "1A0B11",
|
|
411
|
-
"hardware_type": "15",
|
|
412
|
-
"hardware_name": "LCN-SH-Plus",
|
|
413
|
-
},
|
|
414
|
-
"name": "",
|
|
415
|
-
"comment": "",
|
|
416
|
-
"oem_text": ["", "", "", ""],
|
|
417
|
-
"groups": {"static": [11, 51, 220], "dynamic": [15, 52, 120]},
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|