Skip to content

Commit

Permalink
Improve info page
Browse files Browse the repository at this point in the history
  • Loading branch information
rbonghi committed Jan 27, 2025
1 parent 0f404f6 commit 9376f1d
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 121 deletions.
8 changes: 4 additions & 4 deletions src/nanosaur/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down Expand Up @@ -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')

Expand Down Expand Up @@ -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')

Expand Down Expand Up @@ -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')

Expand Down
36 changes: 21 additions & 15 deletions src/nanosaur/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 (
Expand All @@ -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)
Expand Down Expand Up @@ -202,16 +211,16 @@ 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,
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
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")

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions src/nanosaur/prompt_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
123 changes: 83 additions & 40 deletions src/nanosaur/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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():
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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]))

Expand Down Expand Up @@ -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]))

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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':
Expand All @@ -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':
Expand Down
Loading

0 comments on commit 9376f1d

Please sign in to comment.