pololu-jrk
Version:
Drive motors with the Pololu JRK motor controller
347 lines (330 loc) • 9.74 kB
JavaScript
/**
* Module dependencies.
*/
const {
exec
} = require('child_process');
const {
format,
createLogger,
transports
} = require('winston')
const logger = createLogger({
transports: [
new transports.Console({
level: 'info',
silent: false,
format: format.combine(
format.colorize(),
format.simple()
)
})
]
})
/**
* jrk2cmd commands
*/
const device_type = 'Jrk G2';
const jrk_cmd = {
prefix: 'jrk2cmd',
general: {
status: '--status',
full: '--full',
dev: '-d',
list: '--list',
cmd_port: '--cmd-port',
ttl_port: '--ttl-port',
pause: '--pause',
pause_on_error: '--pause-on-error',
help: '--help'
},
ctrl: {
target: '--target',
target_relative: '--target-relative',
speed: '--speed',
stop: '--stop',
run: '--run',
clear_errors: '--clear-errors',
force_duty_cycle: '--force-duty-cycle',
force_duty_cycle_target: '--force-duty-cycle-target'
},
eeprom: {
restore_defaults: '--restore-defaults',
settings: '--settings',
get_settings: '--get-settings',
fix_settings: '--fix-settings'
},
ram: {
get_ram_settings: '--get-ram-settings',
ram_settings: '--ram-settings',
reinitialize: '--reinitialize',
proportional: '--proportional',
integral: '--integral',
derivative: '--derivative',
max_duty_cycle: '--max-duty-cycle',
max_duty_cycle_fwd: '--max-duty-cycle-fwd',
max_duty_cycle_rev: '--max-duty-cycle-rev',
current_limit: '--current-limit',
current_limit_fwd: '--current-limit-fwd',
current_limit_rev: '--current-limit-rev'
},
encoded: {
current_table: '--current-table',
current_decode: '--current-decode',
current_encode: '--current-encode'
}
}
const limits = {
max_target: 4096,
min_target: 0,
max_duty: 600
}
const properties = {
name: {
key: "Name",
type: String
},
serial: {
key: "Serial number",
type: String
},
firmware: {
key: "Firmware version",
type: String
},
cmd_port: {
key: "Command port",
type: String
},
ttl_port: {
key: "TTL port",
type: String
},
last_reset: {
key: "Last reset",
type: String
},
// TODO: update matching regex to handle uptime results
// uptime: {
// key: "Up time",
// type: String
// },
input: {
key: "Input",
type: Number
},
target: {
key: "Target",
type: Number
},
fb: {
key: "Feedback",
type: Number
},
scaled_fb: {
key: "Scaled feedback",
type: Number
},
error: {
key: "Error",
type: String
},
integral: {
key: "Integral",
type: Number
},
duty_cycle_target: {
key: "Duty cycle target",
type: Number
},
duty_cycle: {
key: "Duty cycle",
type: Number
},
current: {
key: "Current",
type: String
},
vin: {
key: "VIN voltage",
type: String
},
pid_period_exceeded: {
key: "PID period exceeded",
type: String
},
pid_period_count: {
key: "PID period count",
type: Number
},
}
function getDeviceList() {
return new Promise(async(resolve, reject) => {
let cmd = `${jrk_cmd.prefix} ${jrk_cmd.general.list}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
let jrk_list = [];
stdout.trim().split('\n').forEach(element => {
jrk_list.push(element.split(',').map(s => s.trim()));
});
logger.debug('Extracted Jrk list: ' + jrk_list);
resolve(jrk_list);
});
});
}
function getVersion() {
return new Promise(async(resolve, reject) => {
let cmd = `${jrk_cmd.prefix}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
version = stdout.trim().split('\n')[1].split(' ')[1];
logger.debug(`Extracted version: ${version}`);
resolve(version);
});
});
}
class Jrk {
constructor(id = null) {
// default to first device
if (id == null) {
getDeviceList().then((dev_list) => {
try {
this.id = dev_list[0][0];
logger.debug(`New Jrk object with ID ${this.id} created`);
} catch (err) {
if (err instanceof TypeError) {
logger.error("No JRK devices found");
} else {
logger.error("Unexpected Error");
}
logger.error(err);
process.exit(1);
}
}).catch((err) => {
logger.error('Error initializing Jrk object');
process.exit(1);
});
} else {
this.id = id
}
}
stop() {
let cmd = `${jrk_cmd.prefix} ${jrk_cmd.ctrl.stop} ${jrk_cmd.general.dev} ${this.id}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
});
}
run() {
let cmd = `${jrk_cmd.prefix} ${jrk_cmd.ctrl.run} ${jrk_cmd.general.dev} ${this.id}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
});
}
setTarget(val) {
let cmd = `${jrk_cmd.prefix} ${jrk_cmd.ctrl.target} ${val} ${jrk_cmd.general.dev} ${this.id}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
});
}
SetTargetRel(val) {
let cmd = `${jrk_cmd.prefix} ${jrk_cmd.ctrl.target_relative} ${val} ${jrk_cmd.general.dev} ${this.id}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
});
}
getProperty(name) {
return new Promise(async(resolve, reject) => {
const prop = properties[name];
if (typeof(prop) == 'undefined') {
reject(`Invalid property: ${name}`);
} else {
let cmd = `${jrk_cmd.prefix} ${jrk_cmd.general.status} ${jrk_cmd.general.dev} ${this.id}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
logger.error(`Command: ${cmd} error: ${error.message}`);
reject(error);
}
if (stderr) {
logger.warn(`Command: ${cmd} stderr: ${stderr}`);
reject(stderr);
}
logger.debug(`Command: ${cmd} stdout: ${stdout}`);
const regex = new RegExp(`${prop.key}:\\s+([\\w\\s\./^\\\n]+)\\n`);
const match = stdout.match(regex);
const prop_val = prop.type(match[1]);
logger.debug(`${prop.key}: ${prop_val}`);
resolve(prop_val);
});
}
});
}
}
// Exports
module.exports.getDeviceList = getDeviceList;
module.exports.getVersion = getVersion;
module.exports.limits = limits;
module.exports.Jrk = Jrk;
// Testing and debugging
async function test() {
let ver = await getVersion().catch((err) => {
console.error(err);
process.exit(1);
});
let mot = new Jrk();
setTimeout(async () => {
mot.setTarget(500);
let prop = await mot.getProperty('name').catch((err) => {
logger.error(err);
});
console.log(prop);
}, 500);
}
// test();