UNPKG

robotics

Version:

Robotics.dev P2P ROS2 robot controller CLI with ROS telemetry and video streaming

269 lines (243 loc) 8.5 kB
#!/usr/bin/env node import {createRequire } from "module"; const require = createRequire(import.meta.url); import Configstore from 'configstore'; const config = new Configstore('robotics'); var robotId = config.get('ROBOT_ID'); const chalk = require('chalk'); const clear = require('clear'); const emoji = require('node-emoji'); const argv = require('minimist')(process.argv.slice(2)); const {exec, spawn} = require('child_process'); // const path = require('path'); import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const setRobotId = async (rId) => { const test = await config.set('ROBOT_ID', rId) robotId = rId console.log(chalk.blue(`ROBOT ID: ${robotId}`)); } const setApiToken = async (token) => { const test = await config.set('API_TOKEN', token) console.log(chalk.blue(`API TOKEN: ${token}`)); } const getRobotId = async () => { const myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); const response = await fetch("https://robotics.dev/new", { method: "POST", headers: myHeaders, }) .then((res) => res.json()) .then((data) => { // console.log("robotId:", data.id); setRobotId(data.id); // config.set('ROBOT_ID', data.id) // robotId = data.id }) } if (!robotId){ getRobotId(); } else { console.log(chalk.blue(`ROBOT ID: ${robotId}`)); } clear(); console.log(emoji.get('robot'),chalk.blue('ROBOTICS.DEV')); // console.log(argv); function killProcessByName(name){ exec(`pkill -f ${name}`, (error, stdout, stderr) => { // console.log(chalk.red('Service stopped.')); if(error){ // console.error(`Error: ${error}`); return; } }); } //HELP (robotics help) if(argv._.includes("help")){ console.log(chalk.blue('Robotics commands: ')); console.log(chalk.blue('robotics help'), chalk.green('returns this list of commands')); console.log(chalk.blue('robotics connect (-s robotics.dev)'), chalk.green('connects robot to cloud/edge via websocket communications')); console.log(chalk.blue('robotics disconnect'), chalk.green('disconnects robot from cloud or edge servers')); console.log(chalk.blue('robotics id'), chalk.green("returns robot's id for connecting with cloud or edge servers")); console.log(chalk.blue('robotics set --token=1234...6789'), chalk.green("sets developer token from robotics.dev API page")); console.log(chalk.blue('robotics status'), chalk.green('returns list of running services')); console.log(chalk.blue('robotics start motors (-b rpi (DEFAULT) or -b firmata (Radxa, LattePanda)) (-p 27,22,17,18 (left to right motor pins)) (-e 13,2,12,11 (left to right motor encoder pins)) (-d /dev/ttyACM0 (overrides default device id for radxa and lattepanda))'), chalk.green('starts ROS2 motors and odometry for the appropriate board')); console.log(chalk.blue('robotics stop motors'), chalk.green("stops robot's motors")); console.log(chalk.blue('robotics start camera (-d /dev/video0)'), chalk.green("starts robot's 2d camera stream with device override. Note RealSense RGB device is /dev/video4. -r 672x672 -f 15")); console.log(chalk.blue('robotics stop camera'), chalk.green("stops robot's 2d camera stream to cloud or edge servers")); console.log(chalk.blue('robotics start realsense (-p enables pointcloud)'), chalk.green("starts robot's 3d realsense camera")); console.log(chalk.blue('robotics stop realsense'), chalk.green("stops robot's 3d realsense camera")); } // CONNECT (robotics connect -s http://192.168.0.6:3001) if(argv._.includes("connect")){ const filePath = path.join(__dirname, 'comms.js'); var options = [filePath] if(argv.s){ options.push('--server') options.push(argv.s.toLowerCase()) } var commsProcess = spawn('node', options, { detached: true, stdio: 'ignore' }); console.log(chalk.green('Robot connected.')); commsProcess.unref(); if(robotId){ console.log(chalk.blue(`ROBOT ID: ${robotId}`)); // process.exit(0); } } // DISCONNECT (robotics disconnect) if(argv._.includes("disconnect")){ killProcessByName('comms.js'); console.log(chalk.red('Robot disconnected.')); process.exit(0); } // ID if(argv._.includes("id")){ console.log(chalk.blue(`ROBOT ID: ${config.get('ROBOT_ID')}`)); if(robotId){ // console.log(chalk.blue(`ROBOT ID: ${robotId}`)); process.exit(0); } } // SET - robotics set --token=123 || robotics set --id=123 if(argv._.includes("set")){ if(argv.token){ setApiToken(argv.token); } if(argv.id){ setRobotId(argv.id); } process.exit(0); } // STATUS if(argv._.includes("status")){ exec(`ps -aux | grep comms.js`, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); }); exec(`ps -aux | grep motors-rpi5.js`, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); }); exec(`ps -aux | grep motors-firmata.js`, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); }); exec(`ps -aux | grep camera-2d-ros.js`, (err, stdout, stdin) => { if (err) throw err; console.log(stdout); }); // process.exit(0); } // MOTORS START (robotics motors start || robotics start motors -b rpi -p 27,22,17,18) if(argv._.includes("motors") && argv._.includes("start")){ var filePath1 = path.join(__dirname, 'motors-rpi5.js'); var filePath2 = path.join(__dirname, 'motors-firmata.js'); var options if(argv.b && argv.b === 'rpi'){ options = [filePath1]; } else if(argv.b && argv.b === 'firmata'){ options = [filePath2] // options = ['motors-firmata.js']; } else { options = [filePath1] // options = ['motors-rpi5.js'] } if(argv.d){ options.push('--device') options.push(argv.d) } if(argv.p){ options.push('--pins') options.push(argv.p) } if(argv.e){ options.push('--encoderpins') options.push(argv.e) } var commsProcess = spawn('node', options, { detached: true, stdio: 'ignore' }); console.log(chalk.green('Robot motors started.')); speak('Robot motors started.'); commsProcess.unref(); process.exit(0); } // MOTORS STOP (robotics motors stop || robotics stop motors) if(argv._.includes("motors") && argv._.includes("stop")){ killProcessByName('motors-rpi5.js'); killProcessByName('motors-firmata.js'); console.log(chalk.red('Robot motors stopped.')); speak('Robot motors stopped.') process.exit(0); } // CAMERA START (robotics camera start || robotics start camera -s http://192.168.0.6:3001 -d /dev/video0) if(argv._.includes("camera") && argv._.includes("start")){ const filePath = path.join(__dirname, 'camera-2d-ros.js'); var options = [filePath] // if(argv.s){ // options.push('--server') // options.push(argv.s.toLowerCase()) // } if(argv.d){ options.push('--device') options.push(argv.d) } if(argv.r){ options.push('--resolution') options.push(argv.r) } if(argv.f){ options.push('--fps') options.push(argv.f) } var commsProcess = spawn('node', options, { detached: true, stdio: 'ignore' }); console.log(chalk.green('Robot camera started.')); speak('Robot camera started.'); commsProcess.unref(); process.exit(0); } // CAMERA STOP (robotics camera stop || robotics stop camera) if(argv._.includes("camera") && argv._.includes("stop")){ killProcessByName('camera-2d-ros.js'); console.log(chalk.red('Robot camera stopped.')); speak('Robot camera stopped.'); process.exit(0); } // REALSENSE START (robotics realsense start || robotics start realsense -p enables pointcloud) if(argv._.includes("realsense") && argv._.includes("start")){ // const filePath = path.join(__dirname, 'camera-2d-ros.js'); // var options = [filePath] var options = ['launch', 'realsense2_camera', 'rs_launch.py']; if(argv.p){ options.push('pointcloud.enable:=true') } var commsProcess = spawn('ros2', options, { detached: true, stdio: 'ignore' }); console.log(chalk.green('Robot realsense started.')); speak('Robot realsense started.'); commsProcess.unref(); process.exit(0); } // CAMERA STOP (robotics camera stop || robotics stop camera) if(argv._.includes("realsense") && argv._.includes("stop")){ killProcessByName('realsense2_camera_node'); console.log(chalk.red('Robot realsense stopped.')); speak('Robot realsense stopped.'); process.exit(0); } //speak("Initializing robot."); function speak(msg){ exec(`espeak "${msg}"`) }