From 297d36dea53202d218cd2e1087d199101f8e9dce Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 23 Jun 2025 16:46:49 +0200 Subject: [PATCH 01/15] Adding neutral tools --- manager/manager/launcher/launcher_console.py | 10 + ...cher_gazebo_view.py => launcher_gazebo.py} | 23 +- ...uncher_gzsim_view.py => launcher_gzsim.py} | 73 ++++- .../launcher/launcher_state_monitor.py | 52 ++++ .../launcher/launcher_visualization.py | 250 +++++------------- manager/manager/launcher/launcher_web_gui.py | 50 ++++ manager/manager/manager.py | 180 +++---------- 7 files changed, 317 insertions(+), 321 deletions(-) rename manager/manager/launcher/{launcher_gazebo_view.py => launcher_gazebo.py} (77%) rename manager/manager/launcher/{launcher_gzsim_view.py => launcher_gzsim.py} (55%) create mode 100644 manager/manager/launcher/launcher_state_monitor.py create mode 100644 manager/manager/launcher/launcher_web_gui.py diff --git a/manager/manager/launcher/launcher_console.py b/manager/manager/launcher/launcher_console.py index 87a0b15..6f39ff3 100644 --- a/manager/manager/launcher/launcher_console.py +++ b/manager/manager/launcher/launcher_console.py @@ -12,6 +12,7 @@ class LauncherConsole(ILauncher): internal_port: int external_port: int running: bool = False + acceptsMsgs: bool = False threads: List[Any] = [] console_vnc: Any = Vnc_server() @@ -38,6 +39,15 @@ def run(self, config_file, callback): self.running = True + def pause(self): + pass + + def unpause(self): + pass + + def reset(self): + pass + def is_running(self): return self.running diff --git a/manager/manager/launcher/launcher_gazebo_view.py b/manager/manager/launcher/launcher_gazebo.py similarity index 77% rename from manager/manager/launcher/launcher_gazebo_view.py rename to manager/manager/launcher/launcher_gazebo.py index 54ed40e..3811559 100644 --- a/manager/manager/launcher/launcher_gazebo_view.py +++ b/manager/manager/launcher/launcher_gazebo.py @@ -1,3 +1,4 @@ +import sys from manager.manager.launcher.launcher_interface import ILauncher from manager.manager.docker_thread.docker_thread import DockerThread from manager.manager.vnc.vnc_server import Vnc_server @@ -11,14 +12,25 @@ import stat from typing import List, Any +def call_service(self, service, service_type, request_data="{}"): + command = f"ros2 service call {service} {service_type} '{request_data}'" + subprocess.call( + f"{command}", + shell=True, + stdout=sys.stdout, + stderr=subprocess.STDOUT, + bufsize=1024, + universal_newlines=True, + ) -class LauncherGazeboView(ILauncher): +class LauncherGazebo(ILauncher): display: str internal_port: int external_port: int height: int width: int running: bool = False + acceptsMsgs: bool = False threads: List[Any] = [] gz_vnc: Any = Vnc_server() @@ -51,6 +63,15 @@ def run(self, config_file, callback): self.running = True + def pause(self): + call_service("/pause_physics", "std_srvs/srv/Empty") + + def unpause(self): + call_service("/unpause_physics", "std_srvs/srv/Empty") + + def reset(self): + call_service("/reset_world", "std_srvs/srv/Empty") + def is_running(self): return self.running diff --git a/manager/manager/launcher/launcher_gzsim_view.py b/manager/manager/launcher/launcher_gzsim.py similarity index 55% rename from manager/manager/launcher/launcher_gzsim_view.py rename to manager/manager/launcher/launcher_gzsim.py index 59d1fb3..f01afbc 100644 --- a/manager/manager/launcher/launcher_gzsim_view.py +++ b/manager/manager/launcher/launcher_gzsim.py @@ -1,3 +1,4 @@ +import sys from manager.manager.launcher.launcher_interface import ILauncher from manager.manager.docker_thread.docker_thread import DockerThread from manager.manager.vnc.vnc_server import Vnc_server @@ -10,9 +11,44 @@ import os import stat from typing import List, Any +from manager.ram_logging.log_manager import LogManager +def call_gzservice(self, service, reqtype, reptype, timeout, req): + command = f"gz service -s {service} --reqtype {reqtype} --reptype {reptype} --timeout {timeout} --req '{req}'" + subprocess.call( + f"{command}", + shell=True, + stdout=sys.stdout, + stderr=subprocess.STDOUT, + bufsize=1024, + universal_newlines=True, + ) -class LauncherGzsimView(ILauncher): +def call_service(self, service, service_type, request_data="{}"): + command = f"ros2 service call {service} {service_type} '{request_data}'" + subprocess.call( + f"{command}", + shell=True, + stdout=sys.stdout, + stderr=subprocess.STDOUT, + bufsize=1024, + universal_newlines=True, + ) + +def is_ros_service_available(self, service_name): + try: + result = subprocess.run( + ["ros2", "service", "list", "--include-hidden-services"], + capture_output=True, + text=True, + check=True, + ) + return service_name in result.stdout + except subprocess.CalledProcessError as e: + LogManager.logger.exception(f"Error checking service availability: {e}") + return False + +class LauncherGzsim(ILauncher): display: str internal_port: int external_port: int @@ -73,6 +109,41 @@ def terminate(self): def died(self): pass + def pause(self): + call_gzservice( + "$(gz service -l | grep '^/world/\w*/control$')", + "gz.msgs.WorldControl", + "gz.msgs.Boolean", + "3000", + "pause: true", + ) + + def unpause(self): + call_gzservice( + "$(gz service -l | grep '^/world/\w*/control$')", + "gz.msgs.WorldControl", + "gz.msgs.Boolean", + "3000", + "pause: false", + ) + + def reset(self): + if is_ros_service_available("/drone0/platform/state_machine/_reset"): + call_service( + "/drone0/platform/state_machine/_reset", + "std_srvs/srv/Trigger", + "{}", + ) + call_gzservice( + "$(gz service -l | grep '^/world/\w*/control$')", + "gz.msgs.WorldControl", + "gz.msgs.Boolean", + "3000", + "reset: {all: true}", + ) + if is_ros_service_available("/drone0/controller/_reset"): + call_service("/drone0/controller/_reset", "std_srvs/srv/Trigger", "{}") + def get_dri_path(self): directory_path = "/dev/dri" dri_path = "" diff --git a/manager/manager/launcher/launcher_state_monitor.py b/manager/manager/launcher/launcher_state_monitor.py new file mode 100644 index 0000000..8e645b5 --- /dev/null +++ b/manager/manager/launcher/launcher_state_monitor.py @@ -0,0 +1,52 @@ +from manager.libs.applications.compatibility.server import Server +from manager.ram_logging.log_manager import LogManager +from manager.comms.new_consumer import ManagerConsumer +from typing import Optional +from manager.libs.applications.compatibility.file_watchdog import FileWatchdog + + +class LauncherStateMonitor: + file: str + consumer: ManagerConsumer + running: bool = False + acceptsMsgs: bool = True + + def __init__(self, type, module, file, consumer): + self.file = file + self.consumer = consumer + self.server = FileWatchdog("/tmp/tree_state", self.update) + + def update(self, data): + LogManager.logger.debug(f"Sending update to client") + if self.consumer is not None: + self.consumer.send_message({"update": data}, command="update") + + def run(self, config_file, callback): + self.server.start() + self.running = True + + def get_msg(self, data): + self.server.send(data) + + def is_running(self): + return self.running + + def terminate(self): + self.server.stop() + self.running = False + + def pause(self): + pass + + def unpause(self): + pass + + def reset(self): + pass + + def died(self): + pass + + def from_config(cls, config): + obj = cls(**config) + return obj diff --git a/manager/manager/launcher/launcher_visualization.py b/manager/manager/launcher/launcher_visualization.py index 369cac5..8bec29d 100644 --- a/manager/manager/launcher/launcher_visualization.py +++ b/manager/manager/launcher/launcher_visualization.py @@ -3,203 +3,70 @@ from pydantic import BaseModel -from manager.libs.process_utils import get_class, class_from_module, get_ros_version +from manager.libs.process_utils import get_class, class_from_module from manager.ram_logging.log_manager import LogManager from manager.manager.launcher.launcher_interface import ILauncher - -visualization = { - "none": [], - "console": [ - { - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - } - ], - "bt_studio": [ - { - "type": "module", - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "gazebo_view", - "display": ":2", - "external_port": 6080, - "internal_port": 5900, - }, - ], - "bt_studio_gz": [ - { - "type": "module", - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "gzsim_view", - "display": ":2", - "external_port": 6080, - "internal_port": 5900, - }, - ], - "gazebo_gra": [ - { - "type": "module", - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "gazebo_view", - "display": ":2", - "external_port": 6080, - "internal_port": 5900, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "robot_display_view", - "display": ":3", - "external_port": 2303, - "internal_port": 5902, - }, - ], - "gazebo_rae": [ - { - "type": "module", - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "gazebo_view", - "display": ":2", - "external_port": 6080, - "internal_port": 5900, - }, - ], - "gzsim_gra": [ - { - "type": "module", - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "gzsim_view", - "display": ":2", - "external_port": 6080, - "internal_port": 5900, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "robot_display_view", - "display": ":3", - "external_port": 2303, - "internal_port": 5902, - }, - ], - "gzsim_rae": [ - { - "type": "module", - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "gzsim_view", - "display": ":2", - "external_port": 6080, - "internal_port": 5900, - }, - ], - "physic_gra": [ - { - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "robot_display_view", - "display": ":2", - "external_port": 2303, - "internal_port": 5902, - }, - ], - "physic_rae": [ - { - "module": "console", - "display": ":1", - "external_port": 1108, - "internal_port": 5901, - }, - { - "type": "module", - "width": 1024, - "height": 768, - "module": "robot_display_view", - "display": ":2", - "external_port": 2303, - "internal_port": 5902, - }, - ], +tools = { + "console": { + "module": "console", + "display": ":1", + "external_port": 1108, + "internal_port": 5901, + }, + "gazebo": { + "type": "module", + "width": 1024, + "height": 768, + "module": "gazebo", + "display": ":2", + "external_port": 6080, + "internal_port": 5900, + }, + "gzsim": { + "type": "module", + "width": 1024, + "height": 768, + "module": "gzsim", + "display": ":2", + "external_port": 6080, + "internal_port": 5900, + }, + "web_gui": { + "type": "module", + "module": "web_gui", + "internal_port": 2303, + "consumer": None, + }, + "state_monitor": { + "type": "module", + "module": "state_monitor", + "file": "/tmp/tree_state", + "consumer": None, + }, } -class LauncherVisualization(BaseModel): +class LauncherTools(BaseModel): module: str = ".".join(__name__.split(".")[:-1]) - visualization: str + tools: list[str] visualization_config_path: Optional[str] = None launchers: Optional[ILauncher] = [] - def run(self): - for module in visualization[self.visualization]: - launcher = self.launch_module(module) + def run(self, consumer): + for tool in self.tools: + module = tools[tool] + launcher = self.launch_module(module, consumer) self.launchers.append(launcher) def terminate(self): - LogManager.logger.info("Terminating visualization launcher") + LogManager.logger.info("Terminating tools launcher") for launcher in self.launchers: if launcher.is_running(): launcher.terminate() self.launchers = [] - def launch_module(self, configuration): + def launch_module(self, configuration, consumer): def process_terminated(name, exit_code): LogManager.logger.info( f"LauncherEngine: {name} exited with code {exit_code}" @@ -207,17 +74,38 @@ def process_terminated(name, exit_code): if self.terminated_callback is not None: self.terminated_callback(name, exit_code) + # Replace consumer + if "consumer" in configuration: + configuration["consumer"] = consumer + launcher_module_name = configuration["module"] launcher_module = f"{self.module}.launcher_{launcher_module_name}.Launcher{class_from_module(launcher_module_name)}" launcher_class = get_class(launcher_module) launcher = launcher_class.from_config(launcher_class, configuration) launcher.run(self.visualization_config_path, process_terminated) return launcher + + def pause(self): + for launcher in self.launchers: + launcher.pause() + + def unpause(self): + for launcher in self.launchers: + launcher.unpause() + + def reset(self): + for launcher in self.launchers: + launcher.reset() + + def pass_msg(self, data): + for launcher in self.launchers: + if launcher.acceptsMsgs: + launcher.get_msg(data) def launch_command(self, configuration): pass -class LauncherVisualizationException(Exception): +class LauncherToolsException(Exception): def __init__(self, message): - super(LauncherWorldException, self).__init__(message) + super(LauncherToolsException, self).__init__(message) diff --git a/manager/manager/launcher/launcher_web_gui.py b/manager/manager/launcher/launcher_web_gui.py new file mode 100644 index 0000000..3988b52 --- /dev/null +++ b/manager/manager/launcher/launcher_web_gui.py @@ -0,0 +1,50 @@ +from manager.libs.applications.compatibility.server import Server +from manager.ram_logging.log_manager import LogManager +from manager.comms.new_consumer import ManagerConsumer +from typing import Optional + +class LauncherWebGui(): + internal_port: int + consumer: ManagerConsumer + running: bool = False + acceptsMsgs: bool = True + + def __init__(self, type, module, internal_port, consumer): + self.internal_port = internal_port + self.consumer = consumer + self.server = Server(self.internal_port, self.update) + + def update(self, data): + LogManager.logger.debug(f"Sending update to client") + if self.consumer is not None: + self.consumer.send_message({"update": data}, command="update") + + def run(self, config_file, callback): + self.server.start() + self.running = True + + def get_msg(self, data): + self.server.send(data) + + def is_running(self): + return self.running + + def terminate(self): + self.server.stop() + self.running = False + + def pause(self): + pass + + def unpause(self): + pass + + def reset(self): + pass + + def died(self): + pass + + def from_config(cls, config): + obj = cls(**config) + return obj \ No newline at end of file diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 6a34c61..46620d1 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -32,10 +32,8 @@ from manager.libs.launch_world_model import ConfigurationManager from manager.manager.launcher.launcher_world import LauncherWorld from manager.manager.launcher.launcher_robot import LauncherRobot -from manager.manager.launcher.launcher_visualization import LauncherVisualization +from manager.manager.launcher.launcher_visualization import LauncherTools from manager.ram_logging.log_manager import LogManager -from manager.libs.applications.compatibility.server import Server -from manager.libs.applications.compatibility.file_watchdog import FileWatchdog from manager.manager.application.robotics_python_application_interface import ( IRoboticsPythonApplication, ) @@ -49,7 +47,7 @@ class Manager: "idle", "connected", "world_ready", - "visualization_ready", + "tools_ready", "application_running", "paused", ] @@ -71,15 +69,15 @@ class Manager: }, # Transitions for state world ready { - "trigger": "prepare_visualization", + "trigger": "prepare_tools", "source": "world_ready", - "dest": "visualization_ready", - "before": "on_prepare_visualization", + "dest": "tools_ready", + "before": "on_prepare_tools", }, - # Transitions for state visualization_ready + # Transitions for state tools_ready { "trigger": "run_application", - "source": ["visualization_ready", "paused", "application_running"], + "source": ["tools_ready", "paused", "application_running"], "dest": "application_running", "before": "on_run_application", }, @@ -99,15 +97,15 @@ class Manager: # Transitions for terminate levels { "trigger": "terminate_application", - "source": ["visualization_ready", "application_running", "paused"], - "dest": "visualization_ready", + "source": ["tools_ready", "application_running", "paused"], + "dest": "tools_ready", "before": "on_terminate_application", }, { - "trigger": "terminate_visualization", - "source": "visualization_ready", + "trigger": "terminate_tools", + "source": "tools_ready", "dest": "world_ready", - "before": "on_terminate_visualization", + "before": "on_terminate_tools", }, { "trigger": "terminate_universe", @@ -130,7 +128,7 @@ class Manager: "connected", "paused", "world_ready", - "visualization_ready", + "tools_ready", ], "dest": "=", "before": "on_style_check_application", @@ -143,7 +141,7 @@ class Manager: "connected", "paused", "world_ready", - "visualization_ready", + "tools_ready", ], "dest": "=", "before": "on_code_analysis", @@ -156,7 +154,7 @@ class Manager: "connected", "paused", "world_ready", - "visualization_ready", + "tools_ready", ], "dest": "=", "before": "on_code_format", @@ -169,7 +167,7 @@ class Manager: "connected", "paused", "world_ready", - "visualization_ready", + "tools_ready", ], "dest": "=", "before": "on_code_autocomplete", @@ -191,11 +189,9 @@ def __init__(self, host: str, port: int): self.consumer = ManagerConsumer(host, port, self.queue) self.world_launcher = None self.robot_launcher = None - self.visualization_launcher = None - self.visualization_type = None + self.tools_launcher = None self.application_process = None self.running = True - self.gui_server = None self.linter = Lint() # Creates workspace directories @@ -342,28 +338,20 @@ def prepare_custom_universe(self, cfg_dict): '/bin/bash -c "cd /workspace/worlds; source /opt/ros/humble/setup.bash; colcon build --symlink-install; source install/setup.bash; cd ../.."' ) - def on_prepare_visualization(self, event): + def on_prepare_tools(self, event): - LogManager.logger.info("Visualization transition started") + LogManager.logger.info("Tools transition started") cfg_dict = event.kwargs.get("data", {}) - self.visualization_type = cfg_dict["type"] + self.tools = cfg_dict["tools"] config_file = cfg_dict["file"] - self.visualization_launcher = LauncherVisualization( - visualization=self.visualization_type, visualization_config_path=config_file + self.tools_launcher = LauncherTools( + tools=self.tools, visualization_config_path=config_file ) - self.visualization_launcher.run() - - if self.visualization_type in ["gazebo_rae", "gzsim_rae", "console"]: - self.gui_server = Server(2303, self.update) - self.gui_server.start() - elif self.visualization_type in ["bt_studio", "bt_studio_gz"]: - self.gui_server = FileWatchdog("/tmp/tree_state", self.update_bt_studio) - self.gui_server.start() - - LogManager.logger.info("Visualization transition finished") + self.tools_launcher.run(self.consumer) + LogManager.logger.info("Tools transition finished") def add_frequency_control(self, code): frequency_control_code_imports = """ @@ -668,12 +656,7 @@ def prepare_RA_code(code_path): # Extract app config app_cfg = event.kwargs.get("data", {}) - type = app_cfg["type"] - - if type == "robotics-academy": - code_path = "/workspace/code/academy.py" - elif type == "bt-studio": - code_path = "/workspace/code/execute_docker.py" + entrypoint = app_cfg["entrypoint"] # Unzip the app if app_cfg["code"].startswith("data:"): @@ -684,19 +667,20 @@ def prepare_RA_code(code_path): zip_ref.extractall("/workspace/code") zip_ref.close() - if not os.path.isfile(code_path): + if not os.path.isfile(entrypoint): LogManager.logger.info("User code not found") raise Exception("User code not found") try: - if type == "robotics-academy": - prepare_RA_code(code_path) + if entrypoint == "/workspace/code/academy.py": + # TODO: temporal + prepare_RA_code(entrypoint) fds = os.listdir("/dev/pts/") console_fd = str(max(map(int, fds[:-1]))) self.application_process = subprocess.Popen( - ["python3", code_path], + ["python3", entrypoint], stdin=open("/dev/pts/" + console_fd, "r"), stdout=sys.stdout, stderr=subprocess.STDOUT, @@ -767,12 +751,9 @@ def on_terminate_application(self, event): LogManager.logger.exception("No application running") print(traceback.format_exc()) - def on_terminate_visualization(self, event): + def on_terminate_tools(self, event): - self.visualization_launcher.terminate() - if self.gui_server != None: - self.gui_server.stop() - self.gui_server = None + self.tools_launcher.terminate() self.terminate_harmonic_processes() def on_terminate_universe(self, event): @@ -797,12 +778,12 @@ def on_disconnect(self, event): except Exception as e: LogManager.logger.exception("Exception stopping application process") - if self.visualization_launcher: + if self.tools_launcher: try: - self.visualization_launcher.terminate() + self.tools_launcher.terminate() except Exception as e: LogManager.logger.exception( - "Exception terminating visualization launcher" + "Exception terminating tools launcher" ) if self.robot_launcher: @@ -825,7 +806,7 @@ def on_disconnect(self, event): def process_message(self, message): if message.command == "gui": - self.gui_server.send(message.data) + self.tools_launcher.pass_msg(message.data) return self.trigger(message.command, data=message.data or None) @@ -862,53 +843,16 @@ def on_resume(self, msg): self.reset_sim() def pause_sim(self): - if self.visualization_type in ["gzsim_rae", "bt_studio_gz"]: - self.call_gzservice( - "$(gz service -l | grep '^/world/\w*/control$')", - "gz.msgs.WorldControl", - "gz.msgs.Boolean", - "3000", - "pause: true", - ) - elif not self.visualization_type in ["console"]: - self.call_service("/pause_physics", "std_srvs/srv/Empty") + self.tools_launcher.pause() def unpause_sim(self): - if self.visualization_type in ["gzsim_rae", "bt_studio_gz"]: - self.call_gzservice( - "$(gz service -l | grep '^/world/\w*/control$')", - "gz.msgs.WorldControl", - "gz.msgs.Boolean", - "3000", - "pause: false", - ) - elif not self.visualization_type in ["console"]: - self.call_service("/unpause_physics", "std_srvs/srv/Empty") + self.tools_launcher.unpause() def reset_sim(self): if self.robot_launcher: self.robot_launcher.terminate() - if self.visualization_type in ["gzsim_rae", "bt_studio_gz"]: - if self.is_ros_service_available("/drone0/platform/state_machine/_reset"): - self.call_service( - "/drone0/platform/state_machine/_reset", - "std_srvs/srv/Trigger", - "{}", - ) - self.call_gzservice( - "$(gz service -l | grep '^/world/\w*/control$')", - "gz.msgs.WorldControl", - "gz.msgs.Boolean", - "3000", - "reset: {all: true}", - ) - if self.is_ros_service_available("/drone0/controller/_reset"): - self.call_service( - "/drone0/controller/_reset", "std_srvs/srv/Trigger", "{}" - ) - elif not self.visualization_type in ["console"]: - self.call_service("/reset_world", "std_srvs/srv/Empty") + self.tools_launcher.reset() if self.robot_launcher: try: @@ -916,41 +860,6 @@ def reset_sim(self): except Exception as e: LogManager.logger.exception("Exception terminating world launcher") - def call_service(self, service, service_type, request_data="{}"): - command = f"ros2 service call {service} {service_type} '{request_data}'" - subprocess.call( - f"{command}", - shell=True, - stdout=sys.stdout, - stderr=subprocess.STDOUT, - bufsize=1024, - universal_newlines=True, - ) - - def call_gzservice(self, service, reqtype, reptype, timeout, req): - command = f"gz service -s {service} --reqtype {reqtype} --reptype {reptype} --timeout {timeout} --req '{req}'" - subprocess.call( - f"{command}", - shell=True, - stdout=sys.stdout, - stderr=subprocess.STDOUT, - bufsize=1024, - universal_newlines=True, - ) - - def is_ros_service_available(self, service_name): - try: - result = subprocess.run( - ["ros2", "service", "list", "--include-hidden-services"], - capture_output=True, - text=True, - check=True, - ) - return service_name in result.stdout - except subprocess.CalledProcessError as e: - LogManager.logger.exception(f"Error checking service availability: {e}") - return False - def start(self): """ Starts the RAM @@ -965,11 +874,6 @@ def start(self): def signal_handler(sign, frame): print("\nprogram exiting gracefully") self.running = False - if self.gui_server is not None: - try: - self.gui_server.stop() - except Exception as e: - LogManager.logger.exception("Exception stopping GUI server") try: self.consumer.stop() @@ -985,12 +889,12 @@ def signal_handler(sign, frame): "Exception stopping application process" ) - if self.visualization_launcher: + if self.tools_launcher: try: - self.visualization_launcher.terminate() + self.tools_launcher.terminate() except Exception as e: LogManager.logger.exception( - "Exception terminating visualization launcher" + "Exception terminating tools launcher" ) if self.robot_launcher: From 507387e4db260191157adb05d9954aa5840d5a3f Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 23 Jun 2025 17:36:01 +0200 Subject: [PATCH 02/15] Change tools config --- .../{launcher_visualization.py => launcher_tools.py} | 12 ++++++++---- manager/manager/manager.py | 8 ++++---- 2 files changed, 12 insertions(+), 8 deletions(-) rename manager/manager/launcher/{launcher_visualization.py => launcher_tools.py} (89%) diff --git a/manager/manager/launcher/launcher_visualization.py b/manager/manager/launcher/launcher_tools.py similarity index 89% rename from manager/manager/launcher/launcher_visualization.py rename to manager/manager/launcher/launcher_tools.py index 8bec29d..4d812ab 100644 --- a/manager/manager/launcher/launcher_visualization.py +++ b/manager/manager/launcher/launcher_tools.py @@ -50,13 +50,13 @@ class LauncherTools(BaseModel): module: str = ".".join(__name__.split(".")[:-1]) tools: list[str] - visualization_config_path: Optional[str] = None + tools_config: Optional[dict] = None launchers: Optional[ILauncher] = [] def run(self, consumer): for tool in self.tools: module = tools[tool] - launcher = self.launch_module(module, consumer) + launcher = self.launch_module(tool, module, consumer) self.launchers.append(launcher) def terminate(self): @@ -66,7 +66,7 @@ def terminate(self): launcher.terminate() self.launchers = [] - def launch_module(self, configuration, consumer): + def launch_module(self, name, configuration, consumer): def process_terminated(name, exit_code): LogManager.logger.info( f"LauncherEngine: {name} exited with code {exit_code}" @@ -81,8 +81,12 @@ def process_terminated(name, exit_code): launcher_module_name = configuration["module"] launcher_module = f"{self.module}.launcher_{launcher_module_name}.Launcher{class_from_module(launcher_module_name)}" launcher_class = get_class(launcher_module) + config = None + if self.tools_config is not None and name in self.tools_config: + config = self.tools_config[name] + launcher = launcher_class.from_config(launcher_class, configuration) - launcher.run(self.visualization_config_path, process_terminated) + launcher.run(config, process_terminated) return launcher def pause(self): diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 46620d1..eeafb72 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -32,7 +32,7 @@ from manager.libs.launch_world_model import ConfigurationManager from manager.manager.launcher.launcher_world import LauncherWorld from manager.manager.launcher.launcher_robot import LauncherRobot -from manager.manager.launcher.launcher_visualization import LauncherTools +from src.manager.manager.launcher.launcher_tools import LauncherTools from manager.ram_logging.log_manager import LogManager from manager.manager.application.robotics_python_application_interface import ( IRoboticsPythonApplication, @@ -343,11 +343,11 @@ def on_prepare_tools(self, event): LogManager.logger.info("Tools transition started") cfg_dict = event.kwargs.get("data", {}) - self.tools = cfg_dict["tools"] - config_file = cfg_dict["file"] + tools = cfg_dict["tools"] + config = cfg_dict["config"] self.tools_launcher = LauncherTools( - tools=self.tools, visualization_config_path=config_file + tools=tools, tools_config=config ) self.tools_launcher.run(self.consumer) From c09d361aa4a38206746131d96e25fe627ae1b1dc Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 23 Jun 2025 17:59:24 +0200 Subject: [PATCH 03/15] Linter --- manager/manager/launcher/launcher_console.py | 2 +- manager/manager/launcher/launcher_gazebo.py | 4 +++- manager/manager/launcher/launcher_gzsim.py | 4 ++++ manager/manager/launcher/launcher_tools.py | 4 ++-- manager/manager/launcher/launcher_web_gui.py | 7 ++++--- manager/manager/manager.py | 12 +++--------- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/manager/manager/launcher/launcher_console.py b/manager/manager/launcher/launcher_console.py index 6f39ff3..0611fef 100644 --- a/manager/manager/launcher/launcher_console.py +++ b/manager/manager/launcher/launcher_console.py @@ -41,7 +41,7 @@ def run(self, config_file, callback): def pause(self): pass - + def unpause(self): pass diff --git a/manager/manager/launcher/launcher_gazebo.py b/manager/manager/launcher/launcher_gazebo.py index 3811559..0273622 100644 --- a/manager/manager/launcher/launcher_gazebo.py +++ b/manager/manager/launcher/launcher_gazebo.py @@ -12,6 +12,7 @@ import stat from typing import List, Any + def call_service(self, service, service_type, request_data="{}"): command = f"ros2 service call {service} {service_type} '{request_data}'" subprocess.call( @@ -23,6 +24,7 @@ def call_service(self, service, service_type, request_data="{}"): universal_newlines=True, ) + class LauncherGazebo(ILauncher): display: str internal_port: int @@ -71,7 +73,7 @@ def unpause(self): def reset(self): call_service("/reset_world", "std_srvs/srv/Empty") - + def is_running(self): return self.running diff --git a/manager/manager/launcher/launcher_gzsim.py b/manager/manager/launcher/launcher_gzsim.py index f01afbc..6306851 100644 --- a/manager/manager/launcher/launcher_gzsim.py +++ b/manager/manager/launcher/launcher_gzsim.py @@ -13,6 +13,7 @@ from typing import List, Any from manager.ram_logging.log_manager import LogManager + def call_gzservice(self, service, reqtype, reptype, timeout, req): command = f"gz service -s {service} --reqtype {reqtype} --reptype {reptype} --timeout {timeout} --req '{req}'" subprocess.call( @@ -24,6 +25,7 @@ def call_gzservice(self, service, reqtype, reptype, timeout, req): universal_newlines=True, ) + def call_service(self, service, service_type, request_data="{}"): command = f"ros2 service call {service} {service_type} '{request_data}'" subprocess.call( @@ -35,6 +37,7 @@ def call_service(self, service, service_type, request_data="{}"): universal_newlines=True, ) + def is_ros_service_available(self, service_name): try: result = subprocess.run( @@ -48,6 +51,7 @@ def is_ros_service_available(self, service_name): LogManager.logger.exception(f"Error checking service availability: {e}") return False + class LauncherGzsim(ILauncher): display: str internal_port: int diff --git a/manager/manager/launcher/launcher_tools.py b/manager/manager/launcher/launcher_tools.py index 4d812ab..efc8e54 100644 --- a/manager/manager/launcher/launcher_tools.py +++ b/manager/manager/launcher/launcher_tools.py @@ -84,11 +84,11 @@ def process_terminated(name, exit_code): config = None if self.tools_config is not None and name in self.tools_config: config = self.tools_config[name] - + launcher = launcher_class.from_config(launcher_class, configuration) launcher.run(config, process_terminated) return launcher - + def pause(self): for launcher in self.launchers: launcher.pause() diff --git a/manager/manager/launcher/launcher_web_gui.py b/manager/manager/launcher/launcher_web_gui.py index 3988b52..6d5c9e3 100644 --- a/manager/manager/launcher/launcher_web_gui.py +++ b/manager/manager/launcher/launcher_web_gui.py @@ -3,7 +3,8 @@ from manager.comms.new_consumer import ManagerConsumer from typing import Optional -class LauncherWebGui(): + +class LauncherWebGui: internal_port: int consumer: ManagerConsumer running: bool = False @@ -35,7 +36,7 @@ def terminate(self): def pause(self): pass - + def unpause(self): pass @@ -47,4 +48,4 @@ def died(self): def from_config(cls, config): obj = cls(**config) - return obj \ No newline at end of file + return obj diff --git a/manager/manager/manager.py b/manager/manager/manager.py index eeafb72..7a6cb9e 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -346,9 +346,7 @@ def on_prepare_tools(self, event): tools = cfg_dict["tools"] config = cfg_dict["config"] - self.tools_launcher = LauncherTools( - tools=tools, tools_config=config - ) + self.tools_launcher = LauncherTools(tools=tools, tools_config=config) self.tools_launcher.run(self.consumer) LogManager.logger.info("Tools transition finished") @@ -782,9 +780,7 @@ def on_disconnect(self, event): try: self.tools_launcher.terminate() except Exception as e: - LogManager.logger.exception( - "Exception terminating tools launcher" - ) + LogManager.logger.exception("Exception terminating tools launcher") if self.robot_launcher: try: @@ -893,9 +889,7 @@ def signal_handler(sign, frame): try: self.tools_launcher.terminate() except Exception as e: - LogManager.logger.exception( - "Exception terminating tools launcher" - ) + LogManager.logger.exception("Exception terminating tools launcher") if self.robot_launcher: try: From 609ad8f33fb8f506d8b8c96df86e5087a1f530a0 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 1 Jul 2025 14:13:27 +0200 Subject: [PATCH 04/15] Adding world type for tools --- manager/libs/launch_world_model.py | 2 +- manager/manager/launcher/launcher_robot.py | 4 ++-- manager/manager/launcher/launcher_tools.py | 8 ++++++++ manager/manager/launcher/launcher_world.py | 24 ++++------------------ manager/manager/manager.py | 9 +++++--- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/manager/libs/launch_world_model.py b/manager/libs/launch_world_model.py index d59d8c3..8a017f3 100644 --- a/manager/libs/launch_world_model.py +++ b/manager/libs/launch_world_model.py @@ -6,7 +6,7 @@ class ConfigurationModel(BaseModel): - world: str + type: str launch_file_path: str diff --git a/manager/manager/launcher/launcher_robot.py b/manager/manager/launcher/launcher_robot.py index 00826af..8519f67 100644 --- a/manager/manager/launcher/launcher_robot.py +++ b/manager/manager/launcher/launcher_robot.py @@ -66,7 +66,7 @@ class LauncherRobot(BaseModel): - world: str + type: str launch_file_path: str module: str = ".".join(__name__.split(".")[:-1]) ros_version: int = get_ros_version() @@ -76,7 +76,7 @@ class LauncherRobot(BaseModel): def run(self, start_pose=None): if start_pose != None: self.start_pose = start_pose - for module in worlds[self.world][str(self.ros_version)]: + for module in worlds[self.type][str(self.ros_version)]: module["launch_file"] = self.launch_file_path launcher = self.launch_module(module) self.launchers.append(launcher) diff --git a/manager/manager/launcher/launcher_tools.py b/manager/manager/launcher/launcher_tools.py index efc8e54..8d1093e 100644 --- a/manager/manager/launcher/launcher_tools.py +++ b/manager/manager/launcher/launcher_tools.py @@ -46,15 +46,23 @@ }, } +simulator = { + "gazebo": {"tool": "gazebo"}, + "gz": {"tool": "gzsim"}, +} + class LauncherTools(BaseModel): module: str = ".".join(__name__.split(".")[:-1]) + world_type: str tools: list[str] tools_config: Optional[dict] = None launchers: Optional[ILauncher] = [] def run(self, consumer): for tool in self.tools: + if tool == "simulator": + tool = simulator[self.world_type]["tool"] module = tools[tool] launcher = self.launch_module(tool, module, consumer) self.launchers.append(launcher) diff --git a/manager/manager/launcher/launcher_world.py b/manager/manager/launcher/launcher_world.py index b96f434..9ff62c5 100644 --- a/manager/manager/launcher/launcher_world.py +++ b/manager/manager/launcher/launcher_world.py @@ -16,27 +16,11 @@ } ], }, - "drones": { + "gz": { "2": [ { "type": "module", - "module": "drones_ros2", - "resource_folders": [], - "model_folders": [], - "plugin_folders": [], - "parameters": [], - "launch_file": [], - } - ], - }, - "gzsimdrones": { - "2": [ - { - "type": "module", - "module": "drones_gzsim", - "resource_folders": [], - "model_folders": [], - "plugin_folders": [], + "module": "ros2_api", "parameters": [], "launch_file": [], } @@ -47,14 +31,14 @@ class LauncherWorld(BaseModel): - world: str + type: str launch_file_path: str module: str = ".".join(__name__.split(".")[:-1]) ros_version: int = get_ros_version() launchers: Optional[ILauncher] = [] def run(self): - for module in worlds[self.world][str(self.ros_version)]: + for module in worlds[self.type][str(self.ros_version)]: module["launch_file"] = self.launch_file_path launcher = self.launch_module(module) self.launchers.append(launcher) diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 7a6cb9e..8b0cd8b 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -188,6 +188,7 @@ def __init__(self, host: str, port: int): self.queue = Queue() self.consumer = ManagerConsumer(host, port, self.queue) self.world_launcher = None + self.world_type = None self.robot_launcher = None self.tools_launcher = None self.application_process = None @@ -270,7 +271,7 @@ def on_launch_world(self, event): # Launch world try: - if world_cfg["world"] == None: + if world_cfg["type"] == None: self.world_launcher = None LogManager.logger.info("Launch transition finished") return @@ -285,6 +286,8 @@ def on_launch_world(self, event): except ValueError as e: LogManager.logger.error(f"Configuration validation failed: {e}") + self.world_type = world_cfg["type"] + self.world_launcher = LauncherWorld(**cfg.model_dump()) LogManager.logger.info(str(self.world_launcher)) self.world_launcher.run() @@ -292,7 +295,7 @@ def on_launch_world(self, event): # Launch robot try: - if robot_cfg["world"] == None: + if robot_cfg["type"] == None: self.robot_launcher = None LogManager.logger.info("Launch transition finished") return @@ -346,7 +349,7 @@ def on_prepare_tools(self, event): tools = cfg_dict["tools"] config = cfg_dict["config"] - self.tools_launcher = LauncherTools(tools=tools, tools_config=config) + self.tools_launcher = LauncherTools(world_type= self.world_type ,tools=tools, tools_config=config) self.tools_launcher.run(self.consumer) LogManager.logger.info("Tools transition finished") From 6a85484f58f4e97c9d78876a69a6ea9fec6831c7 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Thu, 3 Jul 2025 08:22:26 +0200 Subject: [PATCH 05/15] Fix import --- manager/manager/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 8b0cd8b..eb9af75 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -32,7 +32,7 @@ from manager.libs.launch_world_model import ConfigurationManager from manager.manager.launcher.launcher_world import LauncherWorld from manager.manager.launcher.launcher_robot import LauncherRobot -from src.manager.manager.launcher.launcher_tools import LauncherTools +from manager.manager.launcher.launcher_tools import LauncherTools from manager.ram_logging.log_manager import LogManager from manager.manager.application.robotics_python_application_interface import ( IRoboticsPythonApplication, From 89d44b878feb4af653e2b0bc35dd97fd5e71d7e4 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 7 Jul 2025 07:50:34 +0200 Subject: [PATCH 06/15] First try without code injection --- manager/manager/manager.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/manager/manager/manager.py b/manager/manager/manager.py index eb9af75..85f2eca 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -629,10 +629,11 @@ def prepare_RA_code(code_path): errors = self.linter.evaluate_code(code, self.ros_version) if errors == "": - code = self.add_frequency_control(code) - f = open(code_path, "w") - f.write(code) - f.close() + # code = self.add_frequency_control(code) + # f = open(code_path, "w") + # f.write(code) + # f.close() + pass else: console_path = find_docker_console() @@ -667,15 +668,16 @@ def prepare_RA_code(code_path): zip_ref = zipfile.ZipFile("/workspace/code/app.zip", "r") zip_ref.extractall("/workspace/code") zip_ref.close() + print("Here") if not os.path.isfile(entrypoint): LogManager.logger.info("User code not found") raise Exception("User code not found") try: - if entrypoint == "/workspace/code/academy.py": - # TODO: temporal - prepare_RA_code(entrypoint) + # if entrypoint == "/workspace/code/academy.py": + # # TODO: temporal + # prepare_RA_code(entrypoint) fds = os.listdir("/dev/pts/") console_fd = str(max(map(int, fds[:-1]))) From f6a1821e21b94519890558463b7ab0de5f8f1f0a Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 7 Jul 2025 07:52:18 +0200 Subject: [PATCH 07/15] Fix call services --- manager/manager/launcher/launcher_gazebo.py | 2 +- manager/manager/launcher/launcher_gzsim.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manager/manager/launcher/launcher_gazebo.py b/manager/manager/launcher/launcher_gazebo.py index 0273622..c4a3e3a 100644 --- a/manager/manager/launcher/launcher_gazebo.py +++ b/manager/manager/launcher/launcher_gazebo.py @@ -13,7 +13,7 @@ from typing import List, Any -def call_service(self, service, service_type, request_data="{}"): +def call_service(service, service_type, request_data="{}"): command = f"ros2 service call {service} {service_type} '{request_data}'" subprocess.call( f"{command}", diff --git a/manager/manager/launcher/launcher_gzsim.py b/manager/manager/launcher/launcher_gzsim.py index 6306851..a4296d2 100644 --- a/manager/manager/launcher/launcher_gzsim.py +++ b/manager/manager/launcher/launcher_gzsim.py @@ -14,7 +14,7 @@ from manager.ram_logging.log_manager import LogManager -def call_gzservice(self, service, reqtype, reptype, timeout, req): +def call_gzservice(service, reqtype, reptype, timeout, req): command = f"gz service -s {service} --reqtype {reqtype} --reptype {reptype} --timeout {timeout} --req '{req}'" subprocess.call( f"{command}", @@ -26,7 +26,7 @@ def call_gzservice(self, service, reqtype, reptype, timeout, req): ) -def call_service(self, service, service_type, request_data="{}"): +def call_service(service, service_type, request_data="{}"): command = f"ros2 service call {service} {service_type} '{request_data}'" subprocess.call( f"{command}", @@ -38,7 +38,7 @@ def call_service(self, service, service_type, request_data="{}"): ) -def is_ros_service_available(self, service_name): +def is_ros_service_available(service_name): try: result = subprocess.run( ["ros2", "service", "list", "--include-hidden-services"], From d678350bbc8ff86b8b89b2387af76cfeccacbca4 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 8 Jul 2025 10:17:10 +0200 Subject: [PATCH 08/15] Add linter to all source files --- manager/manager/lint/linter.py | 51 +++++++++++++++++++++- manager/manager/manager.py | 78 +++++++--------------------------- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/manager/manager/lint/linter.py b/manager/manager/lint/linter.py index 189f3fb..7fed132 100644 --- a/manager/manager/lint/linter.py +++ b/manager/manager/lint/linter.py @@ -1,7 +1,8 @@ +import glob import re import os import subprocess - +import tempfile class Lint: @@ -103,3 +104,51 @@ def evaluate_code( return final_result.strip() except Exception as ex: print(ex) + + def evaluate_source_code(self, files): + all_files = [] + for f in files: + glob_files = glob.glob(os.path.join("/workspace/code", f)) + for g in glob_files: + all_files.append(g) + + output = [] + + linter_env = os.environ.copy() + linter_env["PYTHONPATH"] = f"{linter_env['PYTHONPATH']}:/workspace/code" + + try: + + for file in all_files: + if file.endswith(".py"): + with open(file) as f: + python_code = f.read() + + # Create temp file + code_file = tempfile.NamedTemporaryFile(delete=False) + code_file.write(python_code.encode()) + code_file.seek(0) + code_file.close() + + options = f"{code_file.name} --enable=similarities --disable=C0114,C0116" + + # Run pylint using subprocess + result = subprocess.run( + ["pylint"] + options.split(), capture_output=True, text=True, env=linter_env + ) + + # Process pylint exit + stdout = result.stdout + + # Clean temp files + if os.path.exists(code_file.name): + os.remove(code_file.name) + + raw_output = stdout + "\n" + cleaned_result = self.clean_pylint_output(raw_output) + final_result = self.append_rating_if_missing(cleaned_result) + output.append(final_result.strip()) + + return output + except Exception as ex: + return ex diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 85f2eca..5b1d64c 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -354,36 +354,6 @@ def on_prepare_tools(self, event): self.tools_launcher.run(self.consumer) LogManager.logger.info("Tools transition finished") - def add_frequency_control(self, code): - frequency_control_code_imports = """ -import time -from datetime import datetime -ideal_cycle = 20 -""" - code = frequency_control_code_imports + code - infinite_loop = re.search( - r"[^ ]while\s*\(\s*True\s*\)\s*:|[^ ]while\s*True\s*:|[^ ]while\s*1\s*:|[^ ]while\s*\(\s*1\s*\)\s*:", - code, - ) - frequency_control_code_pre = """ - start_time_internal_freq_control = datetime.now() - """ - code = ( - code[: infinite_loop.end()] - + frequency_control_code_pre - + code[infinite_loop.end() :] - ) - frequency_control_code_post = """ - finish_time_internal_freq_control = datetime.now() - dt = finish_time_internal_freq_control - start_time_internal_freq_control - ms = (dt.days * 24 * 60 * 60 + dt.seconds) * 1000 + dt.microseconds / 1000.0 - - if (ms < ideal_cycle): - time.sleep((ideal_cycle - ms) / 1000.0) -""" - code = code + frequency_control_code_post - return code - def on_style_check_application(self, event): """ Handles the 'style_check' event, does not change the state and returns the current state. @@ -616,33 +586,6 @@ def find_docker_console(): # raise Exception("No active console other than /dev/pts/0") return consoles - def prepare_RA_code(code_path): - f = open(code_path, "r") - code = f.read() - f.close() - - # Make code backwards compatible - code = code.replace("from GUI import GUI", "import GUI") - code = code.replace("from HAL import HAL", "import HAL") - - # Create executable app - errors = self.linter.evaluate_code(code, self.ros_version) - if errors == "": - - # code = self.add_frequency_control(code) - # f = open(code_path, "w") - # f.write(code) - # f.close() - pass - - else: - console_path = find_docker_console() - for i in console_path: - with open(i, "w") as console: - console.write(errors + "\n\n") - - raise Exception(errors) - # Kill already running code try: proc = psutil.Process(self.application_process.pid) @@ -659,6 +602,7 @@ def prepare_RA_code(code_path): # Extract app config app_cfg = event.kwargs.get("data", {}) entrypoint = app_cfg["entrypoint"] + to_lint = app_cfg["linter"] # Unzip the app if app_cfg["code"].startswith("data:"): @@ -668,17 +612,27 @@ def prepare_RA_code(code_path): zip_ref = zipfile.ZipFile("/workspace/code/app.zip", "r") zip_ref.extractall("/workspace/code") zip_ref.close() - print("Here") if not os.path.isfile(entrypoint): LogManager.logger.info("User code not found") raise Exception("User code not found") - try: - # if entrypoint == "/workspace/code/academy.py": - # # TODO: temporal - # prepare_RA_code(entrypoint) + # Pass the linter + errors = self.linter.evaluate_source_code(to_lint) + failed_linter = False + + for error in errors: + if error != "": + failed_linter = True + console_path = find_docker_console() + for i in console_path: + with open(i, "w") as console: + console.write(errors + "\n\n") + + if failed_linter: + raise Exception(errors) + try: fds = os.listdir("/dev/pts/") console_fd = str(max(map(int, fds[:-1]))) From 90916b4038d606b01fcb2a1febfd1b6bd33cb6fb Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 8 Jul 2025 14:10:11 +0200 Subject: [PATCH 09/15] Fix error printing --- manager/manager/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 5b1d64c..2e5e924 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -627,7 +627,7 @@ def find_docker_console(): console_path = find_docker_console() for i in console_path: with open(i, "w") as console: - console.write(errors + "\n\n") + console.write(error + "\n\n") if failed_linter: raise Exception(errors) From 806885e590ff67ac8407bd3506de87b323f7586c Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 14 Jul 2025 20:23:56 +0200 Subject: [PATCH 10/15] Add debug --- manager/manager/docker_thread/docker_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manager/manager/docker_thread/docker_thread.py b/manager/manager/docker_thread/docker_thread.py index 1531088..863c53a 100644 --- a/manager/manager/docker_thread/docker_thread.py +++ b/manager/manager/docker_thread/docker_thread.py @@ -4,7 +4,7 @@ import subprocess import os import signal - +import sys class DockerThread(threading.Thread): """Threaded Docker Thread Class""" @@ -19,7 +19,7 @@ def run(self): self.process = subprocess.Popen( self.cmd, shell=self.shell, - stdout=subprocess.PIPE, + stdout=sys.stdout, stderr=subprocess.PIPE, start_new_session=True, bufsize=1024, From 4ce895bf935b037eb9e845d16cb2a46d0adba1f9 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Mon, 14 Jul 2025 20:42:50 +0200 Subject: [PATCH 11/15] Remove changes --- manager/manager/docker_thread/docker_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manager/manager/docker_thread/docker_thread.py b/manager/manager/docker_thread/docker_thread.py index 863c53a..1531088 100644 --- a/manager/manager/docker_thread/docker_thread.py +++ b/manager/manager/docker_thread/docker_thread.py @@ -4,7 +4,7 @@ import subprocess import os import signal -import sys + class DockerThread(threading.Thread): """Threaded Docker Thread Class""" @@ -19,7 +19,7 @@ def run(self): self.process = subprocess.Popen( self.cmd, shell=self.shell, - stdout=sys.stdout, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True, bufsize=1024, From b7938564ca427974e606fb45b27df080143f623a Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 15 Jul 2025 08:21:55 +0200 Subject: [PATCH 12/15] Add debugger --- manager/manager/docker_thread/docker_thread.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manager/manager/docker_thread/docker_thread.py b/manager/manager/docker_thread/docker_thread.py index 1531088..0ad624f 100644 --- a/manager/manager/docker_thread/docker_thread.py +++ b/manager/manager/docker_thread/docker_thread.py @@ -4,6 +4,7 @@ import subprocess import os import signal +import sys class DockerThread(threading.Thread): @@ -19,7 +20,7 @@ def run(self): self.process = subprocess.Popen( self.cmd, shell=self.shell, - stdout=subprocess.PIPE, + stdout=sys.stdout, stderr=subprocess.PIPE, start_new_session=True, bufsize=1024, From 3fd9d5a0cf2632e7dbfa502b340da2f7280ed3ea Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 15 Jul 2025 08:42:42 +0200 Subject: [PATCH 13/15] Remove debug --- manager/manager/docker_thread/docker_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manager/manager/docker_thread/docker_thread.py b/manager/manager/docker_thread/docker_thread.py index 0ad624f..27ba677 100644 --- a/manager/manager/docker_thread/docker_thread.py +++ b/manager/manager/docker_thread/docker_thread.py @@ -20,7 +20,7 @@ def run(self): self.process = subprocess.Popen( self.cmd, shell=self.shell, - stdout=sys.stdout, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True, bufsize=1024, From 2cffaaa4906ff4672c1474fa437db7ca1d635b87 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 15 Jul 2025 11:13:05 +0200 Subject: [PATCH 14/15] Fix linter --- manager/manager/lint/linter.py | 10 ++++++++-- manager/manager/manager.py | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/manager/manager/lint/linter.py b/manager/manager/lint/linter.py index 7fed132..fd3856f 100644 --- a/manager/manager/lint/linter.py +++ b/manager/manager/lint/linter.py @@ -4,6 +4,7 @@ import subprocess import tempfile + class Lint: def clean_pylint_output(self, result, warnings=False): @@ -130,11 +131,16 @@ def evaluate_source_code(self, files): code_file.seek(0) code_file.close() - options = f"{code_file.name} --enable=similarities --disable=C0114,C0116" + options = ( + f"{code_file.name} --enable=similarities --disable=C0114,C0116" + ) # Run pylint using subprocess result = subprocess.run( - ["pylint"] + options.split(), capture_output=True, text=True, env=linter_env + ["pylint"] + options.split(), + capture_output=True, + text=True, + env=linter_env, ) # Process pylint exit diff --git a/manager/manager/manager.py b/manager/manager/manager.py index 2e5e924..e9c1591 100644 --- a/manager/manager/manager.py +++ b/manager/manager/manager.py @@ -349,7 +349,9 @@ def on_prepare_tools(self, event): tools = cfg_dict["tools"] config = cfg_dict["config"] - self.tools_launcher = LauncherTools(world_type= self.world_type ,tools=tools, tools_config=config) + self.tools_launcher = LauncherTools( + world_type=self.world_type, tools=tools, tools_config=config + ) self.tools_launcher.run(self.consumer) LogManager.logger.info("Tools transition finished") From 8f0a342d301d3c3611e6baa6a66ad2e78e88b6e5 Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 15 Jul 2025 14:36:10 +0200 Subject: [PATCH 15/15] Add optional world type --- manager/manager/launcher/launcher_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manager/manager/launcher/launcher_tools.py b/manager/manager/launcher/launcher_tools.py index 8d1093e..4eef9b0 100644 --- a/manager/manager/launcher/launcher_tools.py +++ b/manager/manager/launcher/launcher_tools.py @@ -54,7 +54,7 @@ class LauncherTools(BaseModel): module: str = ".".join(__name__.split(".")[:-1]) - world_type: str + world_type: Optional[str] = None tools: list[str] tools_config: Optional[dict] = None launchers: Optional[ILauncher] = []