lr-shuttle 0.2.9__py3-none-any.whl → 0.2.10__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.
Potentially problematic release.
This version of lr-shuttle might be problematic. Click here for more details.
- {lr_shuttle-0.2.9.dist-info → lr_shuttle-0.2.10.dist-info}/METADATA +1 -1
- {lr_shuttle-0.2.9.dist-info → lr_shuttle-0.2.10.dist-info}/RECORD +8 -8
- shuttle/cli.py +18 -5
- shuttle/firmware/esp32c5/devboard.ino.bin +0 -0
- shuttle/serial_client.py +47 -24
- {lr_shuttle-0.2.9.dist-info → lr_shuttle-0.2.10.dist-info}/WHEEL +0 -0
- {lr_shuttle-0.2.9.dist-info → lr_shuttle-0.2.10.dist-info}/entry_points.txt +0 -0
- {lr_shuttle-0.2.9.dist-info → lr_shuttle-0.2.10.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
shuttle/cli.py,sha256
|
|
1
|
+
shuttle/cli.py,sha256=-Lb5d3dwmJ0a2pFC9vrApQPx92x4Vw68OAaab6fawp0,110024
|
|
2
2
|
shuttle/constants.py,sha256=GUlAg3iEuPxLQ2mDCvlv5gVXHnlawl_YeLtaUSqsnPM,757
|
|
3
3
|
shuttle/flash.py,sha256=9ph23MHL40SjKZoL38Sbd3JbykGb-ECxvzBzCIjAues,4492
|
|
4
4
|
shuttle/prodtest.py,sha256=nI8k2OndhqsOv8BMtXwfcpGEdmHU7ywbIgMuW49EULU,8006
|
|
5
|
-
shuttle/serial_client.py,sha256=
|
|
5
|
+
shuttle/serial_client.py,sha256=8VPLP2nrbXIb34pWsnuXBNJ4K82bkdy3H9QWD7n0TVk,21147
|
|
6
6
|
shuttle/timo.py,sha256=SfWgiYUtPjSsUln5hgDLiYMYOt8zg1DLL5t07sgu2wY,18336
|
|
7
7
|
shuttle/firmware/__init__.py,sha256=KRXyz3xJ2GIB473tCHAky3DdPIQb78gX64Qn-uu55To,120
|
|
8
8
|
shuttle/firmware/esp32c5/__init__.py,sha256=U2xXnb80Wv8EJaJ6Tv9iev1mVlpoaEeqsNmjmEtxdFQ,41
|
|
9
9
|
shuttle/firmware/esp32c5/boot_app0.bin,sha256=-UxdeGp6j6sGrF0Q4zvzdxGmaXY23AN1WeoZzEEKF_A,8192
|
|
10
|
-
shuttle/firmware/esp32c5/devboard.ino.bin,sha256=
|
|
10
|
+
shuttle/firmware/esp32c5/devboard.ino.bin,sha256=OIGcK7ZFonpK67YaYfQLzxCPzIikrj-KfgvgeJ5jGe8,1101248
|
|
11
11
|
shuttle/firmware/esp32c5/devboard.ino.bootloader.bin,sha256=LPU51SdUwebYemCZb5Pya-wGe7RC4UXrkRmBnsHePp0,20784
|
|
12
12
|
shuttle/firmware/esp32c5/devboard.ino.partitions.bin,sha256=FIuVnL_xw4qo4dXAup1hLFSZe5ReVqY_QSI-72UGU6E,3072
|
|
13
13
|
shuttle/firmware/esp32c5/manifest.json,sha256=CPOegfEK4PTtI6UPeohuUKkJNeg0t8aWntEczpoxYt4,480
|
|
14
|
-
lr_shuttle-0.2.
|
|
15
|
-
lr_shuttle-0.2.
|
|
16
|
-
lr_shuttle-0.2.
|
|
17
|
-
lr_shuttle-0.2.
|
|
18
|
-
lr_shuttle-0.2.
|
|
14
|
+
lr_shuttle-0.2.10.dist-info/METADATA,sha256=iP9luZKiBfh8p52Ki419v-KB24CM54sbkjuerFim0hM,15575
|
|
15
|
+
lr_shuttle-0.2.10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
16
|
+
lr_shuttle-0.2.10.dist-info/entry_points.txt,sha256=obqdFPgvQLB1_EWcnD9ch8HjQRlNVT_pdB_EidDRDco,44
|
|
17
|
+
lr_shuttle-0.2.10.dist-info/top_level.txt,sha256=PtNxNQQdya-Xs8DYublNTBTa8c1TrtfEpQ0lUd_OeZY,8
|
|
18
|
+
lr_shuttle-0.2.10.dist-info/RECORD,,
|
shuttle/cli.py
CHANGED
|
@@ -151,6 +151,19 @@ def _handle_sys_error_event(event: Dict[str, Any]) -> None:
|
|
|
151
151
|
console.print(f"[red]{' '.join(parts)}[/]")
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
def _flush_client_input(client) -> None:
|
|
155
|
+
"""Best-effort drain any unread serial noise before issuing commands."""
|
|
156
|
+
|
|
157
|
+
flush = getattr(client, "flush_input_and_log", None)
|
|
158
|
+
if flush is None:
|
|
159
|
+
return
|
|
160
|
+
try:
|
|
161
|
+
flush()
|
|
162
|
+
except Exception:
|
|
163
|
+
# Flushing is opportunistic; failures should not leak into CLI flows.
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
|
|
154
167
|
@contextmanager
|
|
155
168
|
def _open_serial_client(
|
|
156
169
|
resolved_port: str,
|
|
@@ -769,7 +782,7 @@ def _execute_timo_sequence(
|
|
|
769
782
|
) as client:
|
|
770
783
|
# Drain any pending serial noise before issuing commands, to avoid
|
|
771
784
|
# mixing stale data into NDJSON responses.
|
|
772
|
-
client
|
|
785
|
+
_flush_client_input(client)
|
|
773
786
|
for transfer in sequence:
|
|
774
787
|
response = client.spi_xfer(**transfer)
|
|
775
788
|
responses.append(response)
|
|
@@ -2637,7 +2650,7 @@ def spi_enable_command(
|
|
|
2637
2650
|
logger=resources.get("logger"),
|
|
2638
2651
|
seq_tracker=resources.get("seq_tracker"),
|
|
2639
2652
|
) as client:
|
|
2640
|
-
client
|
|
2653
|
+
_flush_client_input(client)
|
|
2641
2654
|
response = client.spi_enable()
|
|
2642
2655
|
except ShuttleSerialError as exc:
|
|
2643
2656
|
console.print(f"[red]{exc}[/]")
|
|
@@ -2672,7 +2685,7 @@ def spi_disable_command(
|
|
|
2672
2685
|
logger=resources.get("logger"),
|
|
2673
2686
|
seq_tracker=resources.get("seq_tracker"),
|
|
2674
2687
|
) as client:
|
|
2675
|
-
client
|
|
2688
|
+
_flush_client_input(client)
|
|
2676
2689
|
response = client.spi_disable()
|
|
2677
2690
|
except ShuttleSerialError as exc:
|
|
2678
2691
|
console.print(f"[red]{exc}[/]")
|
|
@@ -3143,7 +3156,7 @@ def power_command(
|
|
|
3143
3156
|
logger=resources.get("logger"),
|
|
3144
3157
|
seq_tracker=resources.get("seq_tracker"),
|
|
3145
3158
|
) as client:
|
|
3146
|
-
client
|
|
3159
|
+
_flush_client_input(client)
|
|
3147
3160
|
method = getattr(client, method_name)
|
|
3148
3161
|
response = method()
|
|
3149
3162
|
except ShuttleSerialError as exc:
|
|
@@ -3219,7 +3232,7 @@ def flash_command(
|
|
|
3219
3232
|
with NDJSONSerialClient(
|
|
3220
3233
|
resolved_port, baudrate=baudrate, timeout=0.5, logger=logger
|
|
3221
3234
|
) as client:
|
|
3222
|
-
client
|
|
3235
|
+
_flush_client_input(client)
|
|
3223
3236
|
except Exception:
|
|
3224
3237
|
pass
|
|
3225
3238
|
|
|
Binary file
|
shuttle/serial_client.py
CHANGED
|
@@ -253,7 +253,7 @@ class NDJSONSerialClient:
|
|
|
253
253
|
except AttributeError:
|
|
254
254
|
# Test stubs without an open() method are already "connected"
|
|
255
255
|
pass
|
|
256
|
-
self.
|
|
256
|
+
self._reset_input_buffer()
|
|
257
257
|
self._lock = threading.Lock()
|
|
258
258
|
self._pending: Dict[int, CommandFuture] = {}
|
|
259
259
|
self._response_backlog: Dict[int, Dict[str, Any]] = {}
|
|
@@ -281,9 +281,21 @@ class NDJSONSerialClient:
|
|
|
281
281
|
if getattr(self, "_serial", None) and self._serial.is_open:
|
|
282
282
|
self._serial.close()
|
|
283
283
|
|
|
284
|
+
def _reset_input_buffer(self) -> None:
|
|
285
|
+
serial_obj = getattr(self, "_serial", None)
|
|
286
|
+
if serial_obj is None:
|
|
287
|
+
return
|
|
288
|
+
reset = getattr(serial_obj, "reset_input_buffer", None)
|
|
289
|
+
if reset is None:
|
|
290
|
+
return
|
|
291
|
+
try:
|
|
292
|
+
reset()
|
|
293
|
+
except SerialException:
|
|
294
|
+
pass
|
|
295
|
+
|
|
284
296
|
def flush_input_and_log(self):
|
|
285
297
|
"""Read and log all available data from the serial buffer before sending a command."""
|
|
286
|
-
if not hasattr(self, "_serial")
|
|
298
|
+
if not hasattr(self, "_serial"):
|
|
287
299
|
return
|
|
288
300
|
try:
|
|
289
301
|
while True:
|
|
@@ -295,6 +307,8 @@ class NDJSONSerialClient:
|
|
|
295
307
|
self._log_serial("RX", data)
|
|
296
308
|
except Exception:
|
|
297
309
|
pass
|
|
310
|
+
finally:
|
|
311
|
+
self._reset_input_buffer()
|
|
298
312
|
|
|
299
313
|
def send_command(self, op: str, params: Dict[str, Any]) -> CommandFuture:
|
|
300
314
|
"""Send a command without blocking, returning a future for the response."""
|
|
@@ -475,28 +489,37 @@ class NDJSONSerialClient:
|
|
|
475
489
|
self._log_serial("TX", payload)
|
|
476
490
|
|
|
477
491
|
def _read(self) -> Optional[Dict[str, Any]]:
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
492
|
+
while True:
|
|
493
|
+
try:
|
|
494
|
+
line = self._serial.readline()
|
|
495
|
+
except SerialException as exc: # pragma: no cover - hardware specific
|
|
496
|
+
raise ShuttleSerialError(f"Serial read failed: {exc}") from exc
|
|
497
|
+
if not line:
|
|
498
|
+
return None
|
|
499
|
+
self._log_serial("RX", line)
|
|
500
|
+
stripped = line.strip()
|
|
501
|
+
if not stripped:
|
|
502
|
+
return None
|
|
503
|
+
try:
|
|
504
|
+
decoded = stripped.decode("utf-8")
|
|
505
|
+
except UnicodeDecodeError as exc:
|
|
506
|
+
self._reset_input_buffer()
|
|
507
|
+
raise ShuttleSerialError(f"Invalid UTF-8 from device: {exc}") from exc
|
|
508
|
+
trimmed = decoded.lstrip()
|
|
509
|
+
if not trimmed:
|
|
510
|
+
continue
|
|
511
|
+
if trimmed[0] not in ("{", "["):
|
|
512
|
+
self._reset_input_buffer()
|
|
513
|
+
continue
|
|
514
|
+
try:
|
|
515
|
+
message = json.loads(decoded)
|
|
516
|
+
except json.JSONDecodeError as exc:
|
|
517
|
+
self._reset_input_buffer()
|
|
518
|
+
raise ShuttleSerialError(
|
|
519
|
+
f"Invalid JSON from device: {decoded} ({exc})"
|
|
520
|
+
) from exc
|
|
521
|
+
self._record_sequence(message)
|
|
522
|
+
return message
|
|
500
523
|
|
|
501
524
|
def _dispatch(self, message: Dict[str, Any]) -> None:
|
|
502
525
|
mtype = message.get("type")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|