| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- # -*- coding: utf-8 -*-
- """
- Abstrakte Basis-Anwendungsklasse für Trixy.
- Definiert die gemeinsame Struktur für Server, Client, Standalone und Trainer.
- """
- from __future__ import annotations
- import asyncio
- import os
- import signal
- import sys
- from abc import ABC, abstractmethod
- from pathlib import Path
- from typing import Any, TYPE_CHECKING
- if TYPE_CHECKING:
- from trixy_core.sync.store import SyncStore
- from trixy_core.service.service_container import ServiceContainer
- from trixy_core.events.eventmanager import EventManager
- from trixy_core.config.config_manager import ConfigManager
- from trixy_core.utils.debug import pinfo, pdebug, perror, set_debug_mode, _setup_file_routing
- from trixy_core.utils.version import VERSION_STRING
- class IApplication(ABC):
- """
- Abstrakte Basisklasse für alle Trixy-Anwendungsmodi.
- Stellt gemeinsame Infrastruktur bereit:
- - ServiceContainer für Service-Management
- - EventManager für Pub/Sub
- - ConfigManager für Konfiguration
- """
- def __init__(self, debug: bool = False) -> None:
- """
- Initialisiert die Anwendung.
- Args:
- debug: Debug-Modus aktivieren
- """
- self._debug = debug
- set_debug_mode(debug)
- self._running = False
- self._shutdown_event = asyncio.Event()
- # Core-Komponenten initialisieren
- self._config_manager = ConfigManager()
- self._event_manager = EventManager(self)
- self._service_container = ServiceContainer(self)
- # Sync-Store (optional, wird von Standalone/Server gesetzt)
- self._sync_store: "SyncStore | None" = None
- # Basis-Pfade
- self._base_path = Path.cwd()
- @property
- def debug(self) -> bool:
- """Debug-Modus Status."""
- return self._debug
- @property
- def running(self) -> bool:
- """Läuft die Anwendung?"""
- return self._running
- @property
- def config_manager(self) -> ConfigManager:
- """ConfigManager-Instanz."""
- return self._config_manager
- @property
- def config(self) -> ConfigManager:
- """Alias für config_manager."""
- return self._config_manager
- @property
- def events(self) -> EventManager:
- """EventManager-Instanz."""
- return self._event_manager
- @property
- def services(self) -> ServiceContainer:
- """ServiceContainer-Instanz."""
- return self._service_container
- @property
- def sync_store(self) -> "SyncStore | None":
- """SyncStore-Instanz (falls verfuegbar)."""
- return self._sync_store
- @property
- def base_path(self) -> Path:
- """Basis-Pfad der Anwendung."""
- return self._base_path
- @abstractmethod
- async def initialize(self) -> None:
- """
- Initialisiert die Anwendung.
- Wird vor dem Start aufgerufen, um Konfiguration zu laden
- und Services zu registrieren.
- """
- pass
- @abstractmethod
- async def start(self) -> None:
- """
- Startet die Anwendung.
- Startet alle Services und beginnt die Hauptschleife.
- """
- pass
- @abstractmethod
- async def stop(self) -> None:
- """
- Stoppt die Anwendung.
- Stoppt alle Services und gibt Ressourcen frei.
- """
- pass
- async def run(self) -> None:
- """
- Hauptmethode zum Ausführen der Anwendung.
- Initialisiert, startet und wartet auf Shutdown-Signal.
- """
- pinfo(f"Trixy v{VERSION_STRING} wird gestartet...")
- # Signal-Handler einrichten
- loop = asyncio.get_running_loop()
- if sys.platform == "win32":
- # Windows: add_signal_handler nicht verfügbar
- for sig in (signal.SIGINT, signal.SIGTERM):
- signal.signal(sig, lambda s, f: loop.call_soon_threadsafe(
- lambda: asyncio.create_task(self._signal_handler())
- ))
- else:
- for sig in (signal.SIGINT, signal.SIGTERM):
- loop.add_signal_handler(
- sig,
- lambda: asyncio.create_task(self._signal_handler())
- )
- try:
- await self.initialize()
- await self.start()
- self._running = True
- pinfo("Anwendung gestartet")
- # Warte auf Shutdown-Signal
- await self._shutdown_event.wait()
- except Exception as e:
- perror(f"Fehler beim Starten: {e}")
- raise
- finally:
- await self.stop()
- pinfo("Anwendung beendet")
- # Force-Exit: Blockierende Executor-Threads (stdin input/read_keys)
- # verhindern sauberen asyncio-Shutdown (300s Timeout).
- # Da stop() bereits alle Services und Ressourcen aufgeräumt hat,
- # ist os._exit() hier sicher.
- os._exit(0)
- async def _signal_handler(self) -> None:
- """Behandelt Shutdown-Signale."""
- if not self._running:
- return
- pinfo("Shutdown-Signal empfangen...")
- self._running = False
- self._shutdown_event.set()
- def shutdown(self) -> None:
- """Löst einen Shutdown aus."""
- self._shutdown_event.set()
- def _init_file_logging(self, logging_config) -> None:
- """
- Initialisiert das File-Logging-System.
- Args:
- logging_config: LoggingConfig-Instanz mit Logging-Parametern
- """
- from trixy_core.utils.logging.file_router import init_file_router
- router = init_file_router(
- log_directory=logging_config.log_directory,
- file_log_level=logging_config.file_log_level,
- max_file_size_mb=logging_config.max_file_size_mb,
- backup_count=logging_config.backup_count,
- )
- _setup_file_routing(router)
- pdebug(f"File-Logging initialisiert: {logging_config.log_directory}/")
- def _shutdown_file_logging(self) -> None:
- """Faehrt das File-Logging-System herunter."""
- from trixy_core.utils.logging.file_router import shutdown_file_router
- _setup_file_routing(None)
- shutdown_file_router()
- def get_path(self, *parts: str) -> Path:
- """
- Gibt einen Pfad relativ zum Basis-Pfad zurück.
- Args:
- *parts: Pfad-Komponenten
- Returns:
- Vollständiger Pfad
- """
- return self._base_path.joinpath(*parts)
|