Release COM port after each versapad MCP tool call
get_board_status()/load_from_board()/write_to_board() kept the serial link open indefinitely after use (VersaPadLink._ensure_open() never closes on its own). Any session that called one of these left the COM port locked for VersaGUI/desktop_viewer/server.py's board fallback until the MCP server process was killed. Close the link in a finally block after each call instead.
This commit is contained in:
parent
540ce5b1eb
commit
8ba524c07b
1 changed files with 38 additions and 24 deletions
|
|
@ -116,9 +116,15 @@ def get_macro(slot: int) -> dict:
|
||||||
def get_board_status() -> dict:
|
def get_board_status() -> dict:
|
||||||
"""Prueft per Serial, ob das Board erreichbar ist und welches Profil dort
|
"""Prueft per Serial, ob das Board erreichbar ist und welches Profil dort
|
||||||
gerade aktiv ist. Schlaegt fehl/liefert busy, wenn VersaGUI oder der
|
gerade aktiv ist. Schlaegt fehl/liefert busy, wenn VersaGUI oder der
|
||||||
Tkinter-Viewer den COM-Port gerade halten."""
|
Tkinter-Viewer den COM-Port gerade halten. Gibt den COM-Port danach
|
||||||
profile = _link.read_active_profile()
|
sofort wieder frei (kein dauerhaft offen gehaltener Serial-Handle --
|
||||||
return {"connected": profile is not None, "active_profile": profile, "error": _link.last_error}
|
sonst blockiert dieser Prozess andere Tools/Viewer mit "busy", bis er
|
||||||
|
beendet wird)."""
|
||||||
|
try:
|
||||||
|
profile = _link.read_active_profile()
|
||||||
|
return {"connected": profile is not None, "active_profile": profile, "error": _link.last_error}
|
||||||
|
finally:
|
||||||
|
_link.close()
|
||||||
|
|
||||||
|
|
||||||
# ── Buttons (20 pro Profil, MX-Matrix) ───────────────────────────────────────
|
# ── Buttons (20 pro Profil, MX-Matrix) ───────────────────────────────────────
|
||||||
|
|
@ -299,22 +305,26 @@ def load_from_board() -> dict:
|
||||||
"""Liest die komplette Config + Makros vom Board (per Serial, ~1-2s) und
|
"""Liest die komplette Config + Makros vom Board (per Serial, ~1-2s) und
|
||||||
ersetzt damit den In-Memory-State. Profilnamen bleiben erhalten (die
|
ersetzt damit den In-Memory-State. Profilnamen bleiben erhalten (die
|
||||||
kennt nur wir, nicht das Board). Schlaegt fehl, wenn der COM-Port gerade
|
kennt nur wir, nicht das Board). Schlaegt fehl, wenn der COM-Port gerade
|
||||||
von VersaGUI/dem Tkinter-Viewer gehalten wird."""
|
von VersaGUI/dem Tkinter-Viewer gehalten wird. Gibt den COM-Port danach
|
||||||
raw_cfg = _link.read_full_config()
|
sofort wieder frei (siehe get_board_status())."""
|
||||||
if raw_cfg is None:
|
try:
|
||||||
raise RuntimeError(f"Config laden fehlgeschlagen: {_link.last_error}")
|
raw_cfg = _link.read_full_config()
|
||||||
raw_macros = _link.read_macros()
|
if raw_cfg is None:
|
||||||
if raw_macros is None:
|
raise RuntimeError(f"Config laden fehlgeschlagen: {_link.last_error}")
|
||||||
raise RuntimeError(f"Makros laden fehlgeschlagen: {_link.last_error}")
|
raw_macros = _link.read_macros()
|
||||||
|
if raw_macros is None:
|
||||||
|
raise RuntimeError(f"Makros laden fehlgeschlagen: {_link.last_error}")
|
||||||
|
|
||||||
cfg_dict = vproto.unpack_config(raw_cfg)
|
cfg_dict = vproto.unpack_config(raw_cfg)
|
||||||
if not (cfg_dict["magic_ok"] and cfg_dict["crc_ok"]):
|
if not (cfg_dict["magic_ok"] and cfg_dict["crc_ok"]):
|
||||||
raise RuntimeError("Board-Antwort ungueltig (Magic/CRC)")
|
raise RuntimeError("Board-Antwort ungueltig (Magic/CRC)")
|
||||||
macro_slots = vproto.unpack_macros(raw_macros)
|
macro_slots = vproto.unpack_macros(raw_macros)
|
||||||
|
|
||||||
names = _cfg()["profile_names"]
|
names = _cfg()["profile_names"]
|
||||||
_state["combined"] = vcomb.from_binary(cfg_dict, macro_slots, profile_names=names)
|
_state["combined"] = vcomb.from_binary(cfg_dict, macro_slots, profile_names=names)
|
||||||
return list_profiles()
|
return list_profiles()
|
||||||
|
finally:
|
||||||
|
_link.close()
|
||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
|
|
@ -322,13 +332,17 @@ def write_to_board() -> dict:
|
||||||
"""Schreibt den kompletten In-Memory-State (alle 3 Profile + Makros) aufs
|
"""Schreibt den kompletten In-Memory-State (alle 3 Profile + Makros) aufs
|
||||||
Board -- ueberschreibt, was dort aktuell im NVM steht. Firmware prueft
|
Board -- ueberschreibt, was dort aktuell im NVM steht. Firmware prueft
|
||||||
Magic/CRC/Keycode-Bereich vor jedem Schreiben und antwortet sonst nur mit
|
Magic/CRC/Keycode-Bereich vor jedem Schreiben und antwortet sonst nur mit
|
||||||
NACK (kein Risiko fuer Datenmuell). Schlaegt fehl bei belegtem COM-Port."""
|
NACK (kein Risiko fuer Datenmuell). Schlaegt fehl bei belegtem COM-Port.
|
||||||
cfg_bytes, macro_bytes = vcomb.to_binary(_cfg())
|
Gibt den COM-Port danach sofort wieder frei (siehe get_board_status())."""
|
||||||
if not _link.write_full_config(cfg_bytes):
|
try:
|
||||||
raise RuntimeError(f"Config-Schreiben fehlgeschlagen: {_link.last_error}")
|
cfg_bytes, macro_bytes = vcomb.to_binary(_cfg())
|
||||||
if not _link.write_macros(macro_bytes):
|
if not _link.write_full_config(cfg_bytes):
|
||||||
raise RuntimeError(f"Makros-Schreiben fehlgeschlagen: {_link.last_error}")
|
raise RuntimeError(f"Config-Schreiben fehlgeschlagen: {_link.last_error}")
|
||||||
return {"ok": True}
|
if not _link.write_macros(macro_bytes):
|
||||||
|
raise RuntimeError(f"Makros-Schreiben fehlgeschlagen: {_link.last_error}")
|
||||||
|
return {"ok": True}
|
||||||
|
finally:
|
||||||
|
_link.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue