This commit is contained in:
parent
43f80a79c1
commit
769ee0aeca
@ -0,0 +1,6 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
# Config is loaded statically at import time
|
||||||
|
class Config:
|
||||||
|
pass
|
@ -1,5 +1,34 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from config import Config
|
||||||
|
from plugins import WaitPlugin
|
||||||
|
from plugin_repository import PluginRepository
|
||||||
|
from program_executor import ProgramExecutor
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Hello world!")
|
# init
|
||||||
|
logging.basicConfig(
|
||||||
|
stream=sys.stdout,
|
||||||
|
format="%(asctime)s [%(levelname)s]: %(name)s: %(message)s",
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
|
compiler_repo = PluginRepository()
|
||||||
|
compiler_repo.register_plugin(WaitPlugin)
|
||||||
|
|
||||||
|
# Example code:
|
||||||
|
compiler_repo.load_plugin("wait")
|
||||||
|
program = []
|
||||||
|
program.append(compiler_repo.get_compiler("wait").compile(secs=2))
|
||||||
|
|
||||||
|
# execute:
|
||||||
|
executor = ProgramExecutor(program)
|
||||||
|
executor.start()
|
||||||
|
|
||||||
|
# End of execution
|
||||||
|
executor.join()
|
||||||
|
compiler_repo.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
35
single_ursim_control/plugin_repository.py
Normal file
35
single_ursim_control/plugin_repository.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from typing import Dict, List
|
||||||
|
from plugins import AbstractPlugin, AbstractCommandCompiler
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class PluginRepository:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._registered_plugins: Dict[str, type(AbstractPlugin)] = {}
|
||||||
|
self._loaded_plugins: List[AbstractPlugin] = []
|
||||||
|
self._command_compilers: Dict[str, AbstractCommandCompiler] = {}
|
||||||
|
self._logger = logging.getLogger("plugin_repository")
|
||||||
|
|
||||||
|
def register_plugin(self, plugin_class: type(AbstractPlugin)):
|
||||||
|
self._registered_plugins[plugin_class.plugin_name] = plugin_class
|
||||||
|
self._logger.debug(f"Registered plugin: {plugin_class.plugin_name}")
|
||||||
|
|
||||||
|
def load_plugin(self, plugin: str):
|
||||||
|
plugin_instance = self._registered_plugins[plugin]() # config is statically loaded
|
||||||
|
|
||||||
|
self._loaded_plugins.append(plugin_instance)
|
||||||
|
|
||||||
|
compilers = plugin_instance.load_compilers()
|
||||||
|
self._command_compilers.update(compilers)
|
||||||
|
self._logger.info(f"Loaded plugin: {plugin}")
|
||||||
|
self._logger.debug(f"Plugin {plugin} loaded the following commands: {', '.join(compilers.keys())}")
|
||||||
|
|
||||||
|
def get_compiler(self, command: str) -> AbstractCommandCompiler:
|
||||||
|
return self._command_compilers[command]
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._command_compilers = []
|
||||||
|
for plugin in self._loaded_plugins:
|
||||||
|
plugin.close()
|
||||||
|
self._logger.info(f"Unloaded plugin: {plugin.plugin_name}")
|
2
single_ursim_control/plugins/__init__.py
Normal file
2
single_ursim_control/plugins/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .abstract_plugin import AbstractCommand, AbstractCommandCompiler, AbstractPlugin
|
||||||
|
from .wait_plugin import WaitPlugin
|
32
single_ursim_control/plugins/abstract_plugin.py
Normal file
32
single_ursim_control/plugins/abstract_plugin.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from typing import Dict
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractCommand(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def describe(self) -> dict:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractCommandCompiler(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def compile(self, *args, **kwargs) -> AbstractCommand:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractPlugin(ABC):
|
||||||
|
plugin_name = ""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def load_compilers(self) -> Dict[str, AbstractCommandCompiler]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self):
|
||||||
|
pass
|
53
single_ursim_control/plugins/wait_plugin.py
Normal file
53
single_ursim_control/plugins/wait_plugin.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from .abstract_plugin import AbstractCommand, AbstractPlugin, AbstractCommandCompiler
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class WaitCommand(AbstractCommand):
|
||||||
|
|
||||||
|
def __init__(self, logger: logging.Logger, secs: float):
|
||||||
|
|
||||||
|
if type(secs) not in [float, int]:
|
||||||
|
raise ValueError("Secs must be float or int")
|
||||||
|
|
||||||
|
if secs <= 0:
|
||||||
|
raise ValueError("Secs must be a positive integer")
|
||||||
|
|
||||||
|
self._secs = secs
|
||||||
|
self._logger = logger
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self._logger.debug(f"Sleeping for {self._secs} seconds")
|
||||||
|
time.sleep(self._secs)
|
||||||
|
self._logger.debug(f"Slept for {self._secs} seconds")
|
||||||
|
|
||||||
|
def describe(self) -> dict:
|
||||||
|
return {
|
||||||
|
"secs": self._secs
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WaitCompiler(AbstractCommandCompiler):
|
||||||
|
|
||||||
|
def __init__(self, logger: logging.Logger):
|
||||||
|
self._logger = logger
|
||||||
|
|
||||||
|
def compile(self, secs: float) -> AbstractCommand:
|
||||||
|
return WaitCommand(self._logger, secs)
|
||||||
|
|
||||||
|
|
||||||
|
class WaitPlugin(AbstractPlugin):
|
||||||
|
plugin_name = "wait"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._logger = logging.getLogger("plugin").getChild("wait")
|
||||||
|
|
||||||
|
def load_compilers(self) -> Dict[str, AbstractCommandCompiler]:
|
||||||
|
return {
|
||||||
|
"wait": WaitCompiler(self._logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
86
single_ursim_control/program_executor.py
Normal file
86
single_ursim_control/program_executor.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
from typing import List
|
||||||
|
from threading import Thread
|
||||||
|
from plugins import AbstractCommand
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class ProgramExecutorStates(Enum):
|
||||||
|
PREPARING = 0
|
||||||
|
RUNNING = 1
|
||||||
|
DONE = 2
|
||||||
|
ABORTED = 3
|
||||||
|
CRASHED = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ProgramExecutor(Thread):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
program: List[AbstractCommand],
|
||||||
|
loop: bool = False
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
self._program = program
|
||||||
|
self._loop = loop
|
||||||
|
self._current_step_desc = {}
|
||||||
|
self._pc = 0
|
||||||
|
self._loop_counter = 0
|
||||||
|
# TODO: jogging wait
|
||||||
|
|
||||||
|
self._state = ProgramExecutorStates.PREPARING
|
||||||
|
|
||||||
|
self._logger = logging.getLogger("program_executor")
|
||||||
|
|
||||||
|
def abort(self):
|
||||||
|
self._state = ProgramExecutorStates.ABORTED
|
||||||
|
# TODO: implement abort
|
||||||
|
|
||||||
|
def cont(self):
|
||||||
|
# TODO: jogging wait
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_status(self) -> dict:
|
||||||
|
return {
|
||||||
|
"current_step": self._pc,
|
||||||
|
"program_length": len(self._program),
|
||||||
|
"step_description": self._current_step_desc,
|
||||||
|
"state": self._state.name,
|
||||||
|
"loop_count": self._loop_counter,
|
||||||
|
"config": {
|
||||||
|
"loop": self._loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
self._state = ProgramExecutorStates.RUNNING
|
||||||
|
self._logger.info("Start running program")
|
||||||
|
|
||||||
|
while True: # needed for loop
|
||||||
|
self._loop_counter += 1
|
||||||
|
for i, step in enumerate(self._program):
|
||||||
|
self._pc = i
|
||||||
|
self._current_step_desc = step.describe()
|
||||||
|
self._logger.debug(f"Executing step {self._pc}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
step.execute()
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.exception(e)
|
||||||
|
self._state = ProgramExecutorStates.CRASHED
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: jogging wait
|
||||||
|
|
||||||
|
if self._loop:
|
||||||
|
self._logger.debug("Looping program")
|
||||||
|
else:
|
||||||
|
self._logger.debug("Program ended")
|
||||||
|
break
|
||||||
|
|
||||||
|
self._state = ProgramExecutorStates.DONE
|
Loading…
Reference in New Issue
Block a user