The application has been refactored. Functions have been moved to separate libraries. Many different functions have been added. Performance is still poor

This commit is contained in:
2025-02-16 21:57:01 +03:00
parent a140b7d8a0
commit cb5329ddb7
46 changed files with 5316 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import re
from typing import Optional, List
import serial
from serial.serialutil import SerialException
from core.config import AppConfig
from core.exceptions import ConnectionError, AuthenticationError
from .base import BaseProtocol
class SerialProtocol(BaseProtocol):
"""Протокол для работы с последовательным портом."""
def __init__(self):
super().__init__()
self._serial: Optional[serial.Serial] = None
self._prompt = AppConfig.DEFAULT_PROMPT
self._timeout = AppConfig.DEFAULT_TIMEOUT
self._baudrate = AppConfig.DEFAULT_BAUDRATE
self._port: Optional[str] = None
def configure(self, port: str, baudrate: int = None, timeout: int = None,
prompt: str = None) -> None:
"""
Конфигурация последовательного порта.
Args:
port: COM-порт
baudrate: Скорость передачи
timeout: Таймаут операций
prompt: Приглашение командной строки
"""
self._port = port
if baudrate is not None:
self._baudrate = baudrate
if timeout is not None:
self._timeout = timeout
if prompt is not None:
self._prompt = prompt
def connect(self) -> None:
"""
Установка соединения с устройством.
Raises:
ConnectionError: При ошибке подключения
"""
if not self._port:
raise ConnectionError("Не указан COM-порт")
try:
self._serial = serial.Serial(
port=self._port,
baudrate=self._baudrate,
timeout=1
)
time.sleep(1) # Ждем инициализации порта
self._notify_connection_established()
except SerialException as e:
self._notify_connection_error(e)
raise ConnectionError(f"Ошибка подключения к порту {self._port}: {e}")
def disconnect(self) -> None:
"""Закрытие соединения."""
if self._serial and self._serial.is_open:
self._serial.close()
self._notify_connection_lost()
def authenticate(self, username: str, password: str) -> None:
"""
Аутентификация на устройстве.
Args:
username: Имя пользователя
password: Пароль
Raises:
AuthenticationError: При ошибке аутентификации
ConnectionError: При ошибке соединения
"""
if not self.is_connected:
raise ConnectionError("Нет подключения к устройству")
try:
# Очищаем буфер
self._serial.reset_input_buffer()
self._serial.reset_output_buffer()
# Отправляем Enter для получения приглашения
self._serial.write(b"\n")
time.sleep(0.5)
# Ожидаем запрос логина или пароля
response = self._read_until(["login:", "username:", "password:", self._prompt])
if "login:" in response.lower() or "username:" in response.lower():
self._serial.write(f"{username}\n".encode())
time.sleep(0.5)
response = self._read_until(["password:", self._prompt])
if "password:" in response.lower():
self._serial.write(f"{password}\n".encode())
time.sleep(0.5)
response = self._read_until([self._prompt])
if self._prompt not in response:
raise AuthenticationError("Неверные учетные данные")
self._authenticated = True
except SerialException as e:
self._notify_connection_error(e)
raise ConnectionError(f"Ошибка при аутентификации: {e}")
def send_command(self, command: str, timeout: Optional[int] = None) -> str:
"""
Отправка команды на устройство.
Args:
command: Команда для отправки
timeout: Таймаут ожидания ответа
Returns:
str: Ответ устройства
Raises:
ConnectionError: При ошибке отправки команды
"""
if not self.is_connected:
raise ConnectionError("Нет подключения к устройству")
if not self.is_authenticated:
raise ConnectionError("Требуется аутентификация")
try:
# Очищаем буфер перед отправкой
self._serial.reset_input_buffer()
# Отправляем команду
self._serial.write(f"{command}\n".encode())
# Ожидаем ответ
response = self._read_until([self._prompt], timeout or self._timeout)
# Удаляем отправленную команду из ответа
lines = response.splitlines()
if lines and command in lines[0]:
lines.pop(0)
return "\n".join(lines).strip()
except SerialException as e:
self._notify_connection_error(e)
raise ConnectionError(f"Ошибка при отправке команды: {e}")
def send_config(self, config_commands: List[str], timeout: Optional[int] = None) -> str:
"""
Отправка конфигурационных команд на устройство.
Args:
config_commands: Список команд конфигурации
timeout: Таймаут ожидания ответа
Returns:
str: Ответ устройства
Raises:
ConnectionError: При ошибке отправки команд
"""
responses = []
for command in config_commands:
response = self.send_command(command, timeout)
responses.append(response)
return "\n".join(responses)
def _read_until(self, patterns: List[str], timeout: Optional[int] = None) -> str:
"""
Чтение данных из порта до появления одного из паттернов.
Args:
patterns: Список паттернов для поиска
timeout: Таймаут операции
Returns:
str: Прочитанные данные
Raises:
ConnectionError: При ошибке чтения или таймауте
"""
if not patterns:
raise ValueError("Не указаны паттерны для поиска")
timeout = timeout or self._timeout
end_time = time.time() + timeout
buffer = ""
while time.time() < end_time:
if self._serial.in_waiting:
chunk = self._serial.read(self._serial.in_waiting).decode(errors="ignore")
buffer += chunk
# Проверяем наличие паттернов
for pattern in patterns:
if pattern in buffer:
return buffer
# Небольшая задержка для снижения нагрузки на CPU
time.sleep(0.1)
raise ConnectionError(f"Таймаут операции ({timeout} сек)")
@staticmethod
def list_ports() -> List[str]:
"""
Получение списка доступных COM-портов.
Returns:
List[str]: Список доступных портов
"""
return [port.device for port in serial.tools.list_ports.comports()]