From 31e5c042df4b32464e9f545decbe0f5e777e56df Mon Sep 17 00:00:00 2001 From: Javier Izquierdo Hernandez Date: Tue, 7 Jul 2026 12:52:47 +0200 Subject: [PATCH] Rename univers to world and world to scene --- README.md | 16 ++-- .../manager/launcher/__init__.py | 2 +- .../{launcher_world.py => launcher_scene.py} | 8 +- .../manager/manager.py | 78 +++++++++---------- test/conftest.py | 6 +- test/test_connected_to_world_ready.py | 4 +- test/test_terminate_transitions.py | 12 +-- test/test_utils.py | 4 +- 8 files changed, 62 insertions(+), 68 deletions(-) rename robotics_application_manager/manager/launcher/{launcher_world.py => launcher_scene.py} (92%) diff --git a/README.md b/README.md index 1f4178b..ef347e3 100644 --- a/README.md +++ b/README.md @@ -75,12 +75,12 @@ The `Manager` class is the core of RAM, orchestrating operations and managing tr 4. **Error Handling**: `ManagerConsumer` communicates exceptions back to the client and `Manager`. 5. **Lifecycle Management**: `Manager` controls the start and stop of the `ManagerConsumer` WebSocket server. -#### Interaction Between `Manager` and `LauncherWorld` +#### Interaction Between `Manager` and `LauncherScene` -1. **World Initialization and Launching**: `Manager` initializes `LauncherWorld` with specific configurations, such as world type (e.g., `gazebo`, `drones`) and the launch file path. -2. **Dynamic Module Management**: `LauncherWorld` dynamically launches modules based on the world configuration and ROS version, as dictated by `Manager`. -3. **State Management and Transition**: The state of `Manager` is updated in response to the actions performed by `LauncherWorld`. For example, once the world is ready, `Manager` may transition to the `world_ready` state. -4. **Termination and Cleanup**: `Manager` can instruct `LauncherWorld` to terminate the world environment through its `terminate` method. `LauncherWorld` ensures a clean and orderly shutdown of all modules and resources involved in the world setup. +1. **World Initialization and Launching**: `Manager` initializes `LauncherScene` with specific configurations, such as world type (e.g., `gazebo`, `drones`) and the launch file path. +2. **Dynamic Module Management**: `LauncherScene` dynamically launches modules based on the world configuration and ROS version, as dictated by `Manager`. +3. **State Management and Transition**: The state of `Manager` is updated in response to the actions performed by `LauncherScene`. For example, once the world is ready, `Manager` may transition to the `world_ready` state. +4. **Termination and Cleanup**: `Manager` can instruct `LauncherScene` to terminate the world environment through its `terminate` method. `LauncherScene` ensures a clean and orderly shutdown of all modules and resources involved in the world setup. 5. **Error Handling and Logging**: `Manager` handles exceptions and errors that may arise during the world setup or termination processes, ensuring robust operation. #### Interaction Between `Manager` and `LauncherTools` @@ -110,32 +110,26 @@ The `Manager` class is the core of RAM, orchestrating operations and managing tr ## Usage Example 1. **Connecting to RAM**: - - Initially, the RAM is in the `idle` state. - A client (e.g., a user interface or another system) connects to RAM, triggering the `connect` transition and moving RAM to the `connected` state. 2. **Launching the World**: - - Once connected, the client can request RAM to launch a robotic world by sending a `launch_world` command. - RAM transitions to the `world_ready` state after successfully setting up the world environment. 3. **Setting Up Tools**: - - After the world is ready, the client requests RAM to prepare the tools with a `prepare_tools` command. - RAM transitions to the `tools_ready` state, indicating that the tools are set up and ready. 4. **Running an Application**: - - The client then requests RAM to run a specific robotic application, moving RAM into the `application_running` state. - The application executes, and RAM handles its process management, including monitoring and error handling. 5. **Pausing and Resuming Application**: - - The client can send `pause` and `resume` commands to RAM to control the application's execution. - RAM transitions to the `paused` state when paused and returns to `application_running` upon resumption. 6. **Stopping the Application**: - - Finally, the client can send a `stop` command to halt the application. - RAM stops the application and transitions back to the `tools_ready` state, ready for new commands. diff --git a/robotics_application_manager/manager/launcher/__init__.py b/robotics_application_manager/manager/launcher/__init__.py index 39ed308..a31448e 100644 --- a/robotics_application_manager/manager/launcher/__init__.py +++ b/robotics_application_manager/manager/launcher/__init__.py @@ -1,3 +1,3 @@ from .launcher_tools import LauncherTools -from .launcher_world import LauncherWorld +from .launcher_scene import LauncherScene from .launcher_robot import LauncherRobot diff --git a/robotics_application_manager/manager/launcher/launcher_world.py b/robotics_application_manager/manager/launcher/launcher_scene.py similarity index 92% rename from robotics_application_manager/manager/launcher/launcher_world.py rename to robotics_application_manager/manager/launcher/launcher_scene.py index 0c54041..afe2b9c 100644 --- a/robotics_application_manager/manager/launcher/launcher_world.py +++ b/robotics_application_manager/manager/launcher/launcher_scene.py @@ -48,7 +48,7 @@ } -class LauncherWorld(BaseModel): +class LauncherScene(BaseModel): type: str launch_file_path: str module: str = ".".join(__name__.split(".")[:-1]) @@ -62,7 +62,7 @@ def run(self): self.launchers.append(launcher) def terminate(self): - LogManager.logger.info("Terminating worlds launchers") + LogManager.logger.info("Terminating scenes launchers") if self.launchers: for launcher in self.launchers: launcher.terminate() @@ -87,6 +87,6 @@ def launch_command(self, configuration): pass -class LauncherWorldException(Exception): +class LauncherSceneException(Exception): def __init__(self, message): - super(LauncherWorldException, self).__init__(message) + super(LauncherSceneException, self).__init__(message) diff --git a/robotics_application_manager/manager/manager.py b/robotics_application_manager/manager/manager.py index 869fafa..69b018f 100644 --- a/robotics_application_manager/manager/manager.py +++ b/robotics_application_manager/manager/manager.py @@ -39,7 +39,7 @@ ) from robotics_application_manager.ram_logging import LogManager from robotics_application_manager.manager.launcher import ( - LauncherWorld, + LauncherScene, LauncherRobot, LauncherTools, ) @@ -121,10 +121,10 @@ class Manager: "before": "on_terminate_tools", }, { - "trigger": "terminate_universe", + "trigger": "terminate_world", "source": "world_ready", "dest": "connected", - "before": "on_terminate_universe", + "before": "on_terminate_world", }, # Global transitions { @@ -222,7 +222,7 @@ def __init__(self, host: str, port: int): self.ros_version = subprocess.check_output(["bash", "-c", "echo $ROS_DISTRO"]) self.queue = Queue() self.consumer = ManagerConsumer(host, port, self.queue) - self.world_launcher = None + self.scene_launcher = None self.world_type = None self.robot_launcher = None self.robot_config = None @@ -307,7 +307,7 @@ def on_launch_world(self, event): This method initializes the launch process based on the provided configuration. During the launch process, it validates and processes the configuration data received from the event. - It then creates and starts a LauncherWorld instance with the validated configuration. + It then creates and starts a LauncherScene instance with the validated configuration. This setup is crucial for preparing the environment and resources necessary for the application's execution. Parameters: @@ -322,30 +322,30 @@ def on_launch_world(self, event): The method logs the start of the launch transition and the configuration details for debugging and traceability. """ cfg_dict = event.kwargs.get("data", {}) - world_cfg = cfg_dict["world"] + scene_cfg = cfg_dict["scene"] robot_cfg = cfg_dict["robot"] - # Launch world + # Launch scene try: - if world_cfg["type"] == None: - self.world_launcher = None + if scene_cfg["type"] == None: + self.scene_launcher = None LogManager.logger.info("Launch transition finished") return - cfg = ConfigurationManager.validate(world_cfg) - if "zip" in world_cfg: - LogManager.logger.info("Launching universe from received zip") - self.prepare_custom_universe(world_cfg) + cfg = ConfigurationManager.validate(scene_cfg) + if "zip" in scene_cfg: + LogManager.logger.info("Launching scene from received zip") + self.prepare_custom_world(scene_cfg) else: - LogManager.logger.info("Launching world from the RB") + LogManager.logger.info("Launching scene from the RB") LogManager.logger.info(cfg) except ValueError as e: LogManager.logger.error(f"Configuration validation failed: {e}") - self.world_type = world_cfg["type"] + self.world_type = scene_cfg["type"] - self.world_launcher = LauncherWorld(**cfg.model_dump()) - LogManager.logger.info(str(self.world_launcher)) + self.scene_launcher = LauncherScene(**cfg.model_dump()) + LogManager.logger.info(str(self.scene_launcher)) # Launch robot self.robot_launcher = None @@ -361,21 +361,21 @@ def on_launch_world(self, event): self.robot_config = robot_cfg LogManager.logger.info(str(self.robot_launcher)) - self.world_launcher.run() + self.scene_launcher.run() if self.robot_launcher is not None: self.robot_launcher.run( robot_cfg["entity"], robot_cfg["start_pose"], robot_cfg["extra_config"] ) LogManager.logger.info("Launch transition finished") - def prepare_custom_universe(self, cfg_dict): + def prepare_custom_world(self, cfg_dict): """ - Prepare and extract a custom universe from a base64-encoded zip file. + Prepare and extract a custom world from a base64-encoded zip file. Then build it in the workspace. Parameters: - cfg_dict (dict): Config dictionary containing the universe name and zip data + cfg_dict (dict): Config dictionary containing the world name and zip data """ # Unzip the app if cfg_dict["zip"].startswith("data:"): @@ -383,22 +383,22 @@ def prepare_custom_universe(self, cfg_dict): else: zip_file = cfg_dict["zip"] - universe_ref = "/workspace/worlds/src/" + cfg_dict["name"] + world_ref = "/workspace/worlds/src/" + cfg_dict["name"] # Remove old content if os.path.exists("/workspace/worlds"): shutil.rmtree("/workspace/worlds", ignore_errors=False) # Create the folder if it doesn't exist - universe_folder = universe_ref + "/" - if not os.path.exists(universe_folder): - os.makedirs(universe_folder) + world_folder = world_ref + "/" + if not os.path.exists(world_folder): + os.makedirs(world_folder) - zip_destination = universe_ref + ".zip" + zip_destination = world_ref + ".zip" with open(zip_destination, "wb") as result: result.write(base64.b64decode(zip_file)) zip_ref = zipfile.ZipFile(zip_destination, "r") - zip_ref.extractall(universe_folder + "/") + zip_ref.extractall(world_folder + "/") zip_ref.close() os.system( @@ -877,9 +877,9 @@ def on_terminate_tools(self, event): self.tools_launcher.terminate() self.tools_launcher = None - def on_terminate_universe(self, event): + def on_terminate_world(self, event): """ - Handle the 'terminate_universe' event. + Handle the 'terminate_world' event. Terminates the world and robot launchers if they exist and terminates related Harmonic processes. @@ -887,9 +887,9 @@ def on_terminate_universe(self, event): Parameters: event: The event object associated with the termination request. """ - if self.world_launcher is not None: - self.world_launcher.terminate() - self.world_launcher = None + if self.scene_launcher is not None: + self.scene_launcher.terminate() + self.scene_launcher = None self.world_type = None if self.robot_launcher is not None: self.robot_launcher.terminate() @@ -922,11 +922,11 @@ def on_disconnect(self, event): except Exception as e: LogManager.logger.exception("Exception terminating robot launcher") - if self.world_launcher: + if self.scene_launcher: try: - self.world_launcher.terminate() + self.scene_launcher.terminate() except Exception as e: - LogManager.logger.exception("Exception terminating world launcher") + LogManager.logger.exception("Exception terminating scene launcher") def process_message(self, message): if message.command == "gui": @@ -1021,7 +1021,7 @@ def reset_sim(self): self.robot_config["extra_config"], ) except Exception as e: - LogManager.logger.exception("Exception terminating world launcher") + LogManager.logger.exception("Exception terminating scene launcher") def start(self): """ @@ -1066,11 +1066,11 @@ def signal_handler(sign, frame): except Exception as e: LogManager.logger.exception("Exception terminating robot launcher") - if self.world_launcher: + if self.scene_launcher: try: - self.world_launcher.terminate() + self.scene_launcher.terminate() except Exception as e: - LogManager.logger.exception("Exception terminating world launcher") + LogManager.logger.exception("Exception terminating scene launcher") exit() diff --git a/test/conftest.py b/test/conftest.py index b2df605..ef1e736 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -112,8 +112,8 @@ def dummy_run(self, start_pose=None, extra_config=None): monkeypatch.setattr("os.makedirs", lambda path, exist_ok=False: None) monkeypatch.setattr("os.path.isdir", lambda path: True) - # Patch LauncherWorld to avoid launching real processes - class DummyLauncherWorld: + # Patch LauncherScene to avoid launching real processes + class DummyLauncherScene: def __init__(self, *a, **k): self.launched = False @@ -129,7 +129,7 @@ def terminate(self): pass monkeypatch.setattr( - "robotics_application_manager.manager.manager.LauncherWorld", DummyLauncherWorld + "robotics_application_manager.manager.manager.LauncherScene", DummyLauncherScene ) class DummyFileWatchdog: diff --git a/test/test_connected_to_world_ready.py b/test/test_connected_to_world_ready.py index 1e85686..a78e9b3 100644 --- a/test/test_connected_to_world_ready.py +++ b/test/test_connected_to_world_ready.py @@ -61,14 +61,14 @@ def test_connected_to_world_ready(manager, monkeypatch): # # Simulate logging error, but return a dummy config to avoid UnboundLocalError # return DummyConfig() -# def fake_prepare_custom_universe(cfg): +# def fake_prepare_custom_world(cfg): # raise ValueError("Invalid world configuration") # monkeypatch.setattr( # "robotics_application_manager.libs.launch_world_model.ConfigurationManager.validate", # fake_validate, # ) -# manager.prepare_custom_universe = fake_prepare_custom_universe +# manager.prepare_custom_world = fake_prepare_custom_world # event_data = {"world": invalid_world_cfg, "robot": valid_robot_cfg} diff --git a/test/test_terminate_transitions.py b/test/test_terminate_transitions.py index 4c2a1a4..aecbba1 100644 --- a/test/test_terminate_transitions.py +++ b/test/test_terminate_transitions.py @@ -116,8 +116,8 @@ def test_terminate_tools_invalid_machine_error(manager, monkeypatch): assert manager.state == "application_running" -def test_terminate_universe_valid(manager, monkeypatch): - """Test the valid terminate_universe transition in the Manager.""" +def test_terminate_world_valid(manager, monkeypatch): + """Test the valid terminate_world transition in the Manager.""" # Ensure the manager is in a state where it can stop setup_manager_to_world_ready(manager, monkeypatch) # Mock needed methods and attributes @@ -125,14 +125,14 @@ def test_terminate_universe_valid(manager, monkeypatch): manager.terminate_harmonic_processes = lambda: None # Trigger the stop transition - manager.trigger("terminate_universe") + manager.trigger("terminate_world") # Check that the state has changed to 'connected' assert manager.state == "connected" -def test_terminate_universe_invalid_machine_error(manager, monkeypatch): +def test_terminate_world_invalid_machine_error(manager, monkeypatch): """ - Test the invalid terminate_universe transition in the Manager. + Test the invalid terminate_world transition in the Manager. Ensure that the transition raises an error when executed from an invalid state. """ @@ -141,6 +141,6 @@ def test_terminate_universe_invalid_machine_error(manager, monkeypatch): # Trigger the stop transition with pytest.raises(MachineError): - manager.trigger("terminate_universe") + manager.trigger("terminate_world") # Check that the state has not changed assert manager.state == "application_running" diff --git a/test/test_utils.py b/test/test_utils.py index 50e220e..8da8cd4 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -43,7 +43,7 @@ def setup_manager_to_world_ready(manager, monkeypatch): # State should now be 'world_ready' assert manager.state == "world_ready" - # Patch LauncherWorld to avoid starting real worlds + # Patch LauncherScene to avoid starting real worlds class DummyConsumer: def __init__(self): self.launched = False @@ -51,7 +51,7 @@ def __init__(self): def consume(self, *args, **kwargs): pass - class DummyLauncherWorld: + class DummyLauncherScene: def __init__(self, *args, **kwargs): self.launched = False