conson-xp 1.2.0__py3-none-any.whl → 1.3.0__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.
- {conson_xp-1.2.0.dist-info → conson_xp-1.3.0.dist-info}/METADATA +1 -5
- {conson_xp-1.2.0.dist-info → conson_xp-1.3.0.dist-info}/RECORD +30 -47
- xp/__init__.py +1 -1
- xp/cli/commands/__init__.py +0 -2
- xp/cli/commands/conbus/conbus_actiontable_commands.py +5 -3
- xp/cli/commands/conbus/conbus_autoreport_commands.py +39 -21
- xp/cli/commands/conbus/conbus_blink_commands.py +8 -8
- xp/cli/commands/conbus/conbus_config_commands.py +3 -1
- xp/cli/commands/conbus/conbus_custom_commands.py +3 -1
- xp/cli/commands/conbus/conbus_datapoint_commands.py +4 -2
- xp/cli/commands/conbus/conbus_discover_commands.py +5 -3
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +68 -32
- xp/cli/commands/conbus/conbus_linknumber_commands.py +32 -17
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +11 -4
- xp/cli/commands/conbus/conbus_output_commands.py +6 -2
- xp/cli/commands/conbus/conbus_receive_commands.py +5 -3
- xp/cli/commands/file_commands.py +9 -3
- xp/cli/commands/homekit/homekit_start_commands.py +3 -1
- xp/cli/commands/module_commands.py +12 -4
- xp/cli/commands/reverse_proxy_commands.py +3 -1
- xp/cli/main.py +0 -2
- xp/models/conbus/conbus_datapoint.py +3 -0
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/services/conbus/conbus_datapoint_service.py +9 -6
- xp/services/conbus/{conbus_linknumber_set_service.py → write_config_service.py} +78 -66
- xp/services/telegram/telegram_datapoint_service.py +70 -0
- xp/utils/dependencies.py +4 -46
- xp/api/__init__.py +0 -1
- xp/api/main.py +0 -125
- xp/api/models/__init__.py +0 -1
- xp/api/models/api.py +0 -31
- xp/api/models/discover.py +0 -31
- xp/api/routers/__init__.py +0 -17
- xp/api/routers/conbus.py +0 -5
- xp/api/routers/conbus_blink.py +0 -117
- xp/api/routers/conbus_custom.py +0 -71
- xp/api/routers/conbus_datapoint.py +0 -74
- xp/api/routers/conbus_output.py +0 -167
- xp/api/routers/errors.py +0 -38
- xp/cli/commands/api.py +0 -12
- xp/cli/commands/api_start_commands.py +0 -132
- xp/services/conbus/conbus_autoreport_get_service.py +0 -94
- xp/services/conbus/conbus_autoreport_set_service.py +0 -141
- xp/services/conbus/conbus_lightlevel_get_service.py +0 -109
- xp/services/conbus/conbus_lightlevel_set_service.py +0 -225
- xp/services/conbus/conbus_linknumber_get_service.py +0 -94
- {conson_xp-1.2.0.dist-info → conson_xp-1.3.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.2.0.dist-info → conson_xp-1.3.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.2.0.dist-info → conson_xp-1.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,9 +9,12 @@ from xp.cli.utils.decorators import (
|
|
|
9
9
|
connection_command,
|
|
10
10
|
)
|
|
11
11
|
from xp.cli.utils.serial_number_type import SERIAL
|
|
12
|
-
from xp.models
|
|
13
|
-
from xp.
|
|
14
|
-
from xp.
|
|
12
|
+
from xp.models import ConbusDatapointResponse
|
|
13
|
+
from xp.models.conbus.conbus_writeconfig import ConbusWriteConfigResponse
|
|
14
|
+
from xp.models.telegram.datapoint_type import DataPointType
|
|
15
|
+
from xp.services.conbus.conbus_datapoint_service import ConbusDatapointService
|
|
16
|
+
from xp.services.conbus.write_config_service import WriteConfigService
|
|
17
|
+
from xp.services.telegram.telegram_datapoint_service import TelegramDatapointService
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
@conbus_linknumber.command("set", short_help="Set link number for a module")
|
|
@@ -33,23 +36,27 @@ def set_linknumber_command(
|
|
|
33
36
|
\b
|
|
34
37
|
xp conbus linknumber set 0123450001 25
|
|
35
38
|
"""
|
|
36
|
-
service = (
|
|
37
|
-
ctx.obj.get("container").get_container().resolve(ConbusLinknumberSetService)
|
|
38
|
-
)
|
|
39
39
|
|
|
40
|
-
def on_finish(response:
|
|
41
|
-
"""Handle successful completion of
|
|
40
|
+
def on_finish(response: "ConbusWriteConfigResponse") -> None:
|
|
41
|
+
"""Handle successful completion of light level on command.
|
|
42
42
|
|
|
43
43
|
Args:
|
|
44
|
-
response:
|
|
44
|
+
response: Light level response object.
|
|
45
45
|
"""
|
|
46
46
|
click.echo(json.dumps(response.to_dict(), indent=2))
|
|
47
47
|
|
|
48
|
+
service: WriteConfigService = (
|
|
49
|
+
ctx.obj.get("container").get_container().resolve(WriteConfigService)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
data_value = f"{link_number: 02d}"
|
|
48
53
|
with service:
|
|
49
|
-
service.
|
|
54
|
+
service.write_config(
|
|
50
55
|
serial_number=serial_number,
|
|
51
|
-
|
|
56
|
+
datapoint_type=DataPointType.LINK_NUMBER,
|
|
57
|
+
data_value=data_value,
|
|
52
58
|
finish_callback=on_finish,
|
|
59
|
+
timeout_seconds=0.5,
|
|
53
60
|
)
|
|
54
61
|
|
|
55
62
|
|
|
@@ -68,20 +75,28 @@ def get_linknumber_command(ctx: click.Context, serial_number: str) -> None:
|
|
|
68
75
|
\b
|
|
69
76
|
xp conbus linknumber get 0123450001
|
|
70
77
|
"""
|
|
71
|
-
service = (
|
|
72
|
-
ctx.obj.get("container").get_container().resolve(
|
|
78
|
+
service: ConbusDatapointService = (
|
|
79
|
+
ctx.obj.get("container").get_container().resolve(ConbusDatapointService)
|
|
80
|
+
)
|
|
81
|
+
telegram_service: TelegramDatapointService = (
|
|
82
|
+
ctx.obj.get("container").get_container().resolve(TelegramDatapointService)
|
|
73
83
|
)
|
|
74
84
|
|
|
75
|
-
def on_finish(
|
|
85
|
+
def on_finish(service_response: ConbusDatapointResponse) -> None:
|
|
76
86
|
"""Handle successful completion of link number get command.
|
|
77
87
|
|
|
78
88
|
Args:
|
|
79
|
-
|
|
89
|
+
service_response: Link number response object.
|
|
80
90
|
"""
|
|
81
|
-
|
|
91
|
+
linknumber_value = telegram_service.get_linknumber(service_response.data_value)
|
|
92
|
+
result = service_response.to_dict()
|
|
93
|
+
result["linknumber_value"] = linknumber_value
|
|
94
|
+
click.echo(json.dumps(result, indent=2))
|
|
82
95
|
|
|
83
96
|
with service:
|
|
84
|
-
service.
|
|
97
|
+
service.query_datapoint(
|
|
85
98
|
serial_number=serial_number,
|
|
99
|
+
datapoint_type=DataPointType.LINK_NUMBER,
|
|
86
100
|
finish_callback=on_finish,
|
|
101
|
+
timeout_seconds=0.5,
|
|
87
102
|
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from dataclasses import asdict
|
|
5
|
+
from typing import Union
|
|
5
6
|
|
|
6
7
|
import click
|
|
7
8
|
from click import Context
|
|
@@ -12,7 +13,9 @@ from xp.cli.utils.decorators import (
|
|
|
12
13
|
)
|
|
13
14
|
from xp.cli.utils.serial_number_type import SERIAL
|
|
14
15
|
from xp.cli.utils.xp_module_type import XP_MODULE_TYPE
|
|
15
|
-
from xp.models.actiontable.
|
|
16
|
+
from xp.models.actiontable.msactiontable_xp20 import Xp20MsActionTable
|
|
17
|
+
from xp.models.actiontable.msactiontable_xp24 import Xp24MsActionTable
|
|
18
|
+
from xp.models.actiontable.msactiontable_xp33 import Xp33MsActionTable
|
|
16
19
|
from xp.services.conbus.actiontable.msactiontable_service import (
|
|
17
20
|
MsActionTableService,
|
|
18
21
|
)
|
|
@@ -33,7 +36,9 @@ def conbus_download_msactiontable(
|
|
|
33
36
|
serial_number: 10-digit module serial number.
|
|
34
37
|
xpmoduletype: XP module type.
|
|
35
38
|
"""
|
|
36
|
-
service =
|
|
39
|
+
service: MsActionTableService = (
|
|
40
|
+
ctx.obj.get("container").get_container().resolve(MsActionTableService)
|
|
41
|
+
)
|
|
37
42
|
|
|
38
43
|
def progress_callback(progress: str) -> None:
|
|
39
44
|
"""Handle progress updates during MS action table download.
|
|
@@ -43,7 +48,9 @@ def conbus_download_msactiontable(
|
|
|
43
48
|
"""
|
|
44
49
|
click.echo(progress, nl=False)
|
|
45
50
|
|
|
46
|
-
def
|
|
51
|
+
def on_finish(
|
|
52
|
+
action_table: Union[Xp20MsActionTable | Xp24MsActionTable | Xp33MsActionTable],
|
|
53
|
+
) -> None:
|
|
47
54
|
"""Handle successful completion of MS action table download.
|
|
48
55
|
|
|
49
56
|
Args:
|
|
@@ -73,6 +80,6 @@ def conbus_download_msactiontable(
|
|
|
73
80
|
serial_number=serial_number,
|
|
74
81
|
xpmoduletype=xpmoduletype,
|
|
75
82
|
progress_callback=progress_callback,
|
|
76
|
-
finish_callback=
|
|
83
|
+
finish_callback=on_finish,
|
|
77
84
|
error_callback=error_callback,
|
|
78
85
|
)
|
|
@@ -34,7 +34,9 @@ def xp_output_on(ctx: click.Context, serial_number: str, output_number: int) ->
|
|
|
34
34
|
\b
|
|
35
35
|
xp conbus output on 0011223344 0 # Turn on output 0
|
|
36
36
|
"""
|
|
37
|
-
service =
|
|
37
|
+
service: ConbusOutputService = (
|
|
38
|
+
ctx.obj.get("container").get_container().resolve(ConbusOutputService)
|
|
39
|
+
)
|
|
38
40
|
|
|
39
41
|
def on_finish(response: ConbusOutputResponse) -> None:
|
|
40
42
|
"""Handle successful completion of output on command.
|
|
@@ -70,7 +72,9 @@ def xp_output_off(ctx: click.Context, serial_number: str, output_number: int) ->
|
|
|
70
72
|
\b
|
|
71
73
|
xp conbus output off 0011223344 1 # Turn off output 1
|
|
72
74
|
"""
|
|
73
|
-
service =
|
|
75
|
+
service: ConbusOutputService = (
|
|
76
|
+
ctx.obj.get("container").get_container().resolve(ConbusOutputService)
|
|
77
|
+
)
|
|
74
78
|
|
|
75
79
|
def on_finish(response: ConbusOutputResponse) -> None:
|
|
76
80
|
"""Handle successful completion of output off command.
|
|
@@ -36,7 +36,7 @@ def receive_telegrams(ctx: Context, timeout: float) -> None:
|
|
|
36
36
|
xp conbus receive 5.0
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
def
|
|
39
|
+
def on_finish(response_received: ConbusReceiveResponse) -> None:
|
|
40
40
|
"""Handle successful completion of telegram receive operation.
|
|
41
41
|
|
|
42
42
|
Args:
|
|
@@ -52,6 +52,8 @@ def receive_telegrams(ctx: Context, timeout: float) -> None:
|
|
|
52
52
|
"""
|
|
53
53
|
click.echo(telegram_received)
|
|
54
54
|
|
|
55
|
-
service =
|
|
55
|
+
service: ConbusReceiveService = (
|
|
56
|
+
ctx.obj.get("container").get_container().resolve(ConbusReceiveService)
|
|
57
|
+
)
|
|
56
58
|
with service:
|
|
57
|
-
service.start(progress,
|
|
59
|
+
service.start(progress, on_finish, timeout)
|
xp/cli/commands/file_commands.py
CHANGED
|
@@ -56,7 +56,9 @@ def decode_log_file(
|
|
|
56
56
|
from xp.services.log_file_service import LogFileService
|
|
57
57
|
from xp.utils.time_utils import TimeParsingError, parse_time_range
|
|
58
58
|
|
|
59
|
-
service =
|
|
59
|
+
service: LogFileService = (
|
|
60
|
+
ctx.obj.get("container").get_container().resolve(LogFileService)
|
|
61
|
+
)
|
|
60
62
|
StatisticsFormatter(True)
|
|
61
63
|
|
|
62
64
|
try:
|
|
@@ -124,7 +126,9 @@ def analyze_log_file(ctx: Context, log_file_path: str) -> None:
|
|
|
124
126
|
"""
|
|
125
127
|
from xp.services.log_file_service import LogFileService
|
|
126
128
|
|
|
127
|
-
service =
|
|
129
|
+
service: LogFileService = (
|
|
130
|
+
ctx.obj.get("container").get_container().resolve(LogFileService)
|
|
131
|
+
)
|
|
128
132
|
StatisticsFormatter(True)
|
|
129
133
|
|
|
130
134
|
try:
|
|
@@ -156,7 +160,9 @@ def validate_log_file(ctx: Context, log_file_path: str) -> None:
|
|
|
156
160
|
"""
|
|
157
161
|
from xp.services.log_file_service import LogFileService
|
|
158
162
|
|
|
159
|
-
service =
|
|
163
|
+
service: LogFileService = (
|
|
164
|
+
ctx.obj.get("container").get_container().resolve(LogFileService)
|
|
165
|
+
)
|
|
160
166
|
OutputFormatter(True)
|
|
161
167
|
|
|
162
168
|
try:
|
|
@@ -28,7 +28,9 @@ def homekit_start(ctx: Context) -> None:
|
|
|
28
28
|
click.echo("Starting XP Protocol HomeKit server...")
|
|
29
29
|
|
|
30
30
|
try:
|
|
31
|
-
service =
|
|
31
|
+
service: HomeKitService = (
|
|
32
|
+
ctx.obj.get("container").get_container().resolve(HomeKitService)
|
|
33
|
+
)
|
|
32
34
|
service.start() # Blocking call - reactor.run() never returns
|
|
33
35
|
|
|
34
36
|
except KeyboardInterrupt:
|
|
@@ -37,7 +37,9 @@ def module_info(ctx: Context, identifier: str) -> None:
|
|
|
37
37
|
xp module info 14
|
|
38
38
|
xp module info XP2606
|
|
39
39
|
"""
|
|
40
|
-
service =
|
|
40
|
+
service: ModuleTypeService = (
|
|
41
|
+
ctx.obj.get("container").get_container().resolve(ModuleTypeService)
|
|
42
|
+
)
|
|
41
43
|
OutputFormatter(True)
|
|
42
44
|
|
|
43
45
|
try:
|
|
@@ -76,7 +78,9 @@ def module_list(ctx: Context, category: str, group_by_category: bool) -> None:
|
|
|
76
78
|
xp module list --category "Interface Panels"
|
|
77
79
|
xp module list --group-by-category
|
|
78
80
|
"""
|
|
79
|
-
service =
|
|
81
|
+
service: ModuleTypeService = (
|
|
82
|
+
ctx.obj.get("container").get_container().resolve(ModuleTypeService)
|
|
83
|
+
)
|
|
80
84
|
ListFormatter(True)
|
|
81
85
|
|
|
82
86
|
try:
|
|
@@ -130,7 +134,9 @@ def module_search(ctx: Context, query: str, field: tuple) -> None:
|
|
|
130
134
|
xp module search "push button"
|
|
131
135
|
xp module search --field name "XP"
|
|
132
136
|
"""
|
|
133
|
-
service =
|
|
137
|
+
service: ModuleTypeService = (
|
|
138
|
+
ctx.obj.get("container").get_container().resolve(ModuleTypeService)
|
|
139
|
+
)
|
|
134
140
|
ListFormatter(True)
|
|
135
141
|
|
|
136
142
|
try:
|
|
@@ -162,7 +168,9 @@ def module_categories(ctx: Context) -> None:
|
|
|
162
168
|
\b
|
|
163
169
|
xp module categories
|
|
164
170
|
"""
|
|
165
|
-
service =
|
|
171
|
+
service: ModuleTypeService = (
|
|
172
|
+
ctx.obj.get("container").get_container().resolve(ModuleTypeService)
|
|
173
|
+
)
|
|
166
174
|
OutputFormatter(True)
|
|
167
175
|
|
|
168
176
|
try:
|
|
@@ -73,7 +73,9 @@ def start_proxy(ctx: Context, port: int, config: str) -> None:
|
|
|
73
73
|
raise SystemExit(1)
|
|
74
74
|
|
|
75
75
|
# Load configuration and create proxy instance
|
|
76
|
-
service =
|
|
76
|
+
service: ReverseProxyService = (
|
|
77
|
+
ctx.obj.get("container").get_container().resolve(ReverseProxyService)
|
|
78
|
+
)
|
|
77
79
|
global_proxy_instance = service
|
|
78
80
|
|
|
79
81
|
# Handle graceful shutdown on SIGINT
|
xp/cli/main.py
CHANGED
|
@@ -6,7 +6,6 @@ import click
|
|
|
6
6
|
from click_help_colors import HelpColorsGroup
|
|
7
7
|
|
|
8
8
|
from xp.cli.commands import homekit
|
|
9
|
-
from xp.cli.commands.api import api
|
|
10
9
|
from xp.cli.commands.conbus.conbus import conbus
|
|
11
10
|
from xp.cli.commands.file_commands import file
|
|
12
11
|
from xp.cli.commands.module_commands import module
|
|
@@ -79,7 +78,6 @@ cli.add_command(telegram)
|
|
|
79
78
|
cli.add_command(module)
|
|
80
79
|
cli.add_command(file)
|
|
81
80
|
cli.add_command(server)
|
|
82
|
-
cli.add_command(api)
|
|
83
81
|
cli.add_command(reverse_proxy)
|
|
84
82
|
|
|
85
83
|
# Add the tree command
|
|
@@ -21,6 +21,7 @@ class ConbusDatapointResponse:
|
|
|
21
21
|
sent_telegram: Telegram sent to device.
|
|
22
22
|
received_telegrams: List of telegrams received.
|
|
23
23
|
datapoint_telegram: Parsed datapoint telegram.
|
|
24
|
+
data_value: Value of the datapoint telegram.
|
|
24
25
|
datapoints: List of datapoint values.
|
|
25
26
|
error: Error message if operation failed.
|
|
26
27
|
timestamp: Timestamp of the response.
|
|
@@ -33,6 +34,7 @@ class ConbusDatapointResponse:
|
|
|
33
34
|
sent_telegram: Optional[str] = None
|
|
34
35
|
received_telegrams: Optional[list] = None
|
|
35
36
|
datapoint_telegram: Optional[ReplyTelegram] = None
|
|
37
|
+
data_value: str = ""
|
|
36
38
|
datapoints: Optional[List[Dict[str, str]]] = None
|
|
37
39
|
error: Optional[str] = None
|
|
38
40
|
timestamp: Optional[datetime] = None
|
|
@@ -55,6 +57,7 @@ class ConbusDatapointResponse:
|
|
|
55
57
|
result: Dict[str, Any] = {
|
|
56
58
|
"success": self.success,
|
|
57
59
|
"serial_number": self.serial_number,
|
|
60
|
+
"data_value": self.data_value,
|
|
58
61
|
"error": self.error,
|
|
59
62
|
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
|
|
60
63
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Conbus link number response model."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from xp.models.telegram.datapoint_type import DataPointType
|
|
8
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ConbusWriteConfigResponse:
|
|
13
|
+
"""Represents a response from Conbus write config operations (set/get).
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
success: Whether the operation was successful.
|
|
17
|
+
serial_number: Serial number of the device.
|
|
18
|
+
datapoint_type: the datapoint to write.
|
|
19
|
+
system_function: ACK or NAK received.
|
|
20
|
+
sent_telegram: Telegram sent to device.
|
|
21
|
+
received_telegrams: List of telegrams received.
|
|
22
|
+
data_value: written value.
|
|
23
|
+
error: Error message if operation failed.
|
|
24
|
+
timestamp: Timestamp of the response.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
success: bool
|
|
28
|
+
serial_number: str
|
|
29
|
+
datapoint_type: Optional[DataPointType] = None
|
|
30
|
+
system_function: Optional[SystemFunction] = None
|
|
31
|
+
sent_telegram: Optional[str] = None
|
|
32
|
+
received_telegrams: Optional[list] = None
|
|
33
|
+
data_value: Optional[str] = None
|
|
34
|
+
error: Optional[str] = None
|
|
35
|
+
timestamp: Optional[datetime] = None
|
|
36
|
+
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
"""Initialize timestamp and received_telegrams if not provided."""
|
|
39
|
+
if self.timestamp is None:
|
|
40
|
+
self.timestamp = datetime.now()
|
|
41
|
+
if self.received_telegrams is None:
|
|
42
|
+
self.received_telegrams = []
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
45
|
+
"""Convert to dictionary for JSON serialization.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Dictionary representation of the response.
|
|
49
|
+
"""
|
|
50
|
+
return {
|
|
51
|
+
"success": self.success,
|
|
52
|
+
"system_function": self.system_function,
|
|
53
|
+
"datapoint_type": self.datapoint_type,
|
|
54
|
+
"data_value": self.data_value,
|
|
55
|
+
"serial_number": self.serial_number,
|
|
56
|
+
"sent_telegram": self.sent_telegram,
|
|
57
|
+
"received_telegrams": self.received_telegrams,
|
|
58
|
+
"error": self.error,
|
|
59
|
+
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
|
|
60
|
+
}
|
|
@@ -44,7 +44,9 @@ class ConbusDatapointService(ConbusProtocol):
|
|
|
44
44
|
self.telegram_service = telegram_service
|
|
45
45
|
self.serial_number: str = ""
|
|
46
46
|
self.datapoint_type: Optional[DataPointType] = None
|
|
47
|
-
self.
|
|
47
|
+
self.datapoint_finished_callback: Optional[
|
|
48
|
+
Callable[[ConbusDatapointResponse], None]
|
|
49
|
+
] = None
|
|
48
50
|
self.service_response: ConbusDatapointResponse = ConbusDatapointResponse(
|
|
49
51
|
success=False,
|
|
50
52
|
serial_number=self.serial_number,
|
|
@@ -125,8 +127,9 @@ class ConbusDatapointService(ConbusProtocol):
|
|
|
125
127
|
self.service_response.system_function = SystemFunction.READ_DATAPOINT
|
|
126
128
|
self.service_response.datapoint_type = self.datapoint_type
|
|
127
129
|
self.service_response.datapoint_telegram = datapoint_telegram
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
self.service_response.data_value = datapoint_telegram.data_value
|
|
131
|
+
if self.datapoint_finished_callback:
|
|
132
|
+
self.datapoint_finished_callback(self.service_response)
|
|
130
133
|
|
|
131
134
|
def failed(self, message: str) -> None:
|
|
132
135
|
"""Handle failed connection event.
|
|
@@ -139,8 +142,8 @@ class ConbusDatapointService(ConbusProtocol):
|
|
|
139
142
|
self.service_response.timestamp = datetime.now()
|
|
140
143
|
self.service_response.serial_number = self.serial_number
|
|
141
144
|
self.service_response.error = message
|
|
142
|
-
if self.
|
|
143
|
-
self.
|
|
145
|
+
if self.datapoint_finished_callback:
|
|
146
|
+
self.datapoint_finished_callback(self.service_response)
|
|
144
147
|
|
|
145
148
|
def query_datapoint(
|
|
146
149
|
self,
|
|
@@ -160,7 +163,7 @@ class ConbusDatapointService(ConbusProtocol):
|
|
|
160
163
|
self.logger.info("Starting query_datapoint")
|
|
161
164
|
if timeout_seconds:
|
|
162
165
|
self.timeout_seconds = timeout_seconds
|
|
163
|
-
self.
|
|
166
|
+
self.datapoint_finished_callback = finish_callback
|
|
164
167
|
self.serial_number = serial_number
|
|
165
168
|
self.datapoint_type = datapoint_type
|
|
166
169
|
self.start_reactor()
|
|
@@ -10,7 +10,7 @@ from typing import Callable, Optional
|
|
|
10
10
|
from twisted.internet.posixbase import PosixReactorBase
|
|
11
11
|
|
|
12
12
|
from xp.models import ConbusClientConfig
|
|
13
|
-
from xp.models.conbus.
|
|
13
|
+
from xp.models.conbus.conbus_writeconfig import ConbusWriteConfigResponse
|
|
14
14
|
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
15
15
|
from xp.models.telegram.datapoint_type import DataPointType
|
|
16
16
|
from xp.models.telegram.system_function import SystemFunction
|
|
@@ -19,11 +19,11 @@ from xp.services.protocol import ConbusProtocol
|
|
|
19
19
|
from xp.services.telegram.telegram_service import TelegramService
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class
|
|
22
|
+
class WriteConfigService(ConbusProtocol):
|
|
23
23
|
"""
|
|
24
|
-
Service for
|
|
24
|
+
Service for writing module settings via Conbus telegrams.
|
|
25
25
|
|
|
26
|
-
Handles
|
|
26
|
+
Handles setting assignment by sending F04DXX telegrams and processing
|
|
27
27
|
ACK/NAK responses from modules.
|
|
28
28
|
"""
|
|
29
29
|
|
|
@@ -42,13 +42,17 @@ class ConbusLinknumberSetService(ConbusProtocol):
|
|
|
42
42
|
"""
|
|
43
43
|
super().__init__(cli_config, reactor)
|
|
44
44
|
self.telegram_service = telegram_service
|
|
45
|
+
self.datapoint_type: Optional[DataPointType] = None
|
|
45
46
|
self.serial_number: str = ""
|
|
46
|
-
self.
|
|
47
|
-
self.
|
|
48
|
-
None
|
|
49
|
-
|
|
50
|
-
self.
|
|
51
|
-
|
|
47
|
+
self.data_value: str = ""
|
|
48
|
+
self.write_config_finished_callback: Optional[
|
|
49
|
+
Callable[[ConbusWriteConfigResponse], None]
|
|
50
|
+
] = None
|
|
51
|
+
self.write_config_response: ConbusWriteConfigResponse = (
|
|
52
|
+
ConbusWriteConfigResponse(
|
|
53
|
+
success=False,
|
|
54
|
+
serial_number=self.serial_number,
|
|
55
|
+
)
|
|
52
56
|
)
|
|
53
57
|
|
|
54
58
|
# Set up logging
|
|
@@ -56,26 +60,30 @@ class ConbusLinknumberSetService(ConbusProtocol):
|
|
|
56
60
|
|
|
57
61
|
def connection_established(self) -> None:
|
|
58
62
|
"""Handle connection established event."""
|
|
59
|
-
self.logger.debug(
|
|
60
|
-
f"Connection established, setting link number {self.link_number}."
|
|
61
|
-
)
|
|
63
|
+
self.logger.debug(f"Connection established, writing config {self.data_value}.")
|
|
62
64
|
|
|
63
65
|
# Validate parameters before sending
|
|
64
66
|
if not self.serial_number or len(self.serial_number) != 10:
|
|
65
67
|
self.failed(f"Serial number must be 10 digits, got: {self.serial_number}")
|
|
66
68
|
return
|
|
67
69
|
|
|
68
|
-
if
|
|
69
|
-
self.failed(f"
|
|
70
|
+
if len(self.data_value) < 2:
|
|
71
|
+
self.failed(f"data_value must be at least 2 bytes, got: {self.data_value}")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
if not self.datapoint_type:
|
|
75
|
+
self.failed(f"datapoint_type must be defined, got: {self.datapoint_type}")
|
|
70
76
|
return
|
|
71
77
|
|
|
72
|
-
# Send
|
|
73
|
-
# F04 = WRITE_CONFIG,
|
|
78
|
+
# Send WRITE_CONFIG telegram
|
|
79
|
+
# Function F04 = WRITE_CONFIG,
|
|
80
|
+
# Datapoint = D datapoint_type
|
|
81
|
+
# Data = XX
|
|
74
82
|
self.send_telegram(
|
|
75
83
|
telegram_type=TelegramType.SYSTEM,
|
|
76
84
|
serial_number=self.serial_number,
|
|
77
85
|
system_function=SystemFunction.WRITE_CONFIG,
|
|
78
|
-
data_value=f"{
|
|
86
|
+
data_value=f"{self.datapoint_type.value}{self.data_value}",
|
|
79
87
|
)
|
|
80
88
|
|
|
81
89
|
def telegram_sent(self, telegram_sent: str) -> None:
|
|
@@ -84,7 +92,7 @@ class ConbusLinknumberSetService(ConbusProtocol):
|
|
|
84
92
|
Args:
|
|
85
93
|
telegram_sent: The telegram that was sent.
|
|
86
94
|
"""
|
|
87
|
-
self.
|
|
95
|
+
self.write_config_response.sent_telegram = telegram_sent
|
|
88
96
|
|
|
89
97
|
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
90
98
|
"""Handle telegram received event.
|
|
@@ -94,9 +102,9 @@ class ConbusLinknumberSetService(ConbusProtocol):
|
|
|
94
102
|
"""
|
|
95
103
|
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
96
104
|
|
|
97
|
-
if not self.
|
|
98
|
-
self.
|
|
99
|
-
self.
|
|
105
|
+
if not self.write_config_response.received_telegrams:
|
|
106
|
+
self.write_config_response.received_telegrams = []
|
|
107
|
+
self.write_config_response.received_telegrams.append(telegram_received.frame)
|
|
100
108
|
|
|
101
109
|
if (
|
|
102
110
|
not telegram_received.checksum_valid
|
|
@@ -111,71 +119,75 @@ class ConbusLinknumberSetService(ConbusProtocol):
|
|
|
111
119
|
telegram_received.frame
|
|
112
120
|
)
|
|
113
121
|
|
|
114
|
-
if not reply_telegram
|
|
115
|
-
|
|
122
|
+
if not reply_telegram or reply_telegram.system_function not in (
|
|
123
|
+
SystemFunction.ACK,
|
|
124
|
+
SystemFunction.NAK,
|
|
125
|
+
):
|
|
126
|
+
self.logger.debug("Not a write config reply")
|
|
116
127
|
return
|
|
117
128
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
self.failed("Module responded with NAK")
|
|
125
|
-
else:
|
|
126
|
-
self.logger.debug(
|
|
127
|
-
f"Unexpected system function: {reply_telegram.system_function}"
|
|
128
|
-
)
|
|
129
|
+
succeed = (
|
|
130
|
+
True if reply_telegram.system_function == SystemFunction.ACK else False
|
|
131
|
+
)
|
|
132
|
+
self.finished(
|
|
133
|
+
succeed_or_failed=succeed, system_function=reply_telegram.system_function
|
|
134
|
+
)
|
|
129
135
|
|
|
130
|
-
def
|
|
131
|
-
"""Handle
|
|
136
|
+
def failed(self, message: str) -> None:
|
|
137
|
+
"""Handle telegram failed event.
|
|
132
138
|
|
|
133
139
|
Args:
|
|
134
|
-
|
|
140
|
+
message: The error message.
|
|
135
141
|
"""
|
|
136
|
-
self.logger.debug("
|
|
137
|
-
self.
|
|
138
|
-
self.service_response.timestamp = datetime.now()
|
|
139
|
-
self.service_response.serial_number = self.serial_number
|
|
140
|
-
self.service_response.result = "ACK"
|
|
141
|
-
self.service_response.link_number = self.link_number
|
|
142
|
-
if self.finish_callback:
|
|
143
|
-
self.finish_callback(self.service_response)
|
|
142
|
+
self.logger.debug("Failed to send telegram")
|
|
143
|
+
self.finished(succeed_or_failed=False, message=message)
|
|
144
144
|
|
|
145
|
-
def
|
|
146
|
-
|
|
145
|
+
def finished(
|
|
146
|
+
self,
|
|
147
|
+
succeed_or_failed: bool,
|
|
148
|
+
message: Optional[str] = None,
|
|
149
|
+
system_function: Optional[SystemFunction] = None,
|
|
150
|
+
) -> None:
|
|
151
|
+
"""Handle successful link number set operation.
|
|
147
152
|
|
|
148
153
|
Args:
|
|
149
|
-
|
|
154
|
+
succeed_or_failed: succeed true, failed false.
|
|
155
|
+
message: error message if any.
|
|
156
|
+
system_function: The system function from the reply telegram.
|
|
150
157
|
"""
|
|
151
|
-
self.logger.debug(
|
|
152
|
-
self.
|
|
153
|
-
self.
|
|
154
|
-
self.
|
|
155
|
-
self.
|
|
156
|
-
self.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
self.logger.debug("finished writing config")
|
|
159
|
+
self.write_config_response.success = succeed_or_failed
|
|
160
|
+
self.write_config_response.error = message
|
|
161
|
+
self.write_config_response.timestamp = datetime.now()
|
|
162
|
+
self.write_config_response.serial_number = self.serial_number
|
|
163
|
+
self.write_config_response.system_function = system_function
|
|
164
|
+
self.write_config_response.datapoint_type = self.datapoint_type
|
|
165
|
+
self.write_config_response.data_value = self.data_value
|
|
166
|
+
if self.write_config_finished_callback:
|
|
167
|
+
self.write_config_finished_callback(self.write_config_response)
|
|
168
|
+
|
|
169
|
+
def write_config(
|
|
161
170
|
self,
|
|
162
171
|
serial_number: str,
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
datapoint_type: DataPointType,
|
|
173
|
+
data_value: str,
|
|
174
|
+
finish_callback: Callable[[ConbusWriteConfigResponse], None],
|
|
165
175
|
timeout_seconds: Optional[float] = None,
|
|
166
176
|
) -> None:
|
|
167
|
-
"""
|
|
177
|
+
"""Write config to a specific module.
|
|
168
178
|
|
|
169
179
|
Args:
|
|
170
180
|
serial_number: 10-digit module serial number.
|
|
171
|
-
|
|
181
|
+
datapoint_type: the datapoint type to write to.
|
|
182
|
+
data_value: the data to write.
|
|
172
183
|
finish_callback: Callback function to call when operation completes.
|
|
173
184
|
timeout_seconds: Optional timeout in seconds.
|
|
174
185
|
"""
|
|
175
|
-
self.logger.info("Starting
|
|
186
|
+
self.logger.info("Starting write_config")
|
|
176
187
|
if timeout_seconds:
|
|
177
188
|
self.timeout_seconds = timeout_seconds
|
|
178
189
|
self.serial_number = serial_number
|
|
179
|
-
self.
|
|
180
|
-
self.
|
|
190
|
+
self.datapoint_type = datapoint_type
|
|
191
|
+
self.data_value = data_value
|
|
192
|
+
self.write_config_finished_callback = finish_callback
|
|
181
193
|
self.start_reactor()
|