from ..variables.loader import Datavars from ..commands.commands import Command from ..logging import dictLogConfig from logging.config import dictConfig from logging import getLogger from typing import Callable from fastapi import FastAPI from .worker import Worker import importlib import uvicorn import asyncio import os # TODO # 1. Разобраться с описанием команд как ресурсов и со всем, что от них зависит. # 2. Разобраться с объектами воркеров. И способом их функционирования. class Server: def __init__(self, socket_path='./input.sock', datavars_path: str = 'calculate/vars/', commands_path: str = 'calculate/commands'): self._app = FastAPI() self._socket_path = socket_path self._event_loop = asyncio.get_event_loop() # Конфигурируем логгирование. dictConfig(dictLogConfig) self._logger = getLogger("main") self.log_msg = {'DEBUG': self._logger.debug, 'INFO': self._logger.info, 'WARNING': self._logger.warning, 'ERROR': self._logger.error, 'CRITICAL': self._logger.critical} self._datavars = Datavars(variables_path=datavars_path, logger=self._logger) # Словарь описаний команд. self._commands = self._get_commands_list(commands_path) # Словарь CID и экземпляров команд, передаваемых воркерам. self._commands_instances = {} # Словарь WID и экземпляров процессов-воркеров, передаваемых воркерам. self._workers = {} # Соответствие путей обработчикам запросов для HTTP-метода GET. self._add_routes(self._app.get, {"/": self._get_root, "/commands": self._get_commands, "/commands/{cid}": self._get_command, "/workers/{wid}": self._get_worker}) self._add_routes(self._app.post, {"/commands/{command_id}": self._post_command}) def _get_commands_list(self, commands_path: str) -> list: '''Метод для получения совокупности описаний команд.''' output = {} package = ".".join(commands_path.split("/")) for entry in os.scandir(commands_path): if (not entry.name.endswith('.py') or entry.name in {"commands.py", "__init__.py"}): continue module_name = entry.name[:-3] try: module = importlib.import_module("{}.{}".format(package, module_name)) for obj in dir(module): if type(module.__getattribute__(obj)) == Command: command_object = module.__getattribute__(obj) output[command_object.id] = command_object except Exception: continue return output async def _get_root(self) -> dict: '''Обработчик корневых запросов.''' return {'msg': 'root msg'} async def _get_commands(self) -> dict: '''Обработчик, отвечающий на запросы списка команд.''' response = {} for command_id, command_object in self._commands.items(): response.update({command_id: {"title": command_object.title, "category": command_object.category, "icon": command_object.icon, "command": command_object.command}}) return response async def _get_command(self, cid: int) -> dict: '''Обработчик запросов списка команд.''' if cid not in self._commands_instances: # TODO добавить какую-то обработку ошибки. pass return {'id': cid, 'name': f'command_{cid}'} async def _get_worker(self, wid: int): '''Тестовый ''' self._make_worker(wid=wid) worker = self._workers[wid] worker.run(None) await worker.send({"text": "INFO"}) data = await worker.get() if data['type'] == 'log': self.log_msg[data['level']](data['msg']) return data async def _post_command(self, command_id: str) -> int: if command_id not in self._commands: # TODO добавить какую-то обработку ошибки. pass return def _add_routes(self, method: Callable, routes: dict) -> None: '''Метод для добавления методов.''' for path, handler in routes.items(): router = method(path) router(handler) def _make_worker(self, wid: int = None): '''Метод для создания воркера для команды.''' worker = Worker(self._event_loop) if wid is None: self._workers[wid] = worker return wid elif not self._workers: self._workers[0] = worker return 0 else: wid = max(self._workers.keys()) + 1 self._workers[wid] = worker return wid def _make_command(self, command_id: str) -> int: '''Метод для создания команды по ее описанию.''' command_description = self._commands[command] @property def app(self): return self._app def run(self): '''Метод для запуска сервера.''' # Выгружаем список команд. uvicorn.run(self._app, uds=self._socket_path) if __name__ == '__main__': server = Server() server.run()