Implemented main basically
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Pünkösd Marcell 2021-04-14 17:41:48 +02:00
parent f8bd18c307
commit 97986d78e5
8 changed files with 152 additions and 44 deletions

View File

@ -1,3 +1,5 @@
ur-rtde~=1.4.1
redis~=3.5.3
pyprocsync>=0.1.0
pyprocsync>=0.1.0
requests
marshmallow~=3.11.1

View File

@ -0,0 +1,17 @@
from plugins import AbstractCommand
from typing import List
from plugin_repository import PluginRepository
import logging
def compile_program(plugin_repository: PluginRepository, program_source: List[dict]) -> List[AbstractCommand]:
logger = logging.getLogger('compiler')
compiled_program = []
for command_source in program_source:
logger.debug(f"Compiling: [{command_source['name']}],{command_source['args']}")
compiled_program.append(
plugin_repository.get_compiler(command_source['name']).compile(**command_source['args'])
)
return compiled_program

View File

@ -1,3 +1,4 @@
import sys
import os
@ -7,4 +8,7 @@ class Config:
SYNC_DELAY = float(os.environ.get("SYNC_DELAY", 1.0))
REDIS_URL = os.environ["REDIS_URL"]
SYNC_TIMEOUT = os.environ.get("SYNC_TIMEOUT", None) # Wait infinity by default
ROBOT_ADDRESS = os.environ.get("ROBOT_ADDRESS")
ROBOT_ADDRESS = os.environ.get("ROBOT_ADDRESS")
PROGRAM_URL = os.environ["PROGRAM_URL"]
DRY_RUN = ('--dry-run' in sys.argv) or bool(os.environ.get("DRY_RUN", False))
DEBUG = ('--debug' in sys.argv) or bool(os.environ.get("DEBUG", False))

View File

@ -1,14 +1,16 @@
#!/usr/bin/env python3
import os
import sys
from config import Config
from plugins import SleepPlugin, SyncPlugin, WaitPlugin, URRTDEPlugin
from plugin_repository import PluginRepository
from program_executor import ProgramExecutor
from program_executor import ProgramExecutor, ProgramExecutorStates
from http_server import ControllerHTTPServer
import logging
import signal
from compiler import compile_program
from program_loader import load_program
class HTTPControl:
@ -22,43 +24,54 @@ class HTTPControl:
return 201, "Will do sir!"
def main():
# init
def main() -> int:
# init instance
logging.basicConfig(
stream=sys.stdout,
format="%(asctime)s [%(levelname)s]: %(name)s: %(message)s",
level=logging.DEBUG
level=logging.DEBUG if Config.DEBUG else logging.INFO
)
http_server = ControllerHTTPServer(HTTPControl())
http_server.start()
compiler_repo = PluginRepository()
compiler_repo.register_plugin(SleepPlugin)
compiler_repo.register_plugin(SyncPlugin)
compiler_repo.register_plugin(WaitPlugin)
compiler_repo.register_plugin(URRTDEPlugin)
logging.info("Registering available plugins...")
# Register all available plugins
plugin_repo = PluginRepository()
plugin_repo.register_plugin(SleepPlugin)
plugin_repo.register_plugin(SyncPlugin)
plugin_repo.register_plugin(WaitPlugin)
plugin_repo.register_plugin(URRTDEPlugin)
# Example code:
compiler_repo.load_plugin("sleep")
compiler_repo.load_plugin("sync")
compiler_repo.load_plugin("wait")
compiler_repo.load_plugin("ur_rtde")
program = []
program.append(compiler_repo.get_compiler("sleep").compile(secs=2))
program.append(compiler_repo.get_compiler("moveJ").compile([5.7386425805573555, -0.536165146212658, 1.6278685933351111, -2.661452576366153, -1.5683528658421044, 1.0096729722787197], 1.0, 4.0))
program.append(compiler_repo.get_compiler("moveL").compile([-0.4, 0.1, -0.31, 3.142, 0, 0], 0.05, 0.75))
program.append(compiler_repo.get_compiler("moveL").compile([-0.4, 0.1, -0.24, 3.142, 0, 0], 0.05, 0.75))
program.append(compiler_repo.get_compiler("moveJ").compile([5.923472948343555, 0.032637657012293965, 0.2590068609959585, -0.2935643801854462, -2.7157323161031766, 4.71238898038469], 1.0, 4.0))
program.append(compiler_repo.get_compiler("moveJ").compile([4.982042349817814, -0.5256931707006921, 1.620887276327134, -1.0993828958312282, -3.660653573132907, 5.271592472723674], 1.0, 4.0))
program.append(compiler_repo.get_compiler("wait").compile())
program.append(compiler_repo.get_compiler("sleep").compile(secs=3))
program.append(compiler_repo.get_compiler("sync").compile(nodes=2, name="test"))
program.append(compiler_repo.get_compiler("sleep").compile(secs=10))
program.append(compiler_repo.get_compiler("sleep").compile(secs=10))
# Download the program
logging.info("Downloading program...")
try:
program_source = load_program(Config.PROGRAM_URL)
except Exception as e:
logging.error(f"Failed to download program: {e}! Exiting...")
logging.exception(e)
return 1
# prepare execution
executor = ProgramExecutor(program, loop=True)
# Load required plugins
logging.info("Loading required plugins...")
try:
plugin_repo.load_plugins(program_source['load_plugins'])
except Exception as e:
logging.error(f"Error during plugin loading: {e}! Exiting...")
logging.exception(e)
return 2
# Compile the program
logging.info("Compiling program...")
try:
program = compile_program(plugin_repo, program_source['program'])
except Exception as e:
logging.error(f"Error during compilation: {e}! Exiting...")
logging.exception(e)
return 3
# prepare the executor
logging.info("Preparing for execution...")
executor = ProgramExecutor(program, loop=False)
# Setup signal handler
def handle_stop_signal(signum, frame):
logging.warning(f"Signal {signum} received. Aborting execution!")
executor.abort()
@ -70,14 +83,28 @@ def main():
signal.signal(signal.SIGINT, handle_stop_signal)
signal.signal(signal.SIGTERM, handle_stop_signal)
# start execution
executor.start()
# Actually execute
execution_success = True
if Config.DRY_RUN:
logging.info("DRY_RUN enabled. Dumping command descriptions and exiting!")
for i, command in enumerate(program):
logging.info(f"{i:04d}: {command.describe()}")
else:
logging.info("Starting execution...")
executor.start()
executor.join()
# End of execution
executor.join()
compiler_repo.close()
http_server.shutdown()
if executor.state == ProgramExecutorStates.DONE:
logging.info("Program executed successfully!")
else:
logging.error(f"Could not finish execution! Executor state: {executor.state.name}")
execution_success = False
# Close all resources
logging.info("Cleaning up...")
plugin_repo.close()
return 0 if execution_success else 5
if __name__ == '__main__':
main()
sys.exit(main())

View File

@ -3,7 +3,11 @@ from plugins import AbstractPlugin, AbstractCommandCompiler
import logging
class ConflictingPlugins(BaseException):
class ConflictingPlugins(Exception):
pass
class UnknownPlugin(Exception):
pass
@ -19,14 +23,25 @@ class PluginRepository:
self._registered_plugins[plugin_class.plugin_name] = plugin_class
self._logger.debug(f"Registered plugin: {plugin_class.plugin_name}")
def load_plugins(self, plugins: List[str]):
for plugin in plugins:
self.load_plugin(plugin)
def load_plugin(self, plugin: str):
self._logger.debug(f"Loading plugin: {plugin}")
if plugin in self._loaded_plugins.keys():
self._logger.warning(f"Plugin {plugin} already loaded!")
return
# create instance
plugin_instance = self._registered_plugins[plugin]() # config is statically loaded
# lookup plugin class
try:
plugin_cls = self._registered_plugins[plugin]
except KeyError:
raise UnknownPlugin(f"Tried to load unknown plugin: {plugin}")
# Create instance
plugin_instance = plugin_cls() # config is statically loaded
# load compilers
compilers = plugin_instance.load_compilers()

View File

@ -50,8 +50,14 @@ class ProgramExecutor(Thread):
@property
def state(self):
# Used to check the successfulness of the run as well
return self._state
def stop_looping(self):
if self._loop:
self._logger.info("Looping disabled! Finishing current loop then exiting...")
self._loop = False
def run(self) -> None:
self._state = ProgramExecutorStates.RUNNING
self._logger.info("Start running program")
@ -61,7 +67,7 @@ class ProgramExecutor(Thread):
for i, step in enumerate(self._program):
self._pc = i
self._current_step_desc = step.describe()
self._logger.debug(f"Executing step {self._pc}")
self._logger.debug(f"Executing step {self._pc:04d}")
try:
step.execute()

View File

@ -0,0 +1,14 @@
from program_schema import ProgramSchema, SUPPORTED_PROGRAM_STRUCTURE_VERSION
import requests
def load_program(url: str) -> dict:
headers = {
'Accept': 'application/json'
}
r = requests.get(url, headers=headers)
r.raise_for_status()
program_schema = ProgramSchema(many=False)
return program_schema.load(r.json()) # Might raise marshmallow exceptions

View File

@ -0,0 +1,23 @@
from marshmallow import Schema, fields
from marshmallow.validate import Length, Equal
from marshmallow import RAISE
SUPPORTED_PROGRAM_STRUCTURE_VERSION = 1
class CommandSchema(Schema):
name = fields.Str(required=True)
args = fields.Dict(required=True)
class Meta:
unknown = RAISE
class ProgramSchema(Schema):
version = fields.Int(required=True, validate=Equal(SUPPORTED_PROGRAM_STRUCTURE_VERSION))
name = fields.Str()
load_plugins = fields.List(fields.Str(), required=True)
program = fields.Nested(CommandSchema, many=True, required=True, validate=Length(min=1))
class Meta:
unknown = RAISE