from pathlib import Path from typing import List from biopro.device.manager import DeviceManager, DeviceManagerOptions, DeviceHardwareInterface from biopro.devlib.device import DeviceInfo, DeviceResponseInfo from biopro.file.manager import FileManager, FileOptions from biopro.file.user.setting import AbstractUserSetting, DummyUserSetting from biopro.util.address import EMPTY_ADDRESS from biopro.util.cli_base import CliOptions from biopro.util.console import RED from biopro.util.json import JSON_OBJECT from biopro.util.logger import logging_info from .controller import ControlServerOptions, ControlServerAPI, HardwareInfo, to_device_info from .socket import * _RUNTIME_COMPILE = False class DebugControllerOptions(CliOptions): def __init__(self): self.user_validation = True self.allow_task_thread = False self.task_thread_pool_size = 4 self.allow_routine_thread = False if _RUNTIME_COMPILE: DebugControllerServer = None else: # noinspection PyAbstractClass class DebugControllerServer(LoggerFlag, ControlServerAPI): def __init__(self, *options: CliOptions): super().__init__('Debug', RED) self.log_verbose('init') self._debug_options = o = DebugControllerOptions.get_options(*options) # controller options self._controller_options = c = ControlServerOptions.get_options(*options) # file manager f = FileOptions.with_default_structure(Path(c.storage_root).absolute()) f.debug_account_system = True self._file_manager = FileManager(f) # device manager d = DeviceManagerOptions.get_options(*options) if d is None: d = DeviceManagerOptions() d.interface = DeviceHardwareInterface.INTERFACE_NULL self._device_manager = DeviceManager(d, self) # setup self.log_verbose('setup file manager') self._file_manager.setup_storage() self.log_verbose('setup device manager') self._device_manager.setup() # setup if o.allow_task_thread: self.log_verbose('setup task thread pool') self._executor = ThreadPoolExecutor(max_workers=o.task_thread_pool_size) if o.allow_routine_thread: self.log_verbose('setup routine thread manager') self._routine: List[ServerThread] = [] self.log_verbose('done') @property def options(self) -> ControlServerOptions: return self._controller_options @property def file_manager(self) -> FileManager: return self._file_manager @property def device_manager(self) -> DeviceManager: return self._device_manager def hardware_info(self) -> HardwareInfo: return HardwareInfo("debug") def hardware_device_found(self, content: Union[str, JSON_OBJECT, DeviceInfo], *, scan: Union[str, bool] = False) -> Optional[DeviceResponseInfo]: if isinstance(content, DeviceInfo): response = content else: response = to_device_info(content) ret = super().hardware_device_found(response, scan=scan) if ret is not None: # if found, just return return ret if not scan: # if not scan mode, return result return None if response.mac_address == EMPTY_ADDRESS: # create demo device return self.device_manager.create_demo_device(response.device_name) return None def user_info(self, user: str, *, user_session: Optional[str] = None) -> Optional[AbstractUserSetting]: if self._debug_options.user_validation: return super().user_info(user, user_session=user_session) elif user in self.user_all(): self.log_verbose('use user', user) return super().user_info(user, user_session=user_session) else: self.log_verbose('use dummy user', ':', user) ret = DummyUserSetting(user) self.file_manager.user_setting._user_add(ret) return ret @logging_info def submit_thread(self, f, *args): if self._debug_options.allow_task_thread: self._executor.submit(f, *args) else: raise RuntimeError('cannot submit task thread') @logging_info def run_thread(self, thread: T_ServerThread) -> T_ServerThread: if self._debug_options.allow_routine_thread: thread.log_verbose('start') Thread(target=self._run_thread, name=thread.log_name, args=(thread,), daemon=True).start() return thread else: raise RuntimeError('cannot run routine thread') def _run_thread(self, thread: ServerThread): try: self._routine.append(thread) thread.log_verbose('setup') thread.setup() if not thread.is_closed: thread.log_verbose('routine') while not thread.is_closed: thread.routine() except ServerShutdownException as e: self.interrupt(e) except ServerThreadClosedInterrupt: pass except: print_exception(self) finally: try: thread.log_verbose('closing') thread.shutdown() except KeyboardInterrupt: thread.log_warn('closing interrupt') finally: try: self._routine.remove(thread) except ValueError: pass thread.log_verbose('closed')