robotics
Version:
Robotics.dev P2P ROS2 robot controller CLI with ROS telemetry and video streaming
269 lines (243 loc) • 8.5 kB
JavaScript
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}"`)
}