From 9376f1dc5e90d5f86fa2f9b9f6b965b211715306 Mon Sep 17 00:00:00 2001 From: Raffaello Bonghi Date: Mon, 27 Jan 2025 11:24:38 +0000 Subject: [PATCH] Improve info page --- src/nanosaur/docker.py | 8 +-- src/nanosaur/main.py | 36 +++++----- src/nanosaur/prompt_colors.py | 4 ++ src/nanosaur/robot.py | 123 +++++++++++++++++++++++----------- src/nanosaur/simulation.py | 49 +++++++------- src/nanosaur/swarm.py | 48 ++++++------- src/nanosaur/utilities.py | 21 +++--- src/nanosaur/workspace.py | 10 ++- 8 files changed, 178 insertions(+), 121 deletions(-) diff --git a/src/nanosaur/docker.py b/src/nanosaur/docker.py index 73c851a..8985909 100644 --- a/src/nanosaur/docker.py +++ b/src/nanosaur/docker.py @@ -50,7 +50,7 @@ def docker_service_run_command(platform, params: Params, service, command=None, docker_compose = f"docker-compose.{workspace_type}.yml" nanosaur_home_path = get_nanosaur_home() # Create the full file path - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) docker_compose_path = os.path.join(nanosaur_home_path, docker_compose) env_file_path = os.path.join(nanosaur_home_path, f'{robot.name}.env') @@ -78,7 +78,7 @@ def docker_robot_start(platform, params: Params, args): docker_compose = f"docker-compose.{workspace_type}.yml" nanosaur_home_path = get_nanosaur_home() # Create the full file path - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) docker_compose_path = os.path.join(nanosaur_home_path, docker_compose) env_file_path = os.path.join(nanosaur_home_path, f'{robot.name}.env') @@ -119,7 +119,7 @@ def docker_simulator_start(platform, params: Params, args): docker_compose = f"docker-compose.{workspace_type}.yml" nanosaur_home_path = get_nanosaur_home() # Create the full file path - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) docker_compose_path = os.path.join(nanosaur_home_path, docker_compose) env_file_path = os.path.join(nanosaur_home_path, f'{robot.name}.env') @@ -155,7 +155,7 @@ def docker_robot_stop(platform, params: Params, args): docker_compose = f"docker-compose.{workspace_type}.yml" nanosaur_home_path = get_nanosaur_home() # Create the full file path - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) docker_compose_path = os.path.join(nanosaur_home_path, docker_compose) env_file_path = os.path.join(nanosaur_home_path, f'{robot.name}.env') diff --git a/src/nanosaur/main.py b/src/nanosaur/main.py index f259893..42f2578 100644 --- a/src/nanosaur/main.py +++ b/src/nanosaur/main.py @@ -88,6 +88,7 @@ def info(platform, params: Params, args): """Print version information.""" + device_type = "robot" if platform['Machine'] == 'jetson' else "desktop" # Print version information package_info(params, args.verbose) # Print mode if it exists in params @@ -102,17 +103,22 @@ def info(platform, params: Params, args): print(f"{TerminalFormatter.color_text('Default debug: ', bold=True)} {debug_string}") # Load the robot list robot_list = RobotList.load(params) + robot_idx = params.get('robot_idx', 0) # Print current robot configuration - robot_data = RobotList.get_robot(params) print() - robot_data.verbose() + if robot_list.robots: + robot_data = robot_list.get_robot(robot_idx) + robot_data.verbose() + else: + print(TerminalFormatter.color_text("No robot configuration found", color='red')) # Print other robots if they exist if len(robot_list.robots) > 1 or args.verbose: print() - robot_list.print_all_robots(params.get('robot_idx', 0)) + robot_list.print_all_robots(robot_idx) # Print simulation tools if they exist - print() - simulation_info(platform, params, args.verbose) + if device_type == 'desktop': + print() + simulation_info(platform, params, args.verbose) # Print installed workspaces workspaces_info(params, args.verbose) # Print all robot configurations @@ -152,6 +158,9 @@ def install(platform, params: Params, args): print(TerminalFormatter.color_text(f"Installing {install_type} workspace...", bold=True)) if not NANOSAUR_INSTALL_OPTIONS_RULES[install_type]['function'](platform, params, args): return False + # Initialize the robot configuration if it doesn't exist + if 'robots' not in params: + wizard(platform, params, args) # Set params in maintainer mode current_mode = params.get('mode') if ( @@ -171,7 +180,7 @@ def nanosaur_wake_up(platform, params: Params, args): def robot_control(params, subparsers): - robot = RobotList.get_robot(params).name + robot = RobotList.current_robot(params).name robot_name = TerminalFormatter.color_text(robot, color='green', bold=True) parser_wakeup = subparsers.add_parser('wake-up', help=f"Start {robot_name} (same as 'nanosaur robot start')") parser_wakeup.set_defaults(func=nanosaur_wake_up) @@ -202,6 +211,9 @@ def main(): parser = argparse.ArgumentParser( description="Nanosaur CLI - A command-line interface for the Nanosaur package.") # Add arguments + parser.add_argument('--mode', type=str, help="Specify the mode of operation") + if ros2_installed is not None: + parser.add_argument('--default-debug', '-dd', type=str, choices=['host', 'docker'], help="Select the debug mode if on host or in docker") parser.add_argument( "--log-level", type=str, @@ -209,9 +221,6 @@ def main(): default="INFO", help="Set the logging level (default: INFO)", ) - parser.add_argument('--mode', type=str, help="Specify the mode of operation") - if ros2_installed is not None: - parser.add_argument('--default-debug', '-dd', type=str, choices=['host', 'docker'], help="Select the debug mode if on host or in docker") # Define subcommands subparsers = parser.add_subparsers(dest='command', help="Available commands") @@ -250,7 +259,7 @@ def main(): parser_swarm = parser_swarm_menu(subparsers, params) # Subcommand: wakeup (with a sub-menu for wakeup operations) - if 'mode' in params: + if 'mode' in params and 'robots' in params: robot_control(params, subparsers) # Enable tab completion @@ -267,9 +276,6 @@ def main(): # Override mode if provided as an argument if args.mode: params.set('mode', args.mode, save=False) - - wizard(platform, params, args) - exit(0) # Print all arguments if hasattr(args, 'default_debug') and args.default_debug is not None: @@ -282,9 +288,9 @@ def main(): parser_workspace.print_help() elif args.command in ['simulation', 'sim'] and not args.simulation_type: parser_simulation.print_help() - elif args.command == 'robot' and not args.robot_type: + elif args.command == 'robot' and 'robot_type' in args and not args.robot_type: parser_robot.print_help() - elif args.command == 'robot' and args.robot_type == 'config' and not args.config_type: + elif args.command == 'robot' and 'robot_type' in args and args.robot_type == 'config' and not args.config_type: parser_config.print_help() elif args.command == 'swarm' and not args.swarm_type: parser_swarm.print_help() diff --git a/src/nanosaur/prompt_colors.py b/src/nanosaur/prompt_colors.py index 4b38f89..85a4ec0 100644 --- a/src/nanosaur/prompt_colors.py +++ b/src/nanosaur/prompt_colors.py @@ -24,6 +24,10 @@ # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os +import logging + +# Set up the logger +logger = logging.getLogger(__name__) class TerminalFormatter: diff --git a/src/nanosaur/robot.py b/src/nanosaur/robot.py index d6e5736..d13e1b0 100644 --- a/src/nanosaur/robot.py +++ b/src/nanosaur/robot.py @@ -32,32 +32,37 @@ from nanosaur import workspace from nanosaur import docker from nanosaur.prompt_colors import TerminalFormatter -from nanosaur.utilities import Params, RobotList +from nanosaur.utilities import Params, RobotList, Robot from nanosaur.utilities import ENGINES_CHOICES, CAMERA_CHOICES, LIDAR_CHOICES # Set up the logger logger = logging.getLogger(__name__) def add_robot_config_subcommands(subparsers: argparse._SubParsersAction, params: Params) -> argparse.ArgumentParser: - robot_data = RobotList.get_robot(params) + # Get the robot data + robot_data = RobotList.current_robot(params) parser_robot_config = subparsers.add_parser('config', help=f"Configure the robot settings [{robot_data.name}]") config_subparsers = parser_robot_config.add_subparsers(dest='config_type', help="Configuration options") # Add robot name subcommand - parser_robot_name = config_subparsers.add_parser('name', help=f"Change the robot name [{robot_data.name}]") + parser_robot_name = config_subparsers.add_parser('name', help=f"Set robot name [{robot_data.name}]") parser_robot_name.set_defaults(func=robot_set_name) # Add robot domain id subcommand - parser_robot_domain_id = config_subparsers.add_parser('domain_id', help=f"Change the robot domain ID [{robot_data.domain_id}]") + parser_robot_domain_id = config_subparsers.add_parser('domain_id', help=f"Set robot domain ID [{robot_data.domain_id}]") parser_robot_domain_id.set_defaults(func=robot_set_domain_id) + # Add robot simulation subcommand + parser_robot_simulation = config_subparsers.add_parser('simulation', help=f"Set robot real or simulation [{'simulation' if robot_data.simulation else 'real'}]") + parser_robot_simulation.set_defaults(func=robot_set_simulation) # Add robot camera subcommand - parser_robot_camera = config_subparsers.add_parser('camera', help=f"Change the robot camera type [{robot_data.camera_type}]") + parser_robot_camera = config_subparsers.add_parser('camera', help=f"Set robot camera type [{robot_data.camera_type or 'NOT SELECTED'}]") parser_robot_camera.add_argument('--new', type=str, help=f"Specify the new camera type (options: {', '.join(CAMERA_CHOICES)})") parser_robot_camera.set_defaults(func=robot_set_camera) # Add robot lidar subcommand - parser_robot_lidar = config_subparsers.add_parser('lidar', help=f"Change the robot lidar type [{robot_data.lidar_type}]") + parser_robot_lidar = config_subparsers.add_parser('lidar', help=f"Set robot lidar type [{robot_data.lidar_type or 'NOT SELECTED'}]") parser_robot_lidar.add_argument('--new', type=str, choices=LIDAR_CHOICES, help=f"Specify the new lidar type (options: {', '.join(LIDAR_CHOICES)})") parser_robot_lidar.set_defaults(func=robot_set_lidar) # Add robot engines subcommand - parser_robot_engines = config_subparsers.add_parser('engines', help=f"Configure the robot engines [{', '.join(robot_data.engines)}]") + engines_help = f"Configure robot engines [{', '.join(robot_data.engines) if robot_data.engines else 'NO ENGINES'}]" + parser_robot_engines = config_subparsers.add_parser('engines', help=engines_help) parser_robot_engines.add_argument('--new', type=str, help="Specify the new engine configuration") parser_robot_engines.set_defaults(func=robot_configure_engines) # Add robot reset subcommand @@ -68,42 +73,59 @@ def add_robot_config_subcommands(subparsers: argparse._SubParsersAction, params: def parser_robot_menu(subparsers: argparse._SubParsersAction, params: Params) -> argparse.ArgumentParser: - robot_data = RobotList.get_robot(params) - parser_robot = subparsers.add_parser('robot', help=f"Manage the Nanosaur robot [{robot_data.name}]") - robot_subparsers = parser_robot.add_subparsers(dest='robot_type', help="Robot operations") - # Add robot display subcommand - parser_robot_display = robot_subparsers.add_parser('display', help="Show the robot") - parser_robot_display.set_defaults(func=robot_display) - # Add robot drive subcommand - parser_robot_drive = robot_subparsers.add_parser('drive', help="Control the robot") - parser_robot_drive.set_defaults(func=control_keyboard) - # Add robot start subcommand - parser_robot_start = robot_subparsers.add_parser('start', help="Activate the robot") - parser_robot_start.add_argument( - '--profile', type=str, help="Specify the profile name to use") - parser_robot_start.add_argument( - '--detach', action='store_true', help="Run the robot in detached mode") - parser_robot_start.set_defaults(func=docker.docker_robot_start) - # Add robot stop subcommand - parser_robot_stop = robot_subparsers.add_parser('stop', help="Deactivate the robot") - parser_robot_stop.set_defaults(func=docker.docker_robot_stop) - - # Add robot config subcommand - parser_config = add_robot_config_subcommands(robot_subparsers, params) - - return parser_robot, parser_config + try: + robot_data = RobotList.current_robot(params) + parser_robot = subparsers.add_parser('robot', help=f"Manage the Nanosaur robot [{robot_data.name}]") + robot_subparsers = parser_robot.add_subparsers(dest='robot_type', help="Robot operations") + # Add robot display subcommand + parser_robot_display = robot_subparsers.add_parser('display', help="Show the robot") + parser_robot_display.set_defaults(func=robot_display) + # Add robot drive subcommand + parser_robot_drive = robot_subparsers.add_parser('drive', help="Control the robot") + parser_robot_drive.set_defaults(func=control_keyboard) + # Add robot start subcommand + parser_robot_start = robot_subparsers.add_parser('start', help="Activate the robot") + parser_robot_start.add_argument( + '--profile', type=str, help="Specify the profile name to use") + parser_robot_start.add_argument( + '--detach', action='store_true', help="Run the robot in detached mode") + parser_robot_start.set_defaults(func=docker.docker_robot_start) + # Add robot stop subcommand + parser_robot_stop = robot_subparsers.add_parser('stop', help="Deactivate the robot") + parser_robot_stop.set_defaults(func=docker.docker_robot_stop) + + # Add robot config subcommand + parser_config = add_robot_config_subcommands(robot_subparsers, params) + + return parser_robot, parser_config + except IndexError: + # No robot configured start the wizard + parser_robot = subparsers.add_parser('robot', help="Initialize a new Nanosaur robot") + parser_robot.set_defaults(func=wizard) + return parser_robot, None + def wizard(platform, params: Params, args): + # Get the device type (robot or desktop) + device_type = "robot" if platform['Machine'] == 'jetson' else "desktop" + # Build the list of functions to run + functions_list = [robot_set_name] + [robot_set_simulation] if device_type == 'desktop' else [] + functions_list += [robot_set_domain_id, robot_set_camera, robot_set_lidar, robot_configure_engines] + # Set new argument to None args.new = None - - for func in [robot_set_name, robot_set_domain_id, robot_set_camera, robot_set_lidar, robot_configure_engines]: + # Add a new robot + robot = Robot() + robot.simulation = device_type == 'desktop' + RobotList.add_robot(params, robot, save=False) + # Run the robot configuration wizard + for func in functions_list: if not func(platform, params, args): return False def robot_set_name(platform, params: Params, args): """Configure the robot name.""" - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) def validate_name(_, x): if not x.isalnum(): @@ -136,7 +158,7 @@ def validate_name(_, x): def robot_set_domain_id(platform, params: Params, args): """Configure the domain ID.""" - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) def validate_domain_id(_, x): if not x.isdigit(): @@ -164,10 +186,31 @@ def validate_domain_id(_, x): logger.debug(TerminalFormatter.color_text(f"Domain ID {new_domain_id} is already set", color='yellow')) return True +def robot_set_simulation(platform, params: Params, args): + """Configure the robot if is real or a simulation.""" + robot = RobotList.current_robot(params) + + question = [ + inquirer.List( + 'simulation', + message="Is a simulated robot o real robot", + choices=['simulation', 'real'], + default='simulation' if robot.simulation else 'real' + ) + ] + + answers = inquirer.prompt(question, theme=GreenPassion()) + if answers is None: + return False + is_simulation = answers['simulation'] == 'simulation' + robot.simulation = is_simulation + RobotList.update_robot(params, robot) + print(TerminalFormatter.color_text(f"Simulator set to: {answers['simulation']}", color='green')) + return True def robot_set_camera(platform, params: Params, args): """Configure the camera.""" - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) all_cameras = sorted(set(CAMERA_CHOICES + [robot.camera_type])) @@ -206,7 +249,7 @@ def robot_set_camera(platform, params: Params, args): def robot_set_lidar(platform, params: Params, args): """Configure the lidar.""" - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) all_lidars = sorted(set(LIDAR_CHOICES + [robot.lidar_type])) @@ -245,7 +288,7 @@ def robot_set_lidar(platform, params: Params, args): def robot_configure_engines(platform, params: Params, args): """Configure the robot engines.""" - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) if args.new is not None: if args.new not in robot.engines: @@ -292,7 +335,7 @@ def control_keyboard(platform, params: Params, args): if selected_location is None: return False # Get the robot - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) command = f"ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args --remap /cmd_vel:=/{robot.name}/key_vel" # Run from local machine if selected_location == 'host': @@ -319,7 +362,7 @@ def robot_display(platform, params: Params, args): if selected_location is None: return False # Get the robot - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) command = f"ros2 launch nanosaur_visualization robot_display.launch.py robot_name:={robot.name}" # Run from local machine if selected_location == 'host': diff --git a/src/nanosaur/simulation.py b/src/nanosaur/simulation.py index b8c5fa5..3c8c289 100644 --- a/src/nanosaur/simulation.py +++ b/src/nanosaur/simulation.py @@ -52,6 +52,29 @@ } +def parser_simulation_menu(subparsers: argparse._SubParsersAction, params: Params) -> argparse.ArgumentParser: + # Get the simulation tool from the parameters + simulation_type = params.get('simulation_tool', "NOT SELECTED") + # Add simulation subcommand + parser_simulation = subparsers.add_parser( + 'simulation', aliases=["sim"], help=f"Work with simulation tools [{simulation_type}]") + simulation_subparsers = parser_simulation.add_subparsers( + dest='simulation_type', help="Simulation types") + + # Add simulation start subcommand + parser_simulation_start = simulation_subparsers.add_parser( + 'start', help="Start the selected simulation") + parser_simulation_start.add_argument( + '--debug', action='store_true', help="Start the simulation in debug mode") + parser_simulation_start.set_defaults(func=simulation_start) + + # Add simulation set subcommand + parser_simulation_set = simulation_subparsers.add_parser( + 'set', help="Select the simulator you want to use") + parser_simulation_set.set_defaults(func=simulation_set) + return parser_simulation + + def find_all_isaac_sim(): # Path where Isaac Sim is usually installed base_path = os.path.expanduser("~/.local/share/ov/pkg") @@ -120,30 +143,6 @@ def simulation_info(platform, params: Params, verbose): if is_gazebo_installed(): print(TerminalFormatter.color_text(" Gazebo is installed", bold=True)) - -def parser_simulation_menu(subparsers: argparse._SubParsersAction, params: Params) -> argparse.ArgumentParser: - # Get the simulation tool from the parameters - simulation_type = params.get('simulation_tool', "NOT SELECTED") - # Add simulation subcommand - parser_simulation = subparsers.add_parser( - 'simulation', aliases=["sim"], help=f"Work with simulation tools [{simulation_type}]") - simulation_subparsers = parser_simulation.add_subparsers( - dest='simulation_type', help="Simulation types") - - # Add simulation start subcommand - parser_simulation_start = simulation_subparsers.add_parser( - 'start', help="Start the selected simulation") - parser_simulation_start.add_argument( - '--debug', action='store_true', help="Start the simulation in debug mode") - parser_simulation_start.set_defaults(func=simulation_start) - - # Add simulation set subcommand - parser_simulation_set = simulation_subparsers.add_parser( - 'set', help="Select the simulator you want to use") - parser_simulation_set.set_defaults(func=simulation_set) - return parser_simulation - - def simulation_robot_start_debug(params): nanosaur_ws_path = workspace.get_workspace_path(params, 'ws_simulation_name') bash_file = os.path.join(nanosaur_ws_path, 'install', 'setup.bash') @@ -158,7 +157,7 @@ def simulation_robot_start_debug(params): # Check if the simulation tool is valid and get the command command = simulation_tools[params['simulation_tool']]['robot'] # Load the robot configuration - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) print(TerminalFormatter.color_text(f"Starting {robot}", color='green')) # Print the command to be run diff --git a/src/nanosaur/swarm.py b/src/nanosaur/swarm.py index 35ad287..941d378 100644 --- a/src/nanosaur/swarm.py +++ b/src/nanosaur/swarm.py @@ -36,27 +36,29 @@ def parser_swarm_menu(subparsers: argparse._SubParsersAction, params: Params) -> argparse.ArgumentParser: # Get the robot index from the parameters - idx_swarm = params.get('robot_idx', 0) - current_robot_name = RobotList.load(params)._get_robot_by_idx(idx_swarm).name - # Subcommand: swarm (with a sub-menu for swarm operations) - parser_swarm = subparsers.add_parser('swarm', help="Manage swarm Nanosaur robots") - swarm_subparsers = parser_swarm.add_subparsers(dest='swarm_type', help="Robot operations") - # Add robot status subcommand - parser_robot_new = swarm_subparsers.add_parser('new', help="Get a new robot to control") - parser_robot_new.add_argument('name', type=str, nargs='?', help="New robot name") - parser_robot_new.set_defaults(func=robot_new) - # Add robot set subcommand - parser_robot_set = swarm_subparsers.add_parser('set', help=f"Set which robot to control [{current_robot_name}]") - parser_robot_set.add_argument('robot_name', type=str, nargs='?', help="Name of the robot to control") - parser_robot_set.set_defaults(func=robot_idx_set) - # Add robot remove subcommand - parser_robot_remove = swarm_subparsers.add_parser('remove', help="Remove a robot from the swarm") - parser_robot_remove.add_argument('robot_name', type=str, nargs='?', help="Name of the robot to remove") - parser_robot_remove.set_defaults(func=robot_remove) - # Add robot list subcommand - parser_robot_list = swarm_subparsers.add_parser('list', help="List all robots in the swarm") - parser_robot_list.set_defaults(func=robot_list) - return parser_swarm + try: + current_robot_name = RobotList.current_robot(params).name + # Subcommand: swarm (with a sub-menu for swarm operations) + parser_swarm = subparsers.add_parser('swarm', help="Manage swarm Nanosaur robots") + swarm_subparsers = parser_swarm.add_subparsers(dest='swarm_type', help="Robot operations") + # Add robot status subcommand + parser_robot_new = swarm_subparsers.add_parser('new', help="Get a new robot to control") + parser_robot_new.add_argument('name', type=str, nargs='?', help="New robot name") + parser_robot_new.set_defaults(func=robot_new) + # Add robot set subcommand + parser_robot_set = swarm_subparsers.add_parser('set', help=f"Set which robot to control [{current_robot_name}]") + parser_robot_set.add_argument('robot_name', type=str, nargs='?', help="Name of the robot to control") + parser_robot_set.set_defaults(func=robot_idx_set) + # Add robot remove subcommand + parser_robot_remove = swarm_subparsers.add_parser('remove', help="Remove a robot from the swarm") + parser_robot_remove.add_argument('robot_name', type=str, nargs='?', help="Name of the robot to remove") + parser_robot_remove.set_defaults(func=robot_remove) + # Add robot list subcommand + parser_robot_list = swarm_subparsers.add_parser('list', help="List all robots in the swarm") + parser_robot_list.set_defaults(func=robot_list) + return parser_swarm + except IndexError: + return None def robot_new(platform, params: Params, args): @@ -88,7 +90,7 @@ def robot_idx_set(platform, params: Params, args): # Load the robot list robots = RobotList.load(params) - default = robots._get_robot_by_idx(params.get('robot_idx', 0)) + default = robots.get_robot(params.get('robot_idx', 0)) if args.robot_name is not None: default = robots._get_robot_by_name(args.robot_name) @@ -114,7 +116,7 @@ def robot_idx_set(platform, params: Params, args): def robot_remove(platform, params: Params, args): """Remove a robot configuration.""" if args.robot_name is None: - robot = RobotList.load(params)._get_robot_by_idx(params.get('robot_idx', 0)) + robot = RobotList.load(params).get_robot(params.get('robot_idx', 0)) args.robot_name = robot.name formatted_robot_name = TerminalFormatter.color_text(args.robot_name, color='green', bold=True) diff --git a/src/nanosaur/utilities.py b/src/nanosaur/utilities.py index f219f7e..b24fe4f 100644 --- a/src/nanosaur/utilities.py +++ b/src/nanosaur/utilities.py @@ -129,11 +129,11 @@ def get_idx_by_name(cls, params, robot_name) -> int: return cls.load(params)._get_idx_by_name(robot_name) @classmethod - def add_robot(cls, params, robot) -> bool: + def add_robot(cls, params, robot, save=True) -> bool: robot_list = cls.load(params) if robot_list._add_robot(robot): - params['robots'] = robot_list.to_dict() - params['robot_idx'] = params.get('robot_idx', 0) + 1 + params.set('robots', robot_list.to_dict(), save=save) + params.set('robot_idx', len(robot_list.to_list()) - 1, save=save) return True return False @@ -162,20 +162,17 @@ def update_robot(cls, params, robot) -> bool: return False @classmethod - def get_robot(cls, params, idx=None) -> Robot: + def current_robot(cls, params, idx=None) -> Robot: if idx is None: idx = params.get('robot_idx', 0) - return cls.load(params)._get_robot_by_idx(idx) + return cls.load(params).get_robot(idx) @classmethod def load(cls, params): return cls() if 'robots' not in params else cls(params['robots']) def __init__(self, robots=None): - if robots is None: - self.robots = [Robot()] - else: - self.robots = [Robot(robot) for robot in robots] + self.robots = [] if robots is None else [Robot(robot) for robot in robots] def _add_robot(self, robot) -> bool: def is_robot(robot): @@ -198,7 +195,7 @@ def _remove_robot(self, idx) -> bool: def _get_idx_by_name(self, name) -> int: return next((i for i, robot in enumerate(self.robots) if robot.name == name), None) - def _get_robot_by_idx(self, idx) -> Robot: + def get_robot(self, idx) -> Robot: return self.robots[idx] def _get_robot_by_name(self, name) -> Robot: @@ -279,7 +276,7 @@ def save(self): # Get the current nanosaur's home directory create_nanosaur_home() # Save the parameters to the file - print(TerminalFormatter.color_text(f"Saving parameters to {params_file}", color='yellow')) + logger.debug(TerminalFormatter.color_text(f"Saving parameters to {params_file}", color='yellow')) with open(params_file, 'w') as file: yaml.dump(self._params_dict, file) @@ -312,7 +309,7 @@ def is_env_file(): def build_env_file(params): nanosaur_home_path = get_nanosaur_home() # Get current robot running - robot = RobotList.get_robot(params) + robot = RobotList.current_robot(params) uid = os.getuid() gid = os.getgid() env_path = os.path.join(nanosaur_home_path, f'{robot.name}.env') diff --git a/src/nanosaur/workspace.py b/src/nanosaur/workspace.py index b267708..c8ef34a 100644 --- a/src/nanosaur/workspace.py +++ b/src/nanosaur/workspace.py @@ -99,6 +99,12 @@ def workspaces_info(params: utilities.Params, verbose: bool): print(f" {TerminalFormatter.color_text(ws_name, bold=True)}: {TerminalFormatter.clickable_link(ws_path)}") elif verbose: print(TerminalFormatter.color_text("No workspaces installed", bold=True)) + + if verbose: + # Print ROS 2 installation path + ros2_path = ros.get_ros2_path(ROS_DISTRO) + ros2_string = TerminalFormatter.color_text(f"ROS 2 {ROS_DISTRO.capitalize()} path:", bold=True) + print(f"{ros2_string} {TerminalFormatter.clickable_link(ros2_path)}") def parser_workspace_menu(subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser: @@ -315,7 +321,7 @@ def debug_simulation(params: utilities.Params, args): ), inquirer.List( 'location', - message="Run locally or on docker?", + message="Select where you want to debug", choices=['host', 'docker'], ignore=lambda answers: debug_mode, ), @@ -346,7 +352,7 @@ def debug_simulation(params: utilities.Params, args): (nanosaur_shared_src, '/shared_src'), ] # Get the robot object - robot = utilities.RobotList.get_robot(params) + robot = utilities.RobotList.current_robot(params) core_tag = "simulation" if robot.simulation else "robot" container_name = f"{robot.name}-{core_tag}-debug" if selected_launcher == 'robot' else f"{selected_launcher}-debug" # Debug in Docker container