Skip to content

Commit

Permalink
Add support for interactive command-line input
Browse files Browse the repository at this point in the history
  • Loading branch information
cheton committed Feb 13, 2017
1 parent 80dab28 commit cffb11f
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 36 deletions.
81 changes: 66 additions & 15 deletions bin/cncjs-pendant-boilerplate
Expand Up @@ -2,24 +2,37 @@

var program = require('commander');
var serialport = require('serialport');
var inquirer = require('inquirer');
var vorpal = require('vorpal')();
var pkg = require('../package.json');
var serverMain = require('../index');
var options = {};

program
.version(pkg.version)
.usage('-p <port> [options]')
.usage('-s <secret> -p <port> [options]')
.option('-l, --list', 'list available ports then exit')
.option('-s, --secret', 'the secret key stored in the ~/.cncrc file')
.option('-p, --port <port>', 'path or name of serial port')
.option('-b, --baudrate <baudrate>', 'baud rate (default: 115200)', 115200)
.option('--socket-address <address>', 'socket address or hostname (default: localhost)', 'localhost')
.option('--socket-port <port>', 'socket port (default: 8000)', 8000)
.option('--controller-type <type>', 'controller type: Grbl|Smoothie|TinyG (default: Grbl)', 'Grbl');
.option('--controller-type <type>', 'controller type: Grbl|Smoothie|TinyG (default: Grbl)', 'Grbl')
.option('--access-token-lifetime <lifetime>', 'access token lifetime in seconds or a time span string (default: 30d)', '30d')

if (process.argv.length > 1) {
program.parse(process.argv);
}
program.parse(process.argv);

var options = {
secret: program.secret,
port: program.port,
baudrate: program.baudrate,
socketAddress: program.socketAddress,
socketPort: program.socketPort,
controllerType: program.controllerType,
accessTokenLifetime: program.accessTokenLifetime
};

if (program.list) {
if (options.list) {
serialport.list(function(err, ports) {
if (err) {
console.error(err);
Expand All @@ -32,15 +45,53 @@ if (program.list) {
return;
}

if (!program.port) {
program.outputHelp();
process.exit(1);
var createServer = function(options) {
serverMain(options, function(err, socket) {
vorpal
.mode('command')
.description('Enters the command-line mode.')
.delimiter(options.controllerType + '>')
.init(function(args, callback) {
this.log('You can now directly enter commands. To exit, type `exit`.');
callback();
})
.action(function(command, callback) {
var line = command + '\n';
socket.emit('write', options.port, line);
callback();
});

console.log('');
console.log('Type "help" to display supported options:');

vorpal
.delimiter('pendant$')
.show();
});
};

if (options.port) {
createServer(options);
return;
}

serverMain({
port: program.port,
baudrate: program.baudrate,
socketAddress: program.socketAddress,
socketPort: program.socketPort,
controllerType: program.controllerType
serialport.list(function(err, ports) {
if (err) {
console.error(err);
process.exit(1);
}
const choices = ports.map(function(port) {
return port.comName;
});

inquirer.prompt([{
type: 'list',
name: 'port',
message: 'Specify which port you want to use?',
choices: choices
}]).then(function(answers) {
options.port = answers.port;

createServer(options);
});
});
69 changes: 49 additions & 20 deletions index.js
@@ -1,33 +1,53 @@
#!/usr/bin/env node

var fs = require('fs');
var path = require('path');
var io = require('socket.io-client');
var jwt = require('jsonwebtoken');
const fs = require('fs');
const path = require('path');
const io = require('socket.io-client');
const jwt = require('jsonwebtoken');
const get = require('lodash.get');

var generateAccessToken = function(payload, secret, expiration) {
var token = jwt.sign(payload, secret, {
const generateAccessToken = function(payload, secret, expiration) {
const token = jwt.sign(payload, secret, {
expiresIn: expiration
});

return token;
};

// Get secret key from the config file and generate an access token
var getUserHome = function () {
const getUserHome = function() {
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
};

module.exports = function(options) {
var cncrc = path.resolve(getUserHome(), '.cncrc');
var config = JSON.parse(fs.readFileSync(cncrc, 'utf8'));
var token = generateAccessToken({ id: '', name: 'pendant' }, config.secret, '30d');
module.exports = function(options, callback) {
options = options || {};
options.secret = get(options, 'secret', process.env['CNCJS_SECRET']);
options.baudrate = get(options, 'baudrate', 115200);
options.socketAddress = get(options, 'socketAddress', 'localhost');
options.socketPort = get(options, 'socketPort', 8000);
options.controllerType = get(options, 'controllerType', 'Grbl');
options.accessTokenLifetime = get(options, 'accessTokenLifetime', '30d');

if (!options.secret) {
const cncrc = path.resolve(getUserHome(), '.cncrc');
try {
const config = JSON.parse(fs.readFileSync(cncrc, 'utf8'));
options.secret = config.secret;
} catch (err) {
console.error(err);
process.exit(1);
}
}

const token = generateAccessToken({ id: '', name: 'cncjs-pendant' }, options.secret, options.accessTokenLifetime);
const url = 'ws://' + options.socketAddress + ':' + options.socketPort + '?token=' + token;

socket = io.connect('ws://' + options.socketAddress + ':' + options.socketPort, {
'query': 'token=' + token
});

socket.on('connect', () => {
console.log('[socket.io] Connected to ' + options.socketAddress + ':' + options.socketPort);
console.log('Connected to ' + url);

// Open port
socket.emit('open', options.port, {
Expand All @@ -36,28 +56,37 @@ module.exports = function(options) {
});
});

socket.on('error', () => {
console.error('[socket.io] Error');
socket.on('error', (err) => {
console.error('Connection error.');
if (socket) {
socket.destroy();
socket = null;
}
});

socket.on('close', () => {
console.log('[socket.io] Connection close');
console.log('Connection closed.');
});

socket.on('serialport:open', function(options) {
options = options || {};

console.log('Connected to port "' + options.port + '" (Baud rate: ' + options.baudrate + ')');

callback(null, socket);
});

socket.on('serialport:open', function (options) {
const { controllerType, port, baudrate, inuse } = options;
console.log('[pendant] Connected to port "' + options.port + '" (Baud rate: ' + options.baudrate + ')');
socket.on('serialport:error', function(options) {
callback(new Error('Error opening serial port "' + options.port + '"'));
});

socket.on('serialport:read', function(data) {
console.log(data);
console.log((data || '').trim());
});

/*
socket.on('serialport:write', function(data) {
console.log('> ' + data);
console.log((data || '').trim());
});
*/
};
8 changes: 7 additions & 1 deletion package.json
Expand Up @@ -3,6 +3,9 @@
"version": "0.1.0",
"description": "A bare minimum example to develop a cncjs pendant.",
"main": "./index.js",
"engines": {
"node": ">=4"
},
"bin": {
"cncjs-pendant-boilerplate": "./bin/cncjs-pendant-boilerplate"
},
Expand All @@ -27,8 +30,11 @@
"homepage": "https://github.com/cncjs/cncjs-pendant-boilerplate#readme",
"dependencies": {
"commander": "^2.9.0",
"inquirer": "^3.0.1",
"jsonwebtoken": "^7.2.1",
"lodash.get": "^4.4.2",
"serialport": "^4.0.7",
"socket.io-client": "^1.7.2"
"socket.io-client": "^1.7.2",
"vorpal": "^1.11.4"
}
}

0 comments on commit cffb11f

Please sign in to comment.