openlch.cli
Defines the CLI for the OpenLCH project.
1"""Defines the CLI for the OpenLCH project.""" 2 3import subprocess 4import click 5from .hal import HAL 6from typing import List, Tuple 7 8DEFAULT_IP = "192.168.42.1" 9 10@click.group() 11def cli() -> None: 12 """OpenLCH CLI tool for interacting with MilkV boards. 13 14 Available commands: 15 - ping: Ping the MilkV board 16 - get-positions: Get current positions of all servos 17 - set-position: Set position for a specific servo 18 - set-wifi: Set WiFi credentials for the MilkV board 19 - get-servo-info: Get information about a specific servo 20 - scan-servos: Scan for connected servos 21 - change-servo-id: Change the ID of a servo 22 - start-calibration: Start calibration for a specific servo 23 - cancel-calibration: Cancel ongoing calibration for a specific servo 24 - get-calibration-status: Get the current calibration status 25 - start-video-stream: Start the video stream 26 - stop-video-stream: Stop the video stream 27 - get-video-stream-urls: Get the URLs for various video stream formats 28 - get-imu-data: Get current IMU sensor data (gyroscope and accelerometer readings) 29 30 Use 'openlch COMMAND --help' for more information on a specific command. 31 """ 32 pass 33 34@cli.command() 35@click.argument("ip", default=DEFAULT_IP) 36def ping(ip: str) -> None: 37 """Ping the MilkV board at the specified IP address.""" 38 try: 39 result = subprocess.run(["ping", "-c", "1", ip], capture_output=True, text=True, check=False) 40 if result.returncode == 0: 41 click.echo(f"Successfully pinged {ip}") 42 click.echo(result.stdout) 43 else: 44 click.echo(f"Failed to ping {ip}") 45 click.echo(result.stderr) 46 except Exception as e: 47 click.echo(f"An error occurred: {str(e)}") 48 49@cli.command() 50@click.argument("ip", default=DEFAULT_IP) 51def get_positions(ip: str) -> None: 52 """Get current positions and speeds of all servos.""" 53 hal = HAL(ip) 54 try: 55 positions = hal.servo.get_positions() 56 click.echo("Current positions and speeds:") 57 for id, position, speed in positions: 58 click.echo(f"Servo {id}: Position = {position:.2f}, Speed = {speed:.2f}") 59 except Exception as e: 60 click.echo(f"An error occurred: {str(e)}") 61 finally: 62 hal.close() 63 64@cli.command() 65@click.argument("id", type=int) 66@click.argument("position", type=float) 67@click.option("--speed", "-s", type=float, default=0, help="Movement speed in degrees per second (0 = max speed)") 68@click.argument("ip", default=DEFAULT_IP) 69def set_position(id: int, position: float, speed: float, ip: str) -> None: 70 """Set position for a specific servo.""" 71 hal = HAL(ip) 72 try: 73 hal.servo.set_position(id, position, speed) 74 click.echo(f"Position set for servo {id} to {position}° at speed {speed if speed > 0 else 'max'} deg/s") 75 except Exception as e: 76 click.echo(f"An error occurred: {str(e)}") 77 finally: 78 hal.close() 79 80@cli.command() 81@click.argument("ssid") 82@click.argument("password") 83@click.argument("ip", default=DEFAULT_IP) 84def set_wifi(ssid: str, password: str, ip: str) -> None: 85 """Set WiFi credentials for the MilkV board.""" 86 hal = HAL(ip) 87 try: 88 hal.system.set_wifi_info(ssid, password) 89 click.echo("WiFi credentials set successfully") 90 except Exception as e: 91 click.echo(f"An error occurred: {str(e)}") 92 finally: 93 hal.close() 94 95@cli.command() 96@click.argument("id", type=int) 97@click.argument("ip", default=DEFAULT_IP) 98def get_servo_info(id: int, ip: str) -> None: 99 """Get information about a specific servo.""" 100 hal = HAL(ip) 101 try: 102 info = hal.servo.get_servo_info(id) 103 click.echo(f"Servo {id} info:") 104 for key, value in info.items(): 105 click.echo(f"{key}: {value}") 106 except Exception as e: 107 click.echo(f"An error occurred: {str(e)}") 108 finally: 109 hal.close() 110 111@cli.command() 112@click.argument("ip", default=DEFAULT_IP) 113def scan_servos(ip: str) -> None: 114 """Scan for connected servos.""" 115 hal = HAL(ip) 116 try: 117 servo_ids = hal.servo.scan() 118 click.echo("Found servo IDs:") 119 for id in servo_ids: 120 click.echo(id) 121 except Exception as e: 122 click.echo(f"An error occurred: {str(e)}") 123 finally: 124 hal.close() 125 126@cli.command() 127@click.argument("old_id", type=int) 128@click.argument("new_id", type=int) 129@click.argument("ip", default=DEFAULT_IP) 130def change_servo_id(old_id: int, new_id: int, ip: str) -> None: 131 """Change the ID of a servo.""" 132 hal = HAL(ip) 133 try: 134 success = hal.servo.change_id(old_id, new_id) 135 if success: 136 click.echo(f"Successfully changed servo ID from {old_id} to {new_id}") 137 else: 138 click.echo("Failed to change servo ID") 139 except Exception as e: 140 click.echo(f"An error occurred: {str(e)}") 141 finally: 142 hal.close() 143 144@cli.command() 145@click.argument("servo_id", type=int) 146@click.option("--speed", "-s", type=int, default=300, 147 help="Calibration speed in degrees per second. Default: 300") 148@click.option("--current", "-c", type=float, default=600.0, 149 help="Current threshold in mA to detect end stops. Default: 600.0") 150@click.argument("ip", default=DEFAULT_IP) 151def start_calibration(servo_id: int, speed: int, current: float, ip: str) -> None: 152 """Start calibration for a specific servo. 153 154 The calibration process will move the servo until it detects end stops based on current draw. 155 Use --speed to adjust movement speed and --current to adjust sensitivity.""" 156 hal = HAL(ip) 157 try: 158 success = hal.servo.start_calibration( 159 servo_id, 160 calibration_speed=speed, 161 current_threshold=current 162 ) 163 if success: 164 click.echo(f"Calibration started for servo {servo_id}") 165 click.echo(f"Speed: {speed} deg/s, Current threshold: {current} mA") 166 else: 167 click.echo(f"Failed to start calibration for servo {servo_id}") 168 except Exception as e: 169 click.echo(f"An error occurred: {str(e)}") 170 finally: 171 hal.close() 172 173@cli.command() 174@click.argument("servo_id", type=int) 175@click.argument("ip", default=DEFAULT_IP) 176def cancel_calibration(servo_id: int, ip: str) -> None: 177 """Cancel ongoing calibration for a specific servo.""" 178 hal = HAL(ip) 179 try: 180 success = hal.servo.cancel_calibration(servo_id) 181 if success: 182 click.echo(f"Calibration cancelled for servo {servo_id}") 183 else: 184 click.echo(f"Failed to cancel calibration for servo {servo_id}") 185 except Exception as e: 186 click.echo(f"An error occurred: {str(e)}") 187 finally: 188 hal.close() 189 190@cli.command() 191@click.argument("ip", default=DEFAULT_IP) 192def get_calibration_status(ip: str) -> None: 193 """Get the current calibration status.""" 194 hal = HAL(ip) 195 try: 196 status = hal.servo.get_calibration_status() 197 if status['is_calibrating']: 198 click.echo(f"Calibration in progress for servo {status['calibrating_servo_id']}") 199 else: 200 click.echo("No calibration in progress") 201 except Exception as e: 202 click.echo(f"An error occurred: {str(e)}") 203 finally: 204 hal.close() 205 206@cli.command() 207@click.argument("ip", default=DEFAULT_IP) 208def start_video_stream(ip: str) -> None: 209 """Start the video stream.""" 210 hal = HAL(ip) 211 try: 212 hal.system.start_video_stream() 213 click.echo("Video stream started") 214 except Exception as e: 215 click.echo(f"An error occurred: {str(e)}") 216 finally: 217 hal.close() 218 219@cli.command() 220@click.argument("ip", default=DEFAULT_IP) 221def stop_video_stream(ip: str) -> None: 222 """Stop the video stream.""" 223 hal = HAL(ip) 224 try: 225 hal.system.stop_video_stream() 226 click.echo("Video stream stopped") 227 except Exception as e: 228 click.echo(f"An error occurred: {str(e)}") 229 finally: 230 hal.close() 231 232@cli.command() 233@click.argument("ip", default=DEFAULT_IP) 234def get_video_stream_urls(ip: str) -> None: 235 """Get the URLs for various video stream formats.""" 236 hal = HAL(ip) 237 try: 238 urls = hal.system.get_video_stream_urls() 239 click.echo("Video stream URLs:") 240 for format, url_list in urls.items(): 241 click.echo(f"{format.upper()}:") 242 for url in url_list: 243 click.echo(f" - {url}") 244 except Exception as e: 245 click.echo(f"An error occurred: {str(e)}") 246 finally: 247 hal.close() 248 249@cli.command() 250@click.option("--settings", "-s", type=(int, float), multiple=True, required=True, 251 help="Servo ID and torque value pairs (e.g., -s 1 0.5 -s 2 0.7)") 252@click.argument("ip", default=DEFAULT_IP) 253def set_torque(settings: List[Tuple[int, float]], ip: str) -> None: 254 """Set torque for multiple servos.""" 255 hal = HAL(ip) 256 try: 257 hal.servo.set_torque(settings) 258 click.echo("Torque settings applied successfully:") 259 for servo_id, torque in settings: 260 click.echo(f"Servo {servo_id}: Torque = {torque:.2f}") 261 except Exception as e: 262 click.echo(f"An error occurred: {str(e)}") 263 finally: 264 hal.close() 265 266@cli.command() 267@click.option("--settings", "-s", type=(int, click.Choice(['true', 'false'])), multiple=True, required=True, 268 help="Servo ID and enable status pairs (e.g., -s 1 true -s 2 false)") 269@click.argument("ip", default=DEFAULT_IP) 270def set_torque_enable(settings: List[Tuple[int, str]], ip: str) -> None: 271 """Enable or disable torque for multiple servos.""" 272 hal = HAL(ip) 273 try: 274 # Convert 'true'/'false' strings to boolean values 275 bool_settings = [(id, status.lower() == 'true') for id, status in settings] 276 hal.servo.set_torque_enable(bool_settings) 277 click.echo("Torque enable settings applied successfully:") 278 for servo_id, status in bool_settings: 279 click.echo(f"Servo {servo_id}: Torque {'enabled' if status else 'disabled'}") 280 except Exception as e: 281 click.echo(f"An error occurred: {str(e)}") 282 finally: 283 hal.close() 284 285@cli.command() 286@click.argument("ip", default=DEFAULT_IP) 287def get_imu_data(ip: str) -> None: 288 """Get current IMU sensor data (gyroscope and accelerometer readings).""" 289 hal = HAL(ip) 290 try: 291 imu_data = hal.imu.get_data() 292 click.echo("IMU Sensor Data:") 293 click.echo("\nGyroscope (degrees/second):") 294 click.echo(f" X: {imu_data['gyro']['x']:.2f}") 295 click.echo(f" Y: {imu_data['gyro']['y']:.2f}") 296 click.echo(f" Z: {imu_data['gyro']['z']:.2f}") 297 click.echo("\nAccelerometer (m/s^2):") 298 click.echo(f" X: {imu_data['accel']['x']:.2f}") 299 click.echo(f" Y: {imu_data['accel']['y']:.2f}") 300 click.echo(f" Z: {imu_data['accel']['z']:.2f}") 301 except Exception as e: 302 click.echo(f"An error occurred: {str(e)}") 303 finally: 304 hal.close() 305 306@cli.command() 307@click.argument("ip", default=DEFAULT_IP) 308def enable_movement(ip: str) -> None: 309 """Enable movement for all servos.""" 310 hal = HAL(ip) 311 try: 312 hal.servo.enable_movement() 313 click.echo("Movement enabled for all servos") 314 except Exception as e: 315 click.echo(f"An error occurred: {str(e)}") 316 finally: 317 hal.close() 318 319@cli.command() 320@click.argument("ip", default=DEFAULT_IP) 321def disable_movement(ip: str) -> None: 322 """Disable movement for all servos.""" 323 hal = HAL(ip) 324 try: 325 hal.servo.disable_movement() 326 click.echo("Movement disabled for all servos") 327 except Exception as e: 328 click.echo(f"An error occurred: {str(e)}") 329 finally: 330 hal.close() 331 332if __name__ == "__main__": 333 cli()
OpenLCH CLI tool for interacting with MilkV boards.
Available commands:
- ping: Ping the MilkV board
- get-positions: Get current positions of all servos
- set-position: Set position for a specific servo
- set-wifi: Set WiFi credentials for the MilkV board
- get-servo-info: Get information about a specific servo
- scan-servos: Scan for connected servos
- change-servo-id: Change the ID of a servo
- start-calibration: Start calibration for a specific servo
- cancel-calibration: Cancel ongoing calibration for a specific servo
- get-calibration-status: Get the current calibration status
- start-video-stream: Start the video stream
- stop-video-stream: Stop the video stream
- get-video-stream-urls: Get the URLs for various video stream formats
- get-imu-data: Get current IMU sensor data (gyroscope and accelerometer readings)
Use 'openlch COMMAND --help' for more information on a specific command.
Ping the MilkV board at the specified IP address.
Get current positions and speeds of all servos.
Set position for a specific servo.
Set WiFi credentials for the MilkV board.
Get information about a specific servo.
Scan for connected servos.
Change the ID of a servo.
Start calibration for a specific servo.
The calibration process will move the servo until it detects end stops based on current draw. Use --speed to adjust movement speed and --current to adjust sensitivity.
Cancel ongoing calibration for a specific servo.
Get the current calibration status.
Start the video stream.
Stop the video stream.
Get the URLs for various video stream formats.
Set torque for multiple servos.
Enable or disable torque for multiple servos.
Get current IMU sensor data (gyroscope and accelerometer readings).
Enable movement for all servos.
Disable movement for all servos.