conson-xp 1.49.0__py3-none-any.whl → 1.50.1__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.
@@ -1,332 +0,0 @@
1
- """Service for uploading MS action tables via Conbus protocol."""
2
-
3
- import logging
4
- from typing import Any, Optional, Union
5
-
6
- from psygnal import Signal
7
-
8
- from xp.models.config.conson_module_config import ConsonModuleListConfig
9
- from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
10
- from xp.models.telegram.system_function import SystemFunction
11
- from xp.models.telegram.telegram_type import TelegramType
12
- from xp.services.actiontable.msactiontable_xp20_serializer import (
13
- Xp20MsActionTableSerializer,
14
- )
15
- from xp.services.actiontable.msactiontable_xp24_serializer import (
16
- Xp24MsActionTableSerializer,
17
- )
18
- from xp.services.actiontable.msactiontable_xp33_serializer import (
19
- Xp33MsActionTableSerializer,
20
- )
21
- from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
22
- from xp.services.telegram.telegram_service import TelegramService
23
-
24
-
25
- class MsActionTableUploadError(Exception):
26
- """Raised when MS action table upload operations fail."""
27
-
28
- pass
29
-
30
-
31
- class MsActionTableUploadService:
32
- """
33
- TCP client service for uploading MS action tables to Conbus modules.
34
-
35
- Manages TCP socket connections, handles telegram generation and transmission,
36
- and processes server responses for MS action table uploads.
37
-
38
- Attributes:
39
- on_progress: Signal emitted with telegram frame when progress is made.
40
- on_error: Signal emitted with error message string when an error occurs.
41
- on_finish: Signal emitted with bool (True on success) when upload completes.
42
- """
43
-
44
- on_progress: Signal = Signal(str)
45
- on_error: Signal = Signal(str)
46
- on_finish: Signal = Signal(bool) # True on success
47
-
48
- def __init__(
49
- self,
50
- conbus_protocol: ConbusEventProtocol,
51
- xp20ms_serializer: Xp20MsActionTableSerializer,
52
- xp24ms_serializer: Xp24MsActionTableSerializer,
53
- xp33ms_serializer: Xp33MsActionTableSerializer,
54
- telegram_service: TelegramService,
55
- conson_config: ConsonModuleListConfig,
56
- ) -> None:
57
- """
58
- Initialize the MS action table upload service.
59
-
60
- Args:
61
- conbus_protocol: ConbusEventProtocol for communication.
62
- xp20ms_serializer: XP20 MS action table serializer.
63
- xp24ms_serializer: XP24 MS action table serializer.
64
- xp33ms_serializer: XP33 MS action table serializer.
65
- telegram_service: Telegram service for parsing.
66
- conson_config: Conson module list configuration.
67
- """
68
- self.conbus_protocol = conbus_protocol
69
- self.xp20ms_serializer = xp20ms_serializer
70
- self.xp24ms_serializer = xp24ms_serializer
71
- self.xp33ms_serializer = xp33ms_serializer
72
- self.serializer: Union[
73
- Xp20MsActionTableSerializer,
74
- Xp24MsActionTableSerializer,
75
- Xp33MsActionTableSerializer,
76
- ] = xp20ms_serializer
77
- self.telegram_service = telegram_service
78
- self.conson_config = conson_config
79
- self.serial_number: str = ""
80
- self.xpmoduletype: str = ""
81
-
82
- # Upload state
83
- self.upload_data: str = ""
84
- self.upload_initiated: bool = False
85
-
86
- # Set up logging
87
- self.logger = logging.getLogger(__name__)
88
-
89
- # Connect protocol signals
90
- self.conbus_protocol.on_connection_made.connect(self.connection_made)
91
- self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
92
- self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
93
- self.conbus_protocol.on_timeout.connect(self.timeout)
94
- self.conbus_protocol.on_failed.connect(self.failed)
95
-
96
- def connection_made(self) -> None:
97
- """Handle connection established event."""
98
- self.logger.debug(
99
- "Connection established, sending upload msactiontable telegram"
100
- )
101
- self.conbus_protocol.send_telegram(
102
- telegram_type=TelegramType.SYSTEM,
103
- serial_number=self.serial_number,
104
- system_function=SystemFunction.UPLOAD_MSACTIONTABLE,
105
- data_value="00",
106
- )
107
-
108
- def telegram_sent(self, telegram_sent: str) -> None:
109
- """
110
- Handle telegram sent event.
111
-
112
- Args:
113
- telegram_sent: The telegram that was sent.
114
- """
115
- self.logger.debug(f"Telegram sent: {telegram_sent}")
116
-
117
- def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
118
- """
119
- Handle telegram received event.
120
-
121
- Args:
122
- telegram_received: The telegram received event.
123
- """
124
- self.logger.debug(f"Telegram received: {telegram_received}")
125
- if (
126
- not telegram_received.checksum_valid
127
- or telegram_received.telegram_type != TelegramType.REPLY.value
128
- or telegram_received.serial_number != self.serial_number
129
- ):
130
- self.logger.debug("Not a reply response")
131
- return
132
-
133
- reply_telegram = self.telegram_service.parse_reply_telegram(
134
- telegram_received.frame
135
- )
136
-
137
- self._handle_upload_response(reply_telegram)
138
-
139
- def _handle_upload_response(self, reply_telegram: Any) -> None:
140
- """
141
- Handle telegram responses during upload.
142
-
143
- Args:
144
- reply_telegram: Parsed reply telegram.
145
- """
146
- if reply_telegram.system_function == SystemFunction.ACK:
147
- self.logger.debug("Received ACK for upload")
148
-
149
- if not self.upload_initiated:
150
- # First ACK - send data chunk
151
- self.logger.debug("Sending msactiontable data")
152
- self.conbus_protocol.send_telegram(
153
- telegram_type=TelegramType.SYSTEM,
154
- serial_number=self.serial_number,
155
- system_function=SystemFunction.MSACTIONTABLE,
156
- data_value=self.upload_data,
157
- )
158
- self.upload_initiated = True
159
- self.on_progress.emit(".")
160
- else:
161
- # Second ACK - send EOF
162
- self.logger.debug("Data sent, sending EOF")
163
- self.conbus_protocol.send_telegram(
164
- telegram_type=TelegramType.SYSTEM,
165
- serial_number=self.serial_number,
166
- system_function=SystemFunction.EOF,
167
- data_value="00",
168
- )
169
- self.on_finish.emit(True)
170
- elif reply_telegram.system_function == SystemFunction.NAK:
171
- self.logger.debug("Received NAK during upload")
172
- self.failed("Upload failed: NAK received")
173
- else:
174
- self.logger.debug(f"Unexpected response during upload: {reply_telegram}")
175
-
176
- def timeout(self) -> None:
177
- """Handle timeout event."""
178
- self.logger.debug("Upload timeout")
179
- self.failed("Upload timeout")
180
-
181
- def failed(self, message: str) -> None:
182
- """
183
- Handle failed connection event.
184
-
185
- Args:
186
- message: Failure message.
187
- """
188
- self.logger.debug(f"Failed: {message}")
189
- self.on_error.emit(message)
190
-
191
- def start(
192
- self,
193
- serial_number: str,
194
- xpmoduletype: str,
195
- timeout_seconds: Optional[float] = None,
196
- ) -> None:
197
- """
198
- Upload MS action table to module.
199
-
200
- Uploads the MS action table configuration to the specified module.
201
-
202
- Args:
203
- serial_number: Module serial number.
204
- xpmoduletype: XP module type (xp20, xp24, xp33).
205
- timeout_seconds: Optional timeout in seconds.
206
-
207
- Raises:
208
- MsActionTableUploadError: If configuration or validation errors occur.
209
- """
210
- self.logger.info("Starting msactiontable upload")
211
- self.serial_number = serial_number
212
- self.xpmoduletype = xpmoduletype
213
-
214
- # Select serializer based on module type
215
- if xpmoduletype == "xp20":
216
- self.serializer = self.xp20ms_serializer
217
- config_field = "xp20_msaction_table"
218
- elif xpmoduletype == "xp24":
219
- self.serializer = self.xp24ms_serializer
220
- config_field = "xp24_msaction_table"
221
- elif xpmoduletype == "xp33":
222
- self.serializer = self.xp33ms_serializer
223
- config_field = "xp33_msaction_table"
224
- else:
225
- raise MsActionTableUploadError(f"Unsupported module type: {xpmoduletype}")
226
-
227
- if timeout_seconds:
228
- self.conbus_protocol.timeout_seconds = timeout_seconds
229
-
230
- # Find module
231
- module = self.conson_config.find_module(serial_number)
232
- if not module:
233
- self.failed(f"Module {serial_number} not found in conson.yml")
234
- return
235
-
236
- # Validate module type matches
237
- if module.module_type.lower() != xpmoduletype.lower():
238
- self.failed(
239
- f"Module type mismatch: module has type {module.module_type}, "
240
- f"but {xpmoduletype} was specified"
241
- )
242
- return
243
-
244
- # Get msactiontable config for the module type
245
- msactiontable_config = getattr(module, config_field, None)
246
-
247
- if not msactiontable_config:
248
- self.failed(
249
- f"Module {serial_number} does not have {config_field} configured in conson.yml"
250
- )
251
- return
252
-
253
- if not isinstance(msactiontable_config, list) or len(msactiontable_config) == 0:
254
- self.failed(
255
- f"Module {serial_number} has empty {config_field} list in conson.yml"
256
- )
257
- return
258
-
259
- # Parse MS action table from short format (first element)
260
- try:
261
- short_format = msactiontable_config
262
- msactiontable: Union[
263
- "Xp20MsActionTable", "Xp24MsActionTable", "Xp33MsActionTable"
264
- ]
265
- if xpmoduletype == "xp20":
266
- from xp.models.actiontable.msactiontable_xp20 import Xp20MsActionTable
267
-
268
- msactiontable = Xp20MsActionTable.from_short_format(short_format)
269
- elif xpmoduletype == "xp24":
270
- from xp.models.actiontable.msactiontable_xp24 import Xp24MsActionTable
271
-
272
- msactiontable = Xp24MsActionTable.from_short_format(short_format)
273
- elif xpmoduletype == "xp33":
274
- from xp.models.actiontable.msactiontable_xp33 import Xp33MsActionTable
275
-
276
- msactiontable = Xp33MsActionTable.from_short_format(short_format)
277
- except (ValueError, AttributeError) as e:
278
- self.logger.error(f"Invalid msactiontable format: {e}")
279
- self.failed(f"Invalid msactiontable format: {e}")
280
- return
281
-
282
- # Serialize to telegram data (64 characters: AAAA + 64 data chars)
283
- self.upload_data = "AAAA" + self.serializer.to_encoded_string(msactiontable) # type: ignore[arg-type]
284
-
285
- self.logger.debug(
286
- f"Upload data encoded: {len(self.upload_data)} chars (single chunk)"
287
- )
288
-
289
- def set_timeout(self, timeout_seconds: float) -> None:
290
- """
291
- Set operation timeout.
292
-
293
- Args:
294
- timeout_seconds: Timeout in seconds.
295
- """
296
- self.conbus_protocol.timeout_seconds = timeout_seconds
297
-
298
- def start_reactor(self) -> None:
299
- """Start the reactor."""
300
- self.conbus_protocol.start_reactor()
301
-
302
- def stop_reactor(self) -> None:
303
- """Stop the reactor."""
304
- self.conbus_protocol.stop_reactor()
305
-
306
- def __enter__(self) -> "MsActionTableUploadService":
307
- """Enter context manager - reset state for singleton reuse.
308
-
309
- Returns:
310
- Self for context manager protocol.
311
- """
312
- # Reset state
313
- self.upload_data = ""
314
- self.upload_initiated = False
315
- self.serial_number = ""
316
- self.xpmoduletype = ""
317
- return self
318
-
319
- def __exit__(self, _exc_type: Any, _exc_val: Any, _exc_tb: Any) -> None:
320
- """Exit context manager - cleanup signals and reactor."""
321
- # Disconnect protocol signals
322
- self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
323
- self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
324
- self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
325
- self.conbus_protocol.on_timeout.disconnect(self.timeout)
326
- self.conbus_protocol.on_failed.disconnect(self.failed)
327
- # Disconnect service signals
328
- self.on_progress.disconnect()
329
- self.on_error.disconnect()
330
- self.on_finish.disconnect()
331
- # Stop reactor
332
- self.stop_reactor()