191 lines
6.0 KiB
Python
191 lines
6.0 KiB
Python
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')
|