Skip to content

Commit

Permalink
Refines CLI operation.
Browse files Browse the repository at this point in the history
- Adds --help usage info.
- Adds --version, taken from `package.json`.
- Moves config loading and verification from `server.js` to `index.js`.
- Adds a console message when the server gets a websocket connection.
- Updates version to 1.0.0-rc6.
  • Loading branch information
mildmojo committed Sep 23, 2017
1 parent 4fa1f9f commit efd95e2
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 46 deletions.
46 changes: 45 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,48 @@
'use strict';
const fs = require('fs');
const nconf = require('nconf');
const cjson = require('cjson');
const chalk = require('chalk');
const {version} = require('./package.json');

nconf.use('memory').argv().env();
const configFile = nconf.get('CONFIG_FILE') || nconf.get('config_file') || 'config.json';
nconf.defaults(readConfig(configFile));
nconf.set('port', nconf.get('PORT') || nconf.get('port') || 3000)
nconf.set('testmode', nconf.get('TESTMODE') || nconf.get('testmode'));
verifyConfig(nconf);

if (nconf.get('version')) {
console.log(version);
process.exit();
}

if (nconf.get('help')) {
console.log('Options:');
console.log(' --help - Prints this usage info');
console.log(' --port n - Listen port number for HTTP server (default: 3000)');
console.log(" --testmode - Simulate serial port activity, don't open real ports");
console.log(' --version - Prints server version number');
process.exit();
}

const server = require('./lib/server.js');
server.start().catch(console.error);
server.start(nconf).catch(console.error);

function readConfig(file) {
if (!fs.existsSync('./config.json')) {
console.warn(chalk.red.bold(`Could not find ${file}. Please copy` +
` ${file}.example to ${file}) and edit it.`));
throw new Error(`File not found: ${file}`)
}

return cjson.load(file);
}

function verifyConfig(config) {
const ports = config.get('serialPorts');
if (!ports || !Array.isArray(ports) || !ports.length) {
console.warn(`No serial ports configured! Please specify serial port(s) in ${configFile}.`);
throw new Error('Serial ports not configured.');
}
}
70 changes: 26 additions & 44 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ const express = require('express');
const WebSocket = require('ws');
const SerialPort = require('serialport');
const chalk = require('chalk');
const cjson = require('cjson');
const nconf = require('nconf');
const xml2js = require('xml2js');
const ButtonStatus = require('./buttonStatus');
nconf.argv().env();
const CONFIG_FILE = nconf.get('CONFIG_FILE') || nconf.get('config_file') || 'config.json';
nconf.defaults(readConfig(CONFIG_FILE));
const PORT = nconf.get('PORT') || nconf.get('port') || 3000;
const TESTMODE = nconf.get('TESTMODE') || nconf.get('testmode');

module.exports = {start};

async function start() {
// Expects a config object with a .get() function for looking up values by key.
async function start(config) {
// Read config
// Verify config
// Read mappings
Expand All @@ -27,45 +21,26 @@ async function start() {
// Bind serial ports to button status instances
// Bind web app to button status instances

const config = readConfig(CONFIG_FILE);
if (!verifyConfig(config)) process.exit(1);
const mappings = await readMappings(config.mappingFiles);
const buttonStatuses = createButtonStatuses(config.serialPorts.length, mappings);
const ports = openSerialPorts(config.serialPorts);
const mappings = await readMappings(config.get('mappingFiles'));
const buttonStatuses = createButtonStatuses(config.get('serialPorts').length, mappings);
const serialPorts = openSerialPorts(config.get('serialPorts'));
const serverPort = config.get('port');

for (let i = 0; i < ports.length; i++) {
ports[i].on('data', chunk => buttonStatuses[i].update(chunk));
for (let i = 0; i < serialPorts.length; i++) {
serialPorts[i].on('data', chunk => buttonStatuses[i].update(chunk));
}

const app = startApp(config, buttonStatuses);
const httpServer = http.createServer(app);
const wsServer = startWebSocketServer(config, httpServer, buttonStatuses);

console.log(chalk.yellow.bold(`Starting server on port ${PORT}...`));
console.log(chalk.blue.bold(`Visit http://localhost:${PORT}/ to see a demo status page.`));
httpServer.listen(PORT, function listening() {
console.log('Listening on %d', httpServer.address().port);
console.log(chalk.yellow(`Starting server on port ${serverPort}...`));
httpServer.listen(serverPort, function listening() {
console.log(chalk.yellow.bold(`Server started on port ${httpServer.address().port}.`));
console.log(chalk.blue.bold(`Visit http://localhost:${serverPort}/ to see a demo status page.`));
});
}

function readConfig(file) {
if (!fs.existsSync('./config.json')) {
console.warn(chalk.red.bold(`Could not find ${file}. Please copy` +
` ${file}.example to ${file}) and edit it.`));
throw new Error(`File not found: ${file}`)
}

return cjson.load(file);
}

function verifyConfig(config) {
if (!config.serialPorts || !config.serialPorts.length) {
console.warn(`No serial ports configured! Please specify serial port(s) in ${CONFIG_FILE}.`);
return false;
}
return true;
}

async function readMappings(mappingFiles) {
const mappings = [];
for (let file of mappingFiles) {
Expand All @@ -79,11 +54,11 @@ function createButtonStatuses(count, mappings) {
return [...Array(count)].map((_v, idx) => new ButtonStatus(mappings[idx]));
}

function openSerialPorts(portNames) {
function openSerialPorts(portNames, testmode) {
return portNames.map(portName => {
let serial = null;

if (TESTMODE) {
if (testmode) {
serial = new EventEmitter();
serial.send = serial.emit;
// Randomly update button states.
Expand Down Expand Up @@ -114,6 +89,8 @@ function openSerialPorts(portNames) {
}

function startApp(config, buttonStatuses) {
const testmode = config.get('testmode');
const serialPorts = config.get('serialPorts');
const app = express();
app.disable('x-powered-by');

Expand All @@ -127,10 +104,10 @@ function startApp(config, buttonStatuses) {
app.use('/', express.static('./public'));

app.get('/buttons', (_req, res) => {
let status = { testMode: TESTMODE, devices: [] };
let status = { testmode: testmode, devices: [] };
buttonStatuses.forEach((buttonStatus, idx) => {
status.devices[idx] = buttonStatus.toJSON();
status.devices[idx].name = config.serialPorts[idx];
status.devices[idx].name = serialPorts[idx];
});
res.send(JSON.stringify(status, null, ' '));
});
Expand All @@ -139,9 +116,14 @@ function startApp(config, buttonStatuses) {
}

function startWebSocketServer(config, httpServer, buttonStatuses) {
const testmode = config.get('testmode');
const serialPorts = config.get('serialPorts');
const wsServer = new WebSocket.Server({ server: httpServer });

wsServer.on('connection', socket => socket.send(JSON.stringify({name: 'testMode', isTestMode: TESTMODE})))
wsServer.on('connection', (socket, req) => {
socket.send(JSON.stringify({name: 'testMode', isTestMode: testmode}));
console.log(chalk.cyan(`WebSocket client connected! (${req.connection.remoteAddress})`));
});

wsServer.broadcast = function broadcast(data) {
wsServer.clients.forEach(function each(client) {
Expand All @@ -152,8 +134,8 @@ function startWebSocketServer(config, httpServer, buttonStatuses) {
};

for (var i = 0; i < buttonStatuses.length; i++) {
buttonStatuses[i].on('buttonDown', createMessageSender('buttonDown', i, config.serialPorts[i], wsServer));
buttonStatuses[i].on('buttonUp', createMessageSender('buttonUp', i, config.serialPorts[i], wsServer));
buttonStatuses[i].on('buttonDown', createMessageSender('buttonDown', i, serialPorts[i], wsServer));
buttonStatuses[i].on('buttonUp', createMessageSender('buttonUp', i, serialPorts[i], wsServer));
}

return wsServer;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "buttons-are-cool-node",
"version": "1.0.0",
"version": "1.0.0-rc6",
"description": "Node.js server to expose the 100 button USB serial board via HTTP and websockets.",
"main": "index.js",
"bin": "index.js",
Expand Down

0 comments on commit efd95e2

Please sign in to comment.