conson-xp 1.34.0__py3-none-any.whl → 1.35.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.
Files changed (29) hide show
  1. {conson_xp-1.34.0.dist-info → conson_xp-1.35.0.dist-info}/METADATA +1 -1
  2. {conson_xp-1.34.0.dist-info → conson_xp-1.35.0.dist-info}/RECORD +29 -29
  3. xp/__init__.py +1 -1
  4. xp/cli/commands/conbus/conbus_actiontable_commands.py +34 -35
  5. xp/cli/commands/conbus/conbus_autoreport_commands.py +11 -10
  6. xp/cli/commands/conbus/conbus_custom_commands.py +6 -4
  7. xp/cli/commands/conbus/conbus_datapoint_commands.py +8 -6
  8. xp/cli/commands/conbus/conbus_lightlevel_commands.py +19 -16
  9. xp/cli/commands/conbus/conbus_linknumber_commands.py +7 -6
  10. xp/cli/commands/conbus/conbus_modulenumber_commands.py +7 -6
  11. xp/cli/commands/conbus/conbus_msactiontable_commands.py +7 -9
  12. xp/cli/commands/conbus/conbus_output_commands.py +9 -7
  13. xp/cli/commands/conbus/conbus_raw_commands.py +7 -2
  14. xp/cli/commands/conbus/conbus_scan_commands.py +4 -2
  15. xp/services/conbus/actiontable/actiontable_download_service.py +79 -37
  16. xp/services/conbus/actiontable/actiontable_list_service.py +17 -17
  17. xp/services/conbus/actiontable/actiontable_upload_service.py +78 -36
  18. xp/services/conbus/actiontable/msactiontable_service.py +88 -48
  19. xp/services/conbus/conbus_custom_service.py +81 -26
  20. xp/services/conbus/conbus_datapoint_queryall_service.py +90 -43
  21. xp/services/conbus/conbus_datapoint_service.py +76 -28
  22. xp/services/conbus/conbus_output_service.py +82 -22
  23. xp/services/conbus/conbus_raw_service.py +78 -37
  24. xp/services/conbus/conbus_scan_service.py +86 -42
  25. xp/services/conbus/write_config_service.py +76 -26
  26. xp/utils/dependencies.py +10 -20
  27. {conson_xp-1.34.0.dist-info → conson_xp-1.35.0.dist-info}/WHEEL +0 -0
  28. {conson_xp-1.34.0.dist-info → conson_xp-1.35.0.dist-info}/entry_points.txt +0 -0
  29. {conson_xp-1.34.0.dist-info → conson_xp-1.35.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,10 @@
1
1
  """Service for downloading XP24 action tables via Conbus protocol."""
2
2
 
3
3
  import logging
4
- from typing import Callable, Optional, Union
4
+ from typing import Any, Optional, Union
5
5
 
6
- from twisted.internet.posixbase import PosixReactorBase
6
+ from psygnal import Signal
7
7
 
8
- from xp.models import ConbusClientConfig
9
8
  from xp.models.actiontable.msactiontable_xp20 import Xp20MsActionTable
10
9
  from xp.models.actiontable.msactiontable_xp24 import Xp24MsActionTable
11
10
  from xp.models.actiontable.msactiontable_xp33 import Xp33MsActionTable
@@ -21,7 +20,7 @@ from xp.services.actiontable.msactiontable_xp24_serializer import (
21
20
  from xp.services.actiontable.msactiontable_xp33_serializer import (
22
21
  Xp33MsActionTableSerializer,
23
22
  )
24
- from xp.services.protocol import ConbusProtocol
23
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
25
24
  from xp.services.telegram.telegram_service import TelegramService
26
25
 
27
26
 
@@ -31,34 +30,42 @@ class MsActionTableError(Exception):
31
30
  pass
32
31
 
33
32
 
34
- class MsActionTableService(ConbusProtocol):
33
+ class MsActionTableService:
35
34
  """
36
- TCP client service for sending telegrams to Conbus servers.
35
+ Service for downloading MS action tables via Conbus protocol.
37
36
 
38
- Manages TCP socket connections, handles telegram generation and transmission,
39
- and processes server responses.
37
+ Uses ConbusEventProtocol to download action tables from XP20, XP24, or XP33 modules.
38
+ Emits signals for progress updates, errors, and completion.
39
+
40
+ Attributes:
41
+ conbus_protocol: Protocol instance for Conbus communication.
42
+ on_progress: Signal emitted for progress updates (str).
43
+ on_error: Signal emitted for errors (str).
44
+ on_finish: Signal emitted when download completes (MsActionTable or None).
40
45
  """
41
46
 
47
+ on_progress: Signal = Signal(str)
48
+ on_error: Signal = Signal(str)
49
+ on_finish: Signal = Signal(object) # Union type for Xp20/24/33 or None
50
+
42
51
  def __init__(
43
52
  self,
44
- cli_config: ConbusClientConfig,
45
- reactor: PosixReactorBase,
53
+ conbus_protocol: ConbusEventProtocol,
46
54
  xp20ms_serializer: Xp20MsActionTableSerializer,
47
55
  xp24ms_serializer: Xp24MsActionTableSerializer,
48
56
  xp33ms_serializer: Xp33MsActionTableSerializer,
49
57
  telegram_service: TelegramService,
50
58
  ) -> None:
51
- """Initialize the Conbus client send service.
59
+ """Initialize the MS action table service.
52
60
 
53
61
  Args:
54
- cli_config: Conbus client configuration.
55
- reactor: Twisted reactor instance.
62
+ conbus_protocol: ConbusEventProtocol instance.
56
63
  xp20ms_serializer: XP20 MS action table serializer.
57
64
  xp24ms_serializer: XP24 MS action table serializer.
58
65
  xp33ms_serializer: XP33 MS action table serializer.
59
66
  telegram_service: Telegram service for parsing.
60
67
  """
61
- super().__init__(cli_config, reactor)
68
+ self.conbus_protocol = conbus_protocol
62
69
  self.xp20ms_serializer = xp20ms_serializer
63
70
  self.xp24ms_serializer = xp24ms_serializer
64
71
  self.xp33ms_serializer = xp33ms_serializer
@@ -70,24 +77,23 @@ class MsActionTableService(ConbusProtocol):
70
77
  self.telegram_service = telegram_service
71
78
  self.serial_number: str = ""
72
79
  self.xpmoduletype: str = ""
73
- self.progress_callback: Optional[Callable[[str], None]] = None
74
- self.error_callback: Optional[Callable[[str], None]] = None
75
- self.finish_callback: Optional[
76
- Callable[
77
- [Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable, None]],
78
- None,
79
- ]
80
- ] = None
81
80
  self.msactiontable_data: list[str] = []
82
81
  # Set up logging
83
82
  self.logger = logging.getLogger(__name__)
84
83
 
85
- def connection_established(self) -> None:
86
- """Handle connection established event."""
84
+ # Connect protocol signals
85
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
86
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
87
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
88
+ self.conbus_protocol.on_timeout.connect(self.timeout)
89
+ self.conbus_protocol.on_failed.connect(self.failed)
90
+
91
+ def connection_made(self) -> None:
92
+ """Handle connection made event."""
87
93
  self.logger.debug(
88
94
  "Connection established, sending download msactiontable telegram"
89
95
  )
90
- self.send_telegram(
96
+ self.conbus_protocol.send_telegram(
91
97
  telegram_type=TelegramType.SYSTEM,
92
98
  serial_number=self.serial_number,
93
99
  system_function=SystemFunction.DOWNLOAD_MSACTIONTABLE,
@@ -143,10 +149,9 @@ class MsActionTableService(ConbusProtocol):
143
149
  self.msactiontable_data.extend(
144
150
  (reply_telegram.data, reply_telegram.data_value)
145
151
  )
146
- if self.progress_callback:
147
- self.progress_callback(".")
152
+ self.on_progress.emit(".")
148
153
 
149
- self.send_telegram(
154
+ self.conbus_protocol.send_telegram(
150
155
  telegram_type=TelegramType.SYSTEM,
151
156
  serial_number=self.serial_number,
152
157
  system_function=SystemFunction.ACK,
@@ -164,6 +169,11 @@ class MsActionTableService(ConbusProtocol):
164
169
 
165
170
  self.logger.debug("Invalid msactiontable response")
166
171
 
172
+ def timeout(self) -> None:
173
+ """Handle timeout event."""
174
+ self.logger.debug("Timeout occurred")
175
+ self.failed("Timeout")
176
+
167
177
  def failed(self, message: str) -> None:
168
178
  """Handle failed connection event.
169
179
 
@@ -171,9 +181,8 @@ class MsActionTableService(ConbusProtocol):
171
181
  message: Failure message.
172
182
  """
173
183
  self.logger.debug(f"Failed: {message}")
174
- if self.error_callback:
175
- self.error_callback(message)
176
- self._stop_reactor()
184
+ self.on_error.emit(message)
185
+ self.on_finish.emit(None)
177
186
 
178
187
  def succeed(
179
188
  self,
@@ -184,29 +193,19 @@ class MsActionTableService(ConbusProtocol):
184
193
  Args:
185
194
  msactiontable: result.
186
195
  """
187
- if self.finish_callback:
188
- self.finish_callback(msactiontable)
189
- self._stop_reactor()
196
+ self.on_finish.emit(msactiontable)
190
197
 
191
198
  def start(
192
199
  self,
193
200
  serial_number: str,
194
201
  xpmoduletype: str,
195
- progress_callback: Callable[[str], None],
196
- error_callback: Callable[[str], None],
197
- finish_callback: Callable[
198
- [Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable, None]], None
199
- ],
200
202
  timeout_seconds: Optional[float] = None,
201
203
  ) -> None:
202
- """Run reactor in dedicated thread with its own event loop.
204
+ """Setup download parameters.
203
205
 
204
206
  Args:
205
207
  serial_number: Module serial number.
206
208
  xpmoduletype: XP module type (xp20, xp24, xp33).
207
- progress_callback: Callback for progress updates.
208
- error_callback: Callback for errors.
209
- finish_callback: Callback when download completes.
210
209
  timeout_seconds: Optional timeout in seconds.
211
210
 
212
211
  Raises:
@@ -225,8 +224,49 @@ class MsActionTableService(ConbusProtocol):
225
224
  raise MsActionTableError(f"Unsupported module type: {xpmoduletype}")
226
225
 
227
226
  if timeout_seconds:
228
- self.timeout_seconds = timeout_seconds
229
- self.progress_callback = progress_callback
230
- self.error_callback = error_callback
231
- self.finish_callback = finish_callback
232
- self.start_reactor()
227
+ self.conbus_protocol.timeout_seconds = timeout_seconds
228
+
229
+ def set_timeout(self, timeout_seconds: float) -> None:
230
+ """Set operation timeout.
231
+
232
+ Args:
233
+ timeout_seconds: Timeout in seconds.
234
+ """
235
+ self.conbus_protocol.timeout_seconds = timeout_seconds
236
+
237
+ def start_reactor(self) -> None:
238
+ """Start the reactor."""
239
+ self.conbus_protocol.start_reactor()
240
+
241
+ def stop_reactor(self) -> None:
242
+ """Stop the reactor."""
243
+ self.conbus_protocol.stop_reactor()
244
+
245
+ def __enter__(self) -> "MsActionTableService":
246
+ """Enter context manager.
247
+
248
+ Returns:
249
+ Self for context manager protocol.
250
+ """
251
+ # Reset state for singleton reuse
252
+ self.msactiontable_data = []
253
+ self.serial_number = ""
254
+ self.xpmoduletype = ""
255
+ return self
256
+
257
+ def __exit__(
258
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
259
+ ) -> None:
260
+ """Exit context manager and disconnect signals."""
261
+ # Disconnect protocol signals
262
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
263
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
264
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
265
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
266
+ self.conbus_protocol.on_failed.disconnect(self.failed)
267
+ # Disconnect service signals
268
+ self.on_progress.disconnect()
269
+ self.on_error.disconnect()
270
+ self.on_finish.disconnect()
271
+ # Stop reactor
272
+ self.stop_reactor()
@@ -5,47 +5,49 @@ This service handles custom telegram operations for modules through Conbus teleg
5
5
 
6
6
  import logging
7
7
  from datetime import datetime
8
- from typing import Callable, Optional
8
+ from typing import Any, Optional
9
9
 
10
- from twisted.internet.posixbase import PosixReactorBase
10
+ from psygnal import Signal
11
11
 
12
- from xp.models import ConbusClientConfig
13
12
  from xp.models.conbus.conbus_custom import ConbusCustomResponse
14
13
  from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
15
14
  from xp.models.telegram.reply_telegram import ReplyTelegram
16
15
  from xp.models.telegram.system_function import SystemFunction
17
16
  from xp.models.telegram.telegram_type import TelegramType
18
- from xp.services.protocol import ConbusProtocol
17
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
19
18
  from xp.services.telegram.telegram_service import TelegramService
20
19
 
21
20
 
22
- class ConbusCustomService(ConbusProtocol):
23
- """
24
- Service for sending custom telegrams to Conbus modules.
21
+ class ConbusCustomService:
22
+ """Service for sending custom telegrams to Conbus modules.
25
23
 
26
- Uses ConbusProtocol to provide custom telegram functionality
24
+ Uses ConbusEventProtocol to provide custom telegram functionality
27
25
  for sending arbitrary function codes and data to modules.
26
+
27
+ Attributes:
28
+ conbus_protocol: Protocol instance for Conbus communication.
29
+ telegram_service: Service for parsing telegrams.
30
+ on_finish: Signal emitted when custom operation completes (with response).
28
31
  """
29
32
 
33
+ on_finish: Signal = Signal(ConbusCustomResponse)
34
+
30
35
  def __init__(
31
36
  self,
37
+ conbus_protocol: ConbusEventProtocol,
32
38
  telegram_service: TelegramService,
33
- cli_config: ConbusClientConfig,
34
- reactor: PosixReactorBase,
35
39
  ) -> None:
36
40
  """Initialize the Conbus custom service.
37
41
 
38
42
  Args:
43
+ conbus_protocol: Protocol instance for Conbus communication.
39
44
  telegram_service: Service for parsing telegrams.
40
- cli_config: Configuration for Conbus client connection.
41
- reactor: Twisted reactor for event loop.
42
45
  """
43
- super().__init__(cli_config, reactor)
46
+ self.conbus_protocol = conbus_protocol
44
47
  self.telegram_service = telegram_service
45
48
  self.serial_number: str = ""
46
49
  self.function_code: str = ""
47
50
  self.data: str = ""
48
- self.finish_callback: Optional[Callable[[ConbusCustomResponse], None]] = None
49
51
  self.service_response: ConbusCustomResponse = ConbusCustomResponse(
50
52
  success=False,
51
53
  serial_number=self.serial_number,
@@ -54,7 +56,14 @@ class ConbusCustomService(ConbusProtocol):
54
56
  # Set up logging
55
57
  self.logger = logging.getLogger(__name__)
56
58
 
57
- def connection_established(self) -> None:
59
+ # Connect protocol signals
60
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
61
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
62
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
63
+ self.conbus_protocol.on_timeout.connect(self.timeout)
64
+ self.conbus_protocol.on_failed.connect(self.failed)
65
+
66
+ def connection_made(self) -> None:
58
67
  """Handle connection established event."""
59
68
  self.logger.debug(
60
69
  f"Connection established, sending custom telegram F{self.function_code}D{self.data}."
@@ -65,7 +74,7 @@ class ConbusCustomService(ConbusProtocol):
65
74
  self.failed(f"Invalid function code {self.function_code}")
66
75
  return
67
76
 
68
- self.send_telegram(
77
+ self.conbus_protocol.send_telegram(
69
78
  serial_number=self.serial_number,
70
79
  telegram_type=TelegramType.SYSTEM,
71
80
  system_function=system_function,
@@ -113,8 +122,13 @@ class ConbusCustomService(ConbusProtocol):
113
122
  self.service_response.data = self.data
114
123
  self.service_response.reply_telegram = reply_telegram
115
124
 
116
- if self.finish_callback:
117
- self.finish_callback(self.service_response)
125
+ # Emit finish signal
126
+ self.on_finish.emit(self.service_response)
127
+
128
+ def timeout(self) -> None:
129
+ """Handle timeout event."""
130
+ self.logger.debug("Timeout occurred")
131
+ self.failed("Timeout")
118
132
 
119
133
  def failed(self, message: str) -> None:
120
134
  """Handle failed connection event.
@@ -126,15 +140,15 @@ class ConbusCustomService(ConbusProtocol):
126
140
  self.service_response.success = False
127
141
  self.service_response.timestamp = datetime.now()
128
142
  self.service_response.error = message
129
- if self.finish_callback:
130
- self.finish_callback(self.service_response)
143
+
144
+ # Emit finish signal
145
+ self.on_finish.emit(self.service_response)
131
146
 
132
147
  def send_custom_telegram(
133
148
  self,
134
149
  serial_number: str,
135
150
  function_code: str,
136
151
  data: str,
137
- finish_callback: Callable[[ConbusCustomResponse], None],
138
152
  timeout_seconds: Optional[float] = None,
139
153
  ) -> None:
140
154
  """Send a custom telegram to a module.
@@ -143,14 +157,55 @@ class ConbusCustomService(ConbusProtocol):
143
157
  serial_number: 10-digit module serial number.
144
158
  function_code: Function code (e.g., "02", "17").
145
159
  data: Data code (e.g., "E2", "AA").
146
- finish_callback: Callback function to call when the reply is received.
147
160
  timeout_seconds: Timeout in seconds.
148
161
  """
149
162
  self.logger.info("Starting send_custom_telegram")
150
- if timeout_seconds:
151
- self.timeout_seconds = timeout_seconds
152
- self.finish_callback = finish_callback
153
163
  self.serial_number = serial_number
154
164
  self.function_code = function_code
155
165
  self.data = data
156
- self.start_reactor()
166
+ if timeout_seconds:
167
+ self.set_timeout(timeout_seconds)
168
+
169
+ def set_timeout(self, timeout_seconds: float) -> None:
170
+ """Set operation timeout.
171
+
172
+ Args:
173
+ timeout_seconds: Timeout in seconds.
174
+ """
175
+ self.conbus_protocol.timeout_seconds = timeout_seconds
176
+
177
+ def start_reactor(self) -> None:
178
+ """Start the reactor."""
179
+ self.conbus_protocol.start_reactor()
180
+
181
+ def stop_reactor(self) -> None:
182
+ """Stop the reactor."""
183
+ self.conbus_protocol.stop_reactor()
184
+
185
+ def __enter__(self) -> "ConbusCustomService":
186
+ """Enter context manager - reset state for singleton reuse.
187
+
188
+ Returns:
189
+ Self for context manager protocol.
190
+ """
191
+ # Reset state for singleton reuse
192
+ self.service_response = ConbusCustomResponse(success=False)
193
+ self.serial_number = ""
194
+ self.function_code = ""
195
+ self.data = ""
196
+ return self
197
+
198
+ def __exit__(
199
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
200
+ ) -> None:
201
+ """Exit context manager and disconnect signals."""
202
+ # Disconnect protocol signals
203
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
204
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
205
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
206
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
207
+ self.conbus_protocol.on_failed.disconnect(self.failed)
208
+ # Disconnect service signals
209
+ self.on_finish.disconnect()
210
+ # Stop reactor
211
+ self.stop_reactor()
@@ -5,46 +5,58 @@ This module provides service for querying all datapoint types from a module.
5
5
 
6
6
  import logging
7
7
  from datetime import datetime
8
- from typing import Callable, Optional
8
+ from typing import Any, Optional
9
9
 
10
- from twisted.internet.posixbase import PosixReactorBase
10
+ from psygnal import Signal
11
11
 
12
- from xp.models import ConbusClientConfig, ConbusDatapointResponse
12
+ from xp.models import ConbusDatapointResponse
13
13
  from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
14
14
  from xp.models.telegram.datapoint_type import DataPointType
15
15
  from xp.models.telegram.reply_telegram import ReplyTelegram
16
16
  from xp.models.telegram.system_function import SystemFunction
17
17
  from xp.models.telegram.telegram_type import TelegramType
18
18
  from xp.services import TelegramService
19
- from xp.services.protocol import ConbusProtocol
19
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
20
20
 
21
21
 
22
- class ConbusDatapointQueryAllService(ConbusProtocol):
23
- """
24
- Utility service for querying all datapoints from a module.
22
+ class ConbusDatapointQueryAllService:
23
+ """Utility service for querying all datapoints from a module.
25
24
 
26
25
  This service orchestrates multiple ConbusDatapointService calls to query
27
26
  all available datapoint types sequentially.
27
+
28
+ Attributes:
29
+ conbus_protocol: ConbusEventProtocol for protocol communication.
30
+ telegram_service: TelegramService for dependency injection.
31
+ on_progress: Signal emitted for each datapoint response received.
32
+ on_finish: Signal emitted when all datapoints queried (with response).
28
33
  """
29
34
 
35
+ on_progress: Signal = Signal(ReplyTelegram)
36
+ on_finish: Signal = Signal(ConbusDatapointResponse)
37
+
30
38
  def __init__(
31
39
  self,
40
+ conbus_protocol: ConbusEventProtocol,
32
41
  telegram_service: TelegramService,
33
- cli_config: ConbusClientConfig,
34
- reactor: PosixReactorBase,
35
42
  ) -> None:
36
43
  """Initialize the query all service.
37
44
 
38
45
  Args:
46
+ conbus_protocol: ConbusEventProtocol for protocol communication.
39
47
  telegram_service: TelegramService for dependency injection.
40
- cli_config: ConbusClientConfig for connection settings.
41
- reactor: PosixReactorBase for async operations.
42
48
  """
43
- super().__init__(cli_config, reactor)
49
+ self.conbus_protocol = conbus_protocol
44
50
  self.telegram_service = telegram_service
51
+
52
+ # Connect protocol signals
53
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
54
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
55
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
56
+ self.conbus_protocol.on_timeout.connect(self.timeout)
57
+ self.conbus_protocol.on_failed.connect(self.failed)
58
+
45
59
  self.serial_number: str = ""
46
- self.finish_callback: Optional[Callable[[ConbusDatapointResponse], None]] = None
47
- self.progress_callback: Optional[Callable[[ReplyTelegram], None]] = None
48
60
  self.service_response: ConbusDatapointResponse = ConbusDatapointResponse(
49
61
  success=False,
50
62
  serial_number=self.serial_number,
@@ -55,7 +67,7 @@ class ConbusDatapointQueryAllService(ConbusProtocol):
55
67
  # Set up logging
56
68
  self.logger = logging.getLogger(__name__)
57
69
 
58
- def connection_established(self) -> None:
70
+ def connection_made(self) -> None:
59
71
  """Handle connection established event."""
60
72
  self.logger.debug("Connection established, querying datapoints.")
61
73
  self.next_datapoint()
@@ -75,7 +87,7 @@ class ConbusDatapointQueryAllService(ConbusProtocol):
75
87
  datapoint_type = DataPointType(datapoint_type_code)
76
88
 
77
89
  self.logger.debug(f"Datapoint: {datapoint_type}")
78
- self.send_telegram(
90
+ self.conbus_protocol.send_telegram(
79
91
  telegram_type=TelegramType.SYSTEM,
80
92
  serial_number=self.serial_number,
81
93
  system_function=SystemFunction.READ_DATAPOINT,
@@ -84,24 +96,19 @@ class ConbusDatapointQueryAllService(ConbusProtocol):
84
96
  self.current_index += 1
85
97
  return True
86
98
 
87
- def timeout(self) -> bool:
88
- """Handle timeout event by querying next datapoint.
89
-
90
- Returns:
91
- True to continue, False to stop the reactor.
92
- """
99
+ def timeout(self) -> None:
100
+ """Handle timeout event by querying next datapoint."""
93
101
  self.logger.debug("Timeout, querying next datapoint")
94
102
  query_next_datapoint = self.next_datapoint()
95
103
  if not query_next_datapoint:
96
- if self.finish_callback:
97
- self.logger.debug("Received all datapoints telegram")
98
- self.service_response.success = True
99
- self.service_response.timestamp = datetime.now()
100
- self.service_response.serial_number = self.serial_number
101
- self.service_response.system_function = SystemFunction.READ_DATAPOINT
102
- self.finish_callback(self.service_response)
103
- return False
104
- return True
104
+ self.logger.debug("Received all datapoints telegram")
105
+ self.service_response.success = True
106
+ self.service_response.timestamp = datetime.now()
107
+ self.service_response.serial_number = self.serial_number
108
+ self.service_response.system_function = SystemFunction.READ_DATAPOINT
109
+
110
+ # Emit finish signal
111
+ self.on_finish.emit(self.service_response)
105
112
 
106
113
  def telegram_sent(self, telegram_sent: str) -> None:
107
114
  """Handle telegram sent event.
@@ -142,8 +149,7 @@ class ConbusDatapointQueryAllService(ConbusProtocol):
142
149
  return
143
150
 
144
151
  self.logger.debug("Received a datapoint telegram")
145
- if self.progress_callback:
146
- self.progress_callback(datapoint_telegram)
152
+ self.on_progress.emit(datapoint_telegram)
147
153
 
148
154
  def failed(self, message: str) -> None:
149
155
  """Handle failed connection event.
@@ -155,28 +161,69 @@ class ConbusDatapointQueryAllService(ConbusProtocol):
155
161
  self.service_response.success = False
156
162
  self.service_response.timestamp = datetime.now()
157
163
  self.service_response.error = message
158
- if self.finish_callback:
159
- self.finish_callback(self.service_response)
164
+
165
+ # Emit finish signal
166
+ self.on_finish.emit(self.service_response)
160
167
 
161
168
  def query_all_datapoints(
162
169
  self,
163
170
  serial_number: str,
164
- finish_callback: Callable[[ConbusDatapointResponse], None],
165
- progress_callback: Callable[[ReplyTelegram], None],
166
171
  timeout_seconds: Optional[float] = None,
167
172
  ) -> None:
168
173
  """Query all datapoints from a module.
169
174
 
170
175
  Args:
171
176
  serial_number: 10-digit module serial number.
172
- finish_callback: Callback function to call when all datapoints are received.
173
- progress_callback: Callback function to call when each datapoint is received.
174
177
  timeout_seconds: Timeout in seconds.
175
178
  """
176
179
  self.logger.info("Starting query_all_datapoints")
177
180
  if timeout_seconds:
178
- self.timeout_seconds = timeout_seconds
179
- self.finish_callback = finish_callback
180
- self.progress_callback = progress_callback
181
+ self.conbus_protocol.timeout_seconds = timeout_seconds
181
182
  self.serial_number = serial_number
182
- self.start_reactor()
183
+
184
+ def set_timeout(self, timeout_seconds: float) -> None:
185
+ """Set operation timeout.
186
+
187
+ Args:
188
+ timeout_seconds: Timeout in seconds.
189
+ """
190
+ self.conbus_protocol.timeout_seconds = timeout_seconds
191
+
192
+ def start_reactor(self) -> None:
193
+ """Start the reactor."""
194
+ self.conbus_protocol.start_reactor()
195
+
196
+ def stop_reactor(self) -> None:
197
+ """Stop the reactor."""
198
+ self.conbus_protocol.stop_reactor()
199
+
200
+ def __enter__(self) -> "ConbusDatapointQueryAllService":
201
+ """Enter context manager - reset state for singleton reuse.
202
+
203
+ Returns:
204
+ Self for context manager protocol.
205
+ """
206
+ # Reset state for singleton reuse
207
+ self.service_response = ConbusDatapointResponse(
208
+ success=False,
209
+ serial_number="",
210
+ )
211
+ self.datapoint_types = list(DataPointType)
212
+ self.current_index = 0
213
+ self.serial_number = ""
214
+ return self
215
+
216
+ def __exit__(
217
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
218
+ ) -> None:
219
+ """Exit context manager - cleanup signals and reactor."""
220
+ # Disconnect protocol signals
221
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
222
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
223
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
224
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
225
+ self.conbus_protocol.on_failed.disconnect(self.failed)
226
+ # Disconnect service signals
227
+ self.on_progress.disconnect()
228
+ # Stop reactor
229
+ self.stop_reactor()