From 2e79d054bb525afe524c6a63bde8c0ab7e52aba1 Mon Sep 17 00:00:00 2001 From: Raffaello Bonghi Date: Thu, 23 Jan 2025 16:04:00 +0000 Subject: [PATCH] Improve docker control --- src/nanosaur/docker.py | 20 +++++------ src/nanosaur/main.py | 6 +++- src/nanosaur/ros.py | 23 ++++++++---- src/nanosaur/simulation.py | 7 ++-- src/nanosaur/utilities.py | 72 ++++++++++++++++++++++---------------- src/nanosaur/workspace.py | 11 +++--- 6 files changed, 79 insertions(+), 60 deletions(-) diff --git a/src/nanosaur/docker.py b/src/nanosaur/docker.py index 6d8a29f..694bc41 100644 --- a/src/nanosaur/docker.py +++ b/src/nanosaur/docker.py @@ -92,9 +92,6 @@ def docker_start(platform, params: Params, args): print(TerminalFormatter.color_text(f"robot {robot.name} starting", color='green')) # Create a DockerClient object with the docker-compose file nanosaur_compose = DockerClient(compose_files=[docker_compose_path]) - if args.build: - print(TerminalFormatter.color_text("Building the Docker container...", color='green')) - nanosaur_compose.compose.build() # Start the container in detached mode try: nanosaur_compose.compose.up(detach=args.detach) @@ -115,27 +112,26 @@ def docker_simulator_start(platform, params: Params, args): nanosaur_home_path = get_nanosaur_home() # Create the full file path docker_compose_path = os.path.join(nanosaur_home_path, docker_compose) - robot = RobotList.get_robot(params) # Check which simulation tool is selected if 'simulation_tool' not in params: print(TerminalFormatter.color_text("No simulation tool selected. Please run simulation set first.", color='red')) return False - + # Start the container in detached mode + simulation_tool = params['simulation_tool'].lower().replace(' ', '_') # Create a DockerClient object with the docker-compose file nanosaur_compose = DockerClient(compose_files=[docker_compose_path]) - if len(nanosaur_compose.compose.ps()) > 0: - print(TerminalFormatter.color_text(f"The robot {robot.name} is already running.", color='red')) - return False + + #if len(nanosaur_compose.compose.ps()) > 0: + # print(TerminalFormatter.color_text(f"The robot {robot.name} is already running.", color='red')) + # return False # Build env file if not is_env_file(): print(TerminalFormatter.color_text("Creating the environment file...", color='green')) build_env_file(params) - print(TerminalFormatter.color_text(f"robot {robot.name} starting", color='green')) - # Start the container in detached mode - simulation_tool = params['simulation_tool'].lower().replace(' ', '_') + print(TerminalFormatter.color_text(f"Simulator {simulation_tool} starting", color='green')) try: - nanosaur_compose.compose.up(services=[f'nanosaur_{simulation_tool}']) + nanosaur_compose.compose.up(services=[f'nanosaur_{simulation_tool}'], recreate=False) except DockerException as e: print(TerminalFormatter.color_text(f"Error starting the simulation tool: {e}", color='red')) return False diff --git a/src/nanosaur/main.py b/src/nanosaur/main.py index 2ffc389..647ddbf 100644 --- a/src/nanosaur/main.py +++ b/src/nanosaur/main.py @@ -155,7 +155,8 @@ def main(): # Create the argument parser parser = argparse.ArgumentParser( description="Nanosaur CLI - A command-line interface for the Nanosaur package.") - + + parser.add_argument('--mode', type=str, help="Specify the mode of operation") # Define subcommands subparsers = parser.add_subparsers(dest='command', help="Available commands") @@ -200,6 +201,9 @@ def main(): # Parse the arguments args = parser.parse_args() + # Override mode if provided as an argument + if args.mode: + params.set('mode', args.mode, save=False) # Handle subcommands without a specific type if args.command in ['workspace', 'ws'] and not args.workspace_type: diff --git a/src/nanosaur/ros.py b/src/nanosaur/ros.py index 21a4e7c..7f8d66f 100644 --- a/src/nanosaur/ros.py +++ b/src/nanosaur/ros.py @@ -43,6 +43,9 @@ ISAAC_ROS_DISTRO_SUFFIX = "ros2_humble" NANOSAUR_DOCKERFILE_SUFFIX = "nanosaur" +NANOSAUR_DOCKER_PACKAGE_ROBOT = "nanosaur" +NANOSAUR_DOCKER_PACKAGE_SIMULATION = "simulation" +NANOSAUR_DOCKER_PACKAGE_PERCEPTION = "perception" def run_dev_script(params, host_workspace_path, workspace_path): @@ -262,27 +265,29 @@ def run_colcon_build(folder_path) -> bool: return False -def deploy_docker_simulation(image_name: str, simulation_ws_path: str, all: bool) -> bool: +def deploy_docker_simulation(docker_user: str, simulation_ws_path: str, all: bool) -> bool: # Get the path to the nanosaur_simulations package nanosaur_simulations_path = os.path.join(simulation_ws_path, 'src', 'nanosaur_simulations') # Build Gazebo sim docker + tag_image = f"{docker_user}/{NANOSAUR_DOCKER_PACKAGE_SIMULATION}:gazebo" try: - print(TerminalFormatter.color_text("Building Gazebo Docker image...", color='magenta', bold=True)) + print(TerminalFormatter.color_text(f"Building Gazebo simulation docker image {tag_image}", color='magenta', bold=True)) docker.build( get_nanosaur_home(), file=f"{nanosaur_simulations_path}/Dockerfile.gazebo", - tags="nanosaur/gazebo" + tags=tag_image ) except DockerException as e: print(TerminalFormatter.color_text(f"Error building Gazebo Docker image: {e}", color='red')) return False # Build the Docker image for nanosaur bridge + tag_image = f"{docker_user}/{NANOSAUR_DOCKER_PACKAGE_ROBOT}:simulation" try: - print(TerminalFormatter.color_text("Building Nanosaur Docker image...", color='magenta', bold=True)) + print(TerminalFormatter.color_text(f"Building Nanosaur robot docker image {tag_image}", color='magenta', bold=True)) docker.build( get_nanosaur_home(), file=f"{nanosaur_simulations_path}/Dockerfile.nanosaur", - tags=image_name + tags=tag_image ) except DockerException as e: print(TerminalFormatter.color_text(f"Error building Nanosaur Docker image: {e}", color='red')) @@ -292,20 +297,24 @@ def deploy_docker_simulation(image_name: str, simulation_ws_path: str, all: bool return True -def deploy_docker_perception(image_name: str, perception_ws_path: str) -> bool: +def deploy_docker_perception(docker_user: str, perception_ws_path: str) -> bool: nanosaur_perception_path = os.path.join(perception_ws_path, 'src', 'nanosaur_perception') src_folders = [ os.path.join(get_nanosaur_home(), 'shared_src'), os.path.join(perception_ws_path, 'src') ] + + tag_image = f"{docker_user}/{NANOSAUR_DOCKER_PACKAGE_PERCEPTION}:simulation" + print(TerminalFormatter.color_text(f"Building Nanosaur robot docker image {tag_image}", color='magenta', bold=True)) try: os.chdir(nanosaur_perception_path) print(f"Changed directory to: {nanosaur_perception_path}") ws_dir_list = '--ws-src ' + ' --ws-src '.join(src_folders) - command = f"scripts/docker_build.sh {ws_dir_list} --image-name {image_name}" + + command = f"scripts/docker_build.sh {ws_dir_list} --image-name {tag_image}" process = subprocess.Popen( command, diff --git a/src/nanosaur/simulation.py b/src/nanosaur/simulation.py index 16e67bd..84dd23d 100644 --- a/src/nanosaur/simulation.py +++ b/src/nanosaur/simulation.py @@ -161,10 +161,11 @@ def simulation_start_debug(platform, params: Params, args): def simulation_start(platform, params: Params, args): - if args.debug: + debug_mode = args.debug or params.get('mode', '') in ['Maintainer', 'Raffo'] + if debug_mode: return simulation_start_debug(platform, params, args) - else: - return docker_simulator_start(platform, params, args) + # Run from docker container + return docker_simulator_start(platform, params, args) def simulation_set(platform, params: Params, args): diff --git a/src/nanosaur/utilities.py b/src/nanosaur/utilities.py index c8de8dd..bf2ee9b 100644 --- a/src/nanosaur/utilities.py +++ b/src/nanosaur/utilities.py @@ -51,6 +51,7 @@ NANOSAUR_MAIN_GITHUB_URL = 'https://github.com/rnanosaur/nanosaur.git' NANOSAUR_MAIN_BRANCH = 'nanosaur2' +NANOSAUR_DOCKER_USER = 'nanosaur' class Robot: @@ -224,30 +225,6 @@ def print_all_robots(self, robot_idx=None): print(f" {TerminalFormatter.color_text(f'Robot {idx}:', bold=True)} {robot}") -def is_env_file(): - nanosaur_home_path = get_nanosaur_home() - env_path = os.path.join(nanosaur_home_path, '.env') - return os.path.exists(env_path) - - -def build_env_file(params): - nanosaur_home_path = get_nanosaur_home() - env_path = os.path.join(nanosaur_home_path, '.env') - # Get current robot running - robot = RobotList.get_robot(params) - uid = os.getuid() - gid = os.getgid() - # Create a .env file and save UID and GID - with open(env_path, 'w') as env_file: - env_file.write(f"USER_UID={uid}\n") - env_file.write(f"USER_GID={gid}\n") - # Check which simulation tool is selected and save it in the .env file - simulation_tool = params['simulation_tool'].lower().replace(' ', '_') - env_file.write(f"SIMULATION={simulation_tool}\n") - # Pass robot ros commands - env_file.write(f"COMMANDS={robot.config_to_ros()}\n") - - class Params: @classmethod @@ -310,28 +287,63 @@ def get_params_file() -> str: def get(self, key, default=None): return getattr(self, key, default) - def set(self, key, value): + def set(self, key, value, save=True): + self._params_dict[key] = value setattr(self, key, value) # save the new value in the file - self.save() + if save: + self.save() return value def items(self): return self._params_dict.items() +def is_env_file(): + nanosaur_home_path = get_nanosaur_home() + env_path = os.path.join(nanosaur_home_path, '.env') + return os.path.exists(env_path) + + +def build_env_file(params): + nanosaur_home_path = get_nanosaur_home() + env_path = os.path.join(nanosaur_home_path, '.env') + # Get current robot running + robot = RobotList.get_robot(params) + uid = os.getuid() + gid = os.getgid() + # Create a .env file and save UID and GID + with open(env_path, 'w') as env_file: + env_file.write(f"USER_UID={uid}\n") + env_file.write(f"USER_GID={gid}\n") + # Check which simulation tool is selected and save it in the .env file + simulation_tool = params['simulation_tool'].lower().replace(' ', '_') + env_file.write(f"SIMULATION={simulation_tool}\n") + # Pass robot ros commands + env_file.write(f"COMMANDS={robot.config_to_ros()}\n") + + def package_info(params: Params, verbose: bool): # Print version information nanosaur_website = TerminalFormatter.clickable_link(NANOSAUR_WEBSITE_URL) print(f"{TerminalFormatter.color_text('Nanosaur website:', bold=True)} {nanosaur_website}") nanosaur_home_folder = TerminalFormatter.clickable_link(get_nanosaur_home()) print(f"{TerminalFormatter.color_text('Nanosaur home:', bold=True)} {nanosaur_home_folder}") - if verbose: - print(f"{TerminalFormatter.color_text('Nanosaur package:', bold=True)} {__version__}") - nanosaur_branch = params.get('nanosaur_branch', NANOSAUR_MAIN_BRANCH) - print(f"{TerminalFormatter.color_text('Nanosaur version (branch):', bold=True)} {nanosaur_branch}") + # Print verbose information + def print_verbose_info(params): + nanosaur_docker_user = get_nanosaur_docker_user(params) + nanosaur_docker_home = TerminalFormatter.clickable_link(f"https://hub.docker.com/u/{nanosaur_docker_user}") + print(f"{TerminalFormatter.color_text('Nanosaur Docker Hub:', bold=True)} {nanosaur_docker_home}") config_file_path = TerminalFormatter.clickable_link(Params.get_params_file()) print(f"{TerminalFormatter.color_text('Nanosaur config file:', bold=True)} {config_file_path}") + nanosaur_branch = params.get('nanosaur_branch', NANOSAUR_MAIN_BRANCH) + print(f"{TerminalFormatter.color_text('Nanosaur version (branch):', bold=True)} {nanosaur_branch}") + print(f"{TerminalFormatter.color_text('Nanosaur package:', bold=True)} {__version__}") + if verbose: + print_verbose_info(params) + +def get_nanosaur_docker_user(params: Params) -> str: + return params.get('nanosaur_docker_user', NANOSAUR_DOCKER_USER) def get_nanosaur_raw_github_url(params: Params) -> str: diff --git a/src/nanosaur/workspace.py b/src/nanosaur/workspace.py index 69222a8..93df034 100644 --- a/src/nanosaur/workspace.py +++ b/src/nanosaur/workspace.py @@ -30,7 +30,7 @@ from nanosaur.prompt_colors import TerminalFormatter from nanosaur import ros from nanosaur.simulation import simulation_robot_start_debug -from nanosaur.utilities import Params, get_nanosaur_raw_github_url, get_nanosaur_home, create_nanosaur_home, require_sudo_password +from nanosaur.utilities import Params, get_nanosaur_raw_github_url, get_nanosaur_home, create_nanosaur_home, require_sudo_password, get_nanosaur_docker_user import inquirer @@ -48,10 +48,6 @@ DEFAULT_WORKSPACE_ROBOT = 'robot_ws' DEFAULT_WORKSPACE_DEVELOPER = 'ros_ws' -DEFAULT_DOCKER_PERCEPTION_IMAGE = 'nanosaur/perception' -DEFAULT_DOCKER_SIMULATION_IMAGE = 'nanosaur/simulation' -DEFAULT_DOCKER_ROBOT_IMAGE = 'nanosaur/nanosaur' - def workspaces_info(params: Params, verbose: bool): """Print information about the workspaces.""" @@ -258,10 +254,11 @@ def debug(platform, params: Params, args): def deploy(platform, params: Params, args): + nanosaur_docker_user = get_nanosaur_docker_user(params) """ Deploy the workspace """ workspace_actions = { - 'simulation': lambda: ros.deploy_docker_simulation(DEFAULT_DOCKER_SIMULATION_IMAGE, get_workspace_path(params, 'ws_simulation_name'), args.all), - 'perception': lambda: ros.deploy_docker_perception(DEFAULT_DOCKER_PERCEPTION_IMAGE, get_workspace_path(params, 'ws_perception_name')), + 'simulation': lambda: ros.deploy_docker_simulation(nanosaur_docker_user, get_workspace_path(params, 'ws_simulation_name'), args.all), + 'perception': lambda: ros.deploy_docker_perception(nanosaur_docker_user, get_workspace_path(params, 'ws_perception_name')), } if args.all: print(TerminalFormatter.color_text("Deploying all workspaces", bold=True))