|
6 | 6 | from datetime import datetime, timedelta, timezone |
7 | 7 | from typing import Any, Callable, List, Literal, Optional, Pattern |
8 | 8 |
|
9 | | -from .constants import SDUINO_CMD_TIMEOUT |
| 9 | +from .constants import ( |
| 10 | + SDUINO_CMD_TIMEOUT, |
| 11 | + SDUINO_INIT_MAXRETRY, |
| 12 | + SDUINO_INIT_WAIT, |
| 13 | + SDUINO_INIT_WAIT_XQ, |
| 14 | +) |
10 | 15 | from .exceptions import SignalduinoCommandTimeout, SignalduinoConnectionError |
11 | 16 | from .mqtt import MqttPublisher # NEU: MQTT-Import |
12 | 17 | from .parser import SignalParser |
@@ -46,6 +51,9 @@ def __init__( |
46 | 51 | self._pending_responses: List[PendingResponse] = [] |
47 | 52 | self._pending_responses_lock = threading.Lock() |
48 | 53 |
|
| 54 | + self.init_retry_count = 0 |
| 55 | + self.init_reset_flag = False |
| 56 | + |
49 | 57 | def connect(self) -> None: |
50 | 58 | """Opens the transport and starts the worker threads.""" |
51 | 59 | if self.transport.is_open: |
@@ -96,6 +104,92 @@ def disconnect(self) -> None: |
96 | 104 | self.transport.close() |
97 | 105 | self.logger.info("Transport closed.") |
98 | 106 |
|
| 107 | + def initialize(self) -> None: |
| 108 | + """Starts the initialization process.""" |
| 109 | + self.logger.info("Initializing device...") |
| 110 | + self.init_retry_count = 0 |
| 111 | + self.init_reset_flag = False |
| 112 | + |
| 113 | + # Schedule Disable Receiver (XQ) and wait briefly |
| 114 | + threading.Timer(SDUINO_INIT_WAIT_XQ, self._send_xq).start() |
| 115 | + |
| 116 | + # Schedule StartInit (Get Version) |
| 117 | + threading.Timer(SDUINO_INIT_WAIT, self._start_init).start() |
| 118 | + |
| 119 | + def _send_xq(self) -> None: |
| 120 | + try: |
| 121 | + self.logger.debug("Sending XQ to disable receiver during init") |
| 122 | + self.send_command("XQ", expect_response=False) |
| 123 | + except Exception as e: |
| 124 | + self.logger.warning("Failed to send XQ: %s", e) |
| 125 | + |
| 126 | + def _start_init(self) -> None: |
| 127 | + self.logger.info("StartInit, get version, retry = %d", self.init_retry_count) |
| 128 | + |
| 129 | + if self.init_retry_count == 0: |
| 130 | + # First attempt: XQ is sent via a separate timer in initialize(), no blocking wait here. |
| 131 | + pass |
| 132 | + |
| 133 | + if self.init_retry_count >= SDUINO_INIT_MAXRETRY: |
| 134 | + if not self.init_reset_flag: |
| 135 | + self.logger.warning("StartInit, retry count reached. Resetting device.") |
| 136 | + self.init_reset_flag = True |
| 137 | + self._reset_device() |
| 138 | + else: |
| 139 | + self.logger.error("StartInit, retry count reached after reset. Closing device.") |
| 140 | + self.disconnect() |
| 141 | + return |
| 142 | + |
| 143 | + response = None |
| 144 | + try: |
| 145 | + # Perl Regex: 'V\s.*SIGNAL(?:duino|ESP|STM).*(?:\s\d\d:\d\d:\d\d)' |
| 146 | + version_pattern = re.compile(r"V\s.*SIGNAL(?:duino|ESP|STM).*", re.IGNORECASE) |
| 147 | + # Use a short timeout here to speed up failed attempts |
| 148 | + response = self.send_command( |
| 149 | + "V", |
| 150 | + expect_response=True, |
| 151 | + timeout=2.0, # Shorter timeout for retries |
| 152 | + response_pattern=version_pattern, |
| 153 | + ) |
| 154 | + except Exception as e: |
| 155 | + self.logger.debug("StartInit: Exception during version check: %s", e) |
| 156 | + |
| 157 | + self._check_version_resp(response) |
| 158 | + |
| 159 | + def _check_version_resp(self, msg: Optional[str]) -> None: |
| 160 | + if msg: |
| 161 | + self.logger.info("Initialized %s", msg.strip()) |
| 162 | + self.init_reset_flag = False |
| 163 | + self.init_retry_count = 0 |
| 164 | + |
| 165 | + # Enable Receiver XE |
| 166 | + try: |
| 167 | + self.logger.info("Enabling receiver (XE)") |
| 168 | + self.send_command("XE", expect_response=False) |
| 169 | + except Exception as e: |
| 170 | + self.logger.warning("Failed to enable receiver: %s", e) |
| 171 | + |
| 172 | + # Check for CC1101 |
| 173 | + if "cc1101" in msg.lower(): |
| 174 | + self.logger.info("CC1101 detected") |
| 175 | + # Here we could query ccconf and ccpatable like in Perl |
| 176 | + else: |
| 177 | + self.logger.warning("StartInit: No valid version response.") |
| 178 | + self.init_retry_count += 1 |
| 179 | + # Retry initialization |
| 180 | + self._start_init() |
| 181 | + |
| 182 | + def _reset_device(self) -> None: |
| 183 | + self.logger.info("Resetting device...") |
| 184 | + try: |
| 185 | + self.disconnect() |
| 186 | + # Wait briefly to ensure port is released/device resets |
| 187 | + threading.Event().wait(2.0) |
| 188 | + self.connect() |
| 189 | + self.initialize() |
| 190 | + except Exception as e: |
| 191 | + self.logger.error("Failed to reset device: %s", e) |
| 192 | + |
99 | 193 | def _reader_loop(self) -> None: |
100 | 194 | """Continuously reads from the transport and puts lines into a queue.""" |
101 | 195 | self.logger.debug("Reader loop started.") |
@@ -123,10 +217,17 @@ def _parser_loop(self) -> None: |
123 | 217 | if not raw_line or self._stop_event.is_set(): |
124 | 218 | continue |
125 | 219 |
|
126 | | - if self._handle_as_command_response(raw_line.strip()): |
| 220 | + line_data = raw_line.strip() |
| 221 | + |
| 222 | + if self._handle_as_command_response(line_data): |
| 223 | + continue |
| 224 | + |
| 225 | + if line_data.startswith("XQ") or line_data.startswith("XR"): |
| 226 | + # Abfangen der Receiver-Statusmeldungen XQ/XR (wie in Perl /^XQ/ und /^XR/) |
| 227 | + self.logger.debug("Found receiver status: %s", line_data) |
127 | 228 | continue |
128 | 229 |
|
129 | | - decoded_messages = self.parser.parse_line(raw_line) |
| 230 | + decoded_messages = self.parser.parse_line(line_data) |
130 | 231 | for message in decoded_messages: |
131 | 232 | if self.mqtt_publisher: |
132 | 233 | try: |
@@ -228,7 +329,7 @@ def set_message_type_enabled( |
228 | 329 | raise ValueError(f"Invalid message type: {message_type}") |
229 | 330 |
|
230 | 331 | verb = "E" if enabled else "D" |
231 | | - noun = message_type # S, U, or C |
| 332 | + noun = message_type[-1] # S, U, or C |
232 | 333 | command = f"C{verb}{noun}" |
233 | 334 | self.send_command(command) |
234 | 335 |
|
|
0 commit comments