mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-19 01:41:30 +10:00
fix mcp-server review feedback from thread
Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/91dc128a-ed50-4d07-8bb2-3dc6623a05f7 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
This commit is contained in:
co-authored by
thebentern
parent
ec3c8378ef
commit
bab808cf62
@@ -10,6 +10,7 @@ artifacts to exist, so these tools build first if needed.
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -308,7 +309,7 @@ def _do_1200bps_touch(port: str, settle_ms: int, touch_timeout_s: float = 3.0) -
|
||||
worker that's still blocked in the background after timeout has already
|
||||
delivered the signal.
|
||||
"""
|
||||
import concurrent.futures
|
||||
errors: list[BaseException] = []
|
||||
|
||||
def _inner() -> None:
|
||||
try:
|
||||
@@ -325,12 +326,19 @@ def _do_1200bps_touch(port: str, settle_ms: int, touch_timeout_s: float = 3.0) -
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
future = pool.submit(_inner)
|
||||
def _runner() -> None:
|
||||
try:
|
||||
future.result(timeout=touch_timeout_s)
|
||||
except concurrent.futures.TimeoutError:
|
||||
pass # signal already delivered; worker thread leaks harmlessly
|
||||
_inner()
|
||||
except BaseException as exc: # re-raised on caller thread after join
|
||||
errors.append(exc)
|
||||
|
||||
worker = threading.Thread(target=_runner, daemon=True)
|
||||
worker.start()
|
||||
worker.join(timeout=touch_timeout_s)
|
||||
if worker.is_alive():
|
||||
return # signal already delivered; allow daemon worker to finish/exit
|
||||
if errors:
|
||||
raise errors[0]
|
||||
|
||||
|
||||
# Adafruit nRF52 bootloader VID/PID (BOTH RAK4631 and most Feather nRF52 boards).
|
||||
|
||||
@@ -54,18 +54,17 @@ def all_sessions() -> list[SerialSession]:
|
||||
|
||||
def sweep_dead() -> int:
|
||||
"""Remove sessions whose subprocess has exited. Returns count removed."""
|
||||
removed = 0
|
||||
removed_sessions: list[SerialSession] = []
|
||||
with _LOCK:
|
||||
for sid in list(_sessions):
|
||||
s = _sessions[sid]
|
||||
for sid, s in list(_sessions.items()):
|
||||
if s.proc.poll() is not None:
|
||||
# Leave the session object around for one read so callers can
|
||||
# see eof=True. We only remove after stopped_at is older than
|
||||
# a few seconds, so an immediate poll-after-close still sees
|
||||
# it. For v1 simpler: just leave dead sessions until explicit
|
||||
# close. No-op.
|
||||
pass
|
||||
return removed
|
||||
removed_sessions.append(_sessions.pop(sid))
|
||||
for session in removed_sessions:
|
||||
try:
|
||||
close_session(session)
|
||||
except Exception:
|
||||
pass
|
||||
return len(removed_sessions)
|
||||
|
||||
|
||||
def shutdown_all() -> None:
|
||||
|
||||
@@ -19,7 +19,7 @@ import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from . import config, pio
|
||||
from . import boards, config, pio
|
||||
|
||||
_BUFFER_MAX_LINES = 10_000
|
||||
_POLL_NEW_PORT_TIMEOUT_S = 3.0
|
||||
@@ -76,7 +76,43 @@ def open_session(
|
||||
effective_baud: int = baud
|
||||
if env is not None:
|
||||
args.extend(["-e", env])
|
||||
effective_filters = filters or [] # pio will fill in from env
|
||||
raw_config: dict[str, Any] = {}
|
||||
try:
|
||||
raw = boards.get_board(env).get("raw_config")
|
||||
if isinstance(raw, dict):
|
||||
raw_config = raw
|
||||
except Exception:
|
||||
raw_config = {}
|
||||
|
||||
monitor_speed = raw_config.get("monitor_speed")
|
||||
has_board_speed = False
|
||||
if monitor_speed is not None:
|
||||
try:
|
||||
effective_baud = int(str(monitor_speed).strip())
|
||||
has_board_speed = True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
monitor_filters_raw = raw_config.get("monitor_filters")
|
||||
parsed_board_filters: list[str] = []
|
||||
if isinstance(monitor_filters_raw, str):
|
||||
for token in monitor_filters_raw.replace("\n", ",").split(","):
|
||||
item = token.strip()
|
||||
if item:
|
||||
parsed_board_filters.append(item)
|
||||
elif isinstance(monitor_filters_raw, list):
|
||||
parsed_board_filters = [
|
||||
str(item).strip() for item in monitor_filters_raw if str(item).strip()
|
||||
]
|
||||
|
||||
has_board_filters = len(parsed_board_filters) > 0
|
||||
effective_filters = parsed_board_filters if has_board_filters else (filters or [])
|
||||
|
||||
if not has_board_speed:
|
||||
args.extend(["--baud", str(effective_baud)])
|
||||
if not has_board_filters:
|
||||
for f in effective_filters:
|
||||
args.extend(["--filter", f])
|
||||
else:
|
||||
args.extend(["--baud", str(baud)])
|
||||
effective_filters = filters or ["direct"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""FastMCP server wiring — 32 tools across 6 categories.
|
||||
"""FastMCP server wiring — 38 tools across 7 categories.
|
||||
|
||||
Each tool handler is a thin delegation to a named module (pio.py, admin.py,
|
||||
etc.). Business logic does not live here.
|
||||
|
||||
Reference in New Issue
Block a user