Files
ComConfigCopy/src/core/state.py

413 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Any, Dict, Optional, List
from dataclasses import dataclass, field
from enum import Enum, auto
import logging
from datetime import datetime
from .events import event_bus, Event, EventTypes
class ConnectionState(Enum):
"""Состояния подключения."""
DISCONNECTED = auto()
CONNECTING = auto()
CONNECTED = auto()
ERROR = auto()
class TransferState(Enum):
"""Состояния передачи данных."""
IDLE = auto()
PREPARING = auto()
TRANSFERRING = auto()
COMPLETED = auto()
ERROR = auto()
@dataclass
class DeviceInfo:
"""Информация об устройстве."""
version: Optional[str] = None
model: Optional[str] = None
hostname: Optional[str] = None
interfaces: List[Dict[str, Any]] = field(default_factory=list)
last_update: Optional[datetime] = None
@dataclass
class ConfigInfo:
"""Информация о файле конфигурации."""
name: str
path: str
size: int
modified: datetime
created: datetime
is_watched: bool = False
@dataclass
class ApplicationState:
"""Состояние приложения."""
connection_state: ConnectionState = ConnectionState.DISCONNECTED
transfer_state: TransferState = TransferState.IDLE
device_info: DeviceInfo = field(default_factory=DeviceInfo)
configs: Dict[str, ConfigInfo] = field(default_factory=dict)
current_config: Optional[str] = None
transfer_progress: float = 0.0
status_message: str = ""
error_message: Optional[str] = None
is_tftp_server_running: bool = False
custom_data: Dict[str, Any] = field(default_factory=dict)
class StateManager:
"""Менеджер состояния приложения."""
def __init__(self):
self._state = ApplicationState()
self._logger = logging.getLogger(__name__)
self._setup_event_handlers()
def _setup_event_handlers(self) -> None:
"""Настройка обработчиков событий."""
event_bus.subscribe(EventTypes.CONNECTION_ESTABLISHED, self._handle_connection_established)
event_bus.subscribe(EventTypes.CONNECTION_LOST, self._handle_connection_lost)
event_bus.subscribe(EventTypes.CONNECTION_ERROR, self._handle_connection_error)
event_bus.subscribe(EventTypes.TRANSFER_STARTED, self._handle_transfer_started)
event_bus.subscribe(EventTypes.TRANSFER_PROGRESS, self._handle_transfer_progress)
event_bus.subscribe(EventTypes.TRANSFER_COMPLETED, self._handle_transfer_completed)
event_bus.subscribe(EventTypes.TRANSFER_ERROR, self._handle_transfer_error)
event_bus.subscribe(EventTypes.TFTP_SERVER_STARTED, self._handle_tftp_server_started)
event_bus.subscribe(EventTypes.TFTP_SERVER_STOPPED, self._handle_tftp_server_stopped)
# Подписка на события конфигурации
event_bus.subscribe(EventTypes.CONFIG_CREATED, self._handle_config_created)
event_bus.subscribe(EventTypes.CONFIG_MODIFIED, self._handle_config_modified)
event_bus.subscribe(EventTypes.CONFIG_DELETED, self._handle_config_deleted)
def _handle_connection_established(self, event: Event) -> None:
self._state.connection_state = ConnectionState.CONNECTED
self._state.error_message = None
self._update_status("Подключение установлено")
def _handle_connection_lost(self, event: Event) -> None:
self._state.connection_state = ConnectionState.DISCONNECTED
self._update_status("Подключение потеряно")
def _handle_connection_error(self, event: Event) -> None:
self._state.connection_state = ConnectionState.ERROR
self._state.error_message = str(event.data)
self._update_status(f"Ошибка подключения: {event.data}")
def _handle_transfer_started(self, event: Event) -> None:
self._state.transfer_state = TransferState.TRANSFERRING
self._state.transfer_progress = 0.0
self._update_status("Передача данных начата")
def _handle_transfer_progress(self, event: Event) -> None:
self._state.transfer_progress = float(event.data)
self._update_status(f"Прогресс передачи: {self._state.transfer_progress:.1f}%")
def _handle_transfer_completed(self, event: Event) -> None:
self._state.transfer_state = TransferState.COMPLETED
self._state.transfer_progress = 100.0
self._update_status("Передача данных завершена")
def _handle_transfer_error(self, event: Event) -> None:
self._state.transfer_state = TransferState.ERROR
self._state.error_message = str(event.data)
self._update_status(f"Ошибка передачи: {event.data}")
def _handle_tftp_server_started(self, event: Event) -> None:
self._state.is_tftp_server_running = True
self._update_status("TFTP сервер запущен")
def _handle_tftp_server_stopped(self, event: Event) -> None:
self._state.is_tftp_server_running = False
self._update_status("TFTP сервер остановлен")
def _handle_config_created(self, event: Event) -> None:
"""
Обработка создания конфигурации.
Args:
event: Событие создания конфигурации
"""
try:
self.update_config_info(event.data)
self._update_status(f"Конфигурация создана: {event.data['name']}")
except Exception as e:
self._logger.error(f"Ошибка обработки создания конфигурации: {e}")
def _handle_config_modified(self, event: Event) -> None:
"""
Обработка изменения конфигурации.
Args:
event: Событие изменения конфигурации
"""
try:
self.update_config_info(event.data)
self._update_status(f"Конфигурация изменена: {event.data['name']}")
except Exception as e:
self._logger.error(f"Ошибка обработки изменения конфигурации: {e}")
def _handle_config_deleted(self, event: Event) -> None:
"""
Обработка удаления конфигурации.
Args:
event: Событие удаления конфигурации
"""
try:
config_name = event.data['name']
self.remove_config_info(config_name)
self._update_status(f"Конфигурация удалена: {config_name}")
except Exception as e:
self._logger.error(f"Ошибка обработки удаления конфигурации: {e}")
def _update_status(self, message: str) -> None:
"""Обновление статусного сообщения."""
self._state.status_message = message
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, message))
def update_device_info(self, info: Dict[str, Any]) -> None:
"""
Обновление информации об устройстве.
Args:
info: Словарь с информацией об устройстве
"""
try:
self._state.device_info = DeviceInfo(
version=info.get("version"),
model=info.get("model"),
hostname=info.get("hostname"),
interfaces=info.get("interfaces", []),
last_update=datetime.now()
)
# Обновляем состояние подключения
self._state.connection_state = ConnectionState.CONNECTED
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": f"Подключено к {self._state.device_info.hostname}"
}))
self._logger.info("Информация об устройстве обновлена")
except Exception as e:
self._logger.error(f"Ошибка обновления информации об устройстве: {e}")
self._state.connection_state = ConnectionState.ERROR
self._state.error_message = str(e)
def clear_device_info(self) -> None:
"""Очистка информации об устройстве."""
self._state.device_info = DeviceInfo()
self._state.connection_state = ConnectionState.DISCONNECTED
self._state.error_message = None
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": "Отключено от устройства"
}))
self._logger.info("Информация об устройстве очищена")
def update_config_info(self, info: Dict[str, Any]) -> None:
"""
Обновление информации о файле конфигурации.
Args:
info: Словарь с информацией о файле
"""
try:
config = ConfigInfo(
name=info["name"],
path=info["path"],
size=info["size"],
modified=info["modified"],
created=info["created"],
is_watched=info.get("is_watched", False)
)
self._state.configs[config.name] = config
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": f"Конфигурация обновлена: {config.name}"
}))
self._logger.debug(f"Обновлена информация о конфигурации: {config.name}")
except Exception as e:
self._logger.error(f"Ошибка обновления информации о конфигурации: {e}")
def remove_config_info(self, config_name: str) -> None:
"""
Удаление информации о файле конфигурации.
Args:
config_name: Имя файла конфигурации
"""
if config_name in self._state.configs:
self._state.configs.pop(config_name)
# Если удаляется текущая конфигурация, очищаем её
if self._state.current_config == config_name:
self._state.current_config = None
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": f"Конфигурация удалена: {config_name}"
}))
self._logger.debug(f"Удалена информация о конфигурации: {config_name}")
def set_current_config(self, config_name: Optional[str]) -> None:
"""
Установка текущей конфигурации.
Args:
config_name: Имя файла конфигурации
"""
if config_name is None or config_name in self._state.configs:
self._state.current_config = config_name
if config_name:
self._logger.debug(f"Установлена текущая конфигурация: {config_name}")
else:
self._logger.debug("Текущая конфигурация очищена")
def update_transfer_state(self, state: TransferState, progress: float = 0.0,
error: Optional[str] = None) -> None:
"""
Обновление состояния передачи.
Args:
state: Новое состояние
progress: Прогресс передачи
error: Сообщение об ошибке
"""
self._state.transfer_state = state
self._state.transfer_progress = progress
self._state.error_message = error
# Формируем сообщение о состоянии
if state == TransferState.IDLE:
message = "Готов к передаче"
elif state == TransferState.PREPARING:
message = "Подготовка к передаче..."
elif state == TransferState.TRANSFERRING:
message = f"Передача данных: {progress:.1f}%"
elif state == TransferState.COMPLETED:
message = "Передача завершена"
elif state == TransferState.ERROR:
message = f"Ошибка передачи: {error}"
else:
message = "Неизвестное состояние"
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": message
}))
self._logger.debug(f"Состояние передачи обновлено: {state.name}")
def set_tftp_server_state(self, is_running: bool) -> None:
"""
Установка состояния TFTP сервера.
Args:
is_running: Флаг работы сервера
"""
self._state.is_tftp_server_running = is_running
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": "TFTP сервер запущен" if is_running else "TFTP сервер остановлен"
}))
self._logger.debug(f"Состояние TFTP сервера: {'запущен' if is_running else 'остановлен'}")
def set_status_message(self, message: str) -> None:
"""
Установка сообщения о состоянии.
Args:
message: Сообщение
"""
self._state.status_message = message
# Уведомляем об изменении состояния
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": message
}))
def set_error_message(self, error: Optional[str]) -> None:
"""
Установка сообщения об ошибке.
Args:
error: Сообщение об ошибке
"""
self._state.error_message = error
if error:
# Уведомляем об ошибке
event_bus.publish(Event(EventTypes.UI_STATUS_CHANGED, {
"message": f"Ошибка: {error}"
}))
def get_device_info(self) -> DeviceInfo:
"""
Получение информации об устройстве.
Returns:
DeviceInfo: Информация об устройстве
"""
return self._state.device_info
def get_config_info(self, config_name: str) -> Optional[ConfigInfo]:
"""
Получение информации о конфигурации.
Args:
config_name: Имя файла конфигурации
Returns:
Optional[ConfigInfo]: Информация о конфигурации или None
"""
return self._state.configs.get(config_name)
def get_configs(self) -> Dict[str, ConfigInfo]:
"""
Получение списка всех конфигураций.
Returns:
Dict[str, ConfigInfo]: Словарь с информацией о конфигурациях
"""
return self._state.configs.copy()
def get_current_config(self) -> Optional[str]:
"""
Получение текущей конфигурации.
Returns:
Optional[str]: Имя текущей конфигурации или None
"""
return self._state.current_config
@property
def state(self) -> ApplicationState:
"""Получение текущего состояния."""
return self._state
def set_custom_data(self, key: str, value: Any) -> None:
"""Установка пользовательских данных."""
self._state.custom_data[key] = value
def get_custom_data(self, key: str, default: Any = None) -> Any:
"""Получение пользовательских данных."""
return self._state.custom_data.get(key, default)
# Создаем глобальный экземпляр менеджера состояния
state_manager = StateManager()