diff --git a/2026-06-14 15-31-58 Pacienti.xlsx b/2026-06-14 15-31-58 Pacienti.xlsx new file mode 100644 index 0000000..a809c2b Binary files /dev/null and b/2026-06-14 15-31-58 Pacienti.xlsx differ diff --git a/2026-06-14 15-46-05 Laborator 2026-06-12.xlsx b/2026-06-14 15-46-05 Laborator 2026-06-12.xlsx new file mode 100644 index 0000000..6114212 Binary files /dev/null and b/2026-06-14 15-46-05 Laborator 2026-06-12.xlsx differ diff --git a/Knihovny/medicus_db.py b/Knihovny/medicus_db.py index 9bd9b71..a8b1b98 100644 --- a/Knihovny/medicus_db.py +++ b/Knihovny/medicus_db.py @@ -31,6 +31,72 @@ def get_medicus_connection(): return fdb.connect(dsn=dsn, user="SYSDBA", password="masterkey", charset="win1250") +class ReconnectingConnection: + """Obal nad fdb.Connection, který se sám znovu připojí, když spojení umře. + + Dlouho běžící procesy (např. MCP server) drží jedno spojení od startu. + To se rozpadne, když: + * se notebook uspí/hibernuje (TCP socket odumře), nebo + * na serveru proběhne denní gbak restore (Firebird zabije stará spojení). + Výsledkem je `SQLCODE -902 Error writing data to the connection`. + + Tato třída ověří před každým použitím, že spojení žije, a když ne, + tiše ho znovu naváže. Volající kód používá .cursor()/.commit() beze změny. + """ + + def __init__(self, connect_fn=get_medicus_connection): + self._connect = connect_fn + self._conn = None + + def _alive(self): + if self._conn is None: + return False + try: + cur = self._conn.cursor() + cur.execute("SELECT 1 FROM RDB$DATABASE") + cur.fetchone() + return True + except Exception: + return False + + def _ensure(self): + if not self._alive(): + if self._conn is not None: + try: + self._conn.close() + except Exception: + pass + self._conn = None + self._conn = self._connect() + return self._conn + + def cursor(self): + return self._ensure().cursor() + + def commit(self): + return self._ensure().commit() + + def rollback(self): + if self._conn is not None: + return self._conn.rollback() + + def close(self): + if self._conn is not None: + try: + self._conn.close() + finally: + self._conn = None + + def __getattr__(self, name): + # ostatní atributy/metody proxy na živé spojení + return getattr(self._ensure(), name) + + +def get_medicus_connection_reconnecting(): + """Vrátí spojení s automatickým reconnectem (vhodné pro dlouho běžící procesy).""" + return ReconnectingConnection(get_medicus_connection) + + def get_medicus_db(): """Vrátí MedicusDB instanci s připojením podle názvu počítače.""" conn = get_medicus_connection() diff --git a/mcp_firebird.py b/mcp_firebird.py index 4355a9a..7cd62f8 100644 --- a/mcp_firebird.py +++ b/mcp_firebird.py @@ -12,20 +12,23 @@ from typing import Optional from mcp.server.fastmcp import FastMCP sys.path.insert(0, str(Path(__file__).resolve().parent)) -from Knihovny.medicus_db import get_medicus_connection +from Knihovny.medicus_db import get_medicus_connection_reconnecting # Všechny logy MUSÍ jít na stderr — stdout je rezervován pro JSON-RPC def log(msg: str): print(msg, file=sys.stderr, flush=True) -# Připojení k Firebirdu +# Připojení k Firebirdu — lazy + auto-reconnect. +# Spojení se naváže až při prvním dotazu a samo se obnoví, když umře +# (uspání notebooku, denní gbak restore na serveru). Proto nepadáme při startu, +# i kdyby DB zrovna nebyla dostupná. +conn = get_medicus_connection_reconnecting() try: - conn = get_medicus_connection() + conn.cursor().execute("SELECT 1 FROM RDB$DATABASE") log("✓ Připojeno k Firebirdu (Medicus)") except Exception as e: - log(f"✗ Chyba připojení k Firebirdu: {e}") - sys.exit(1) + log(f"⚠ Firebird zatím nedostupný, zkusím se připojit při prvním dotazu: {e}") def rows_to_json(rows, description):