Merge branch 'dev'
* dev: Bump version to "0.17.0" Update and lock Robeaux version Add basic error handling Simplify coercion of Robot to JSON Correctly respond w/ 404 error Add 'details' subsection to Device/Connection Emit 'ping' when Ping is told to #ping Add MCP commands route, fix /robots route Only look for command params in POST bodies WIP on Threepio changes Namespace API routes under '/api' WIP on commands + JSON serialization Fix typo Refer to device connections by name in JSON format Remove connection_id from Connection class Defer to Utils#fetch for getting ENV state Slightly clean up Connection Clean up robot initialization Require a connection if a robot has any devices Misc. small refactors
This commit is contained in:
commit
ac737e51ba
|
@ -246,6 +246,8 @@ on the [https://github.com/hybridgroup/cylon-site](https://github.com/hybridgrou
|
||||||
|
|
||||||
## Release History
|
## Release History
|
||||||
|
|
||||||
|
Version 0.17.0 - Updates to API to match CPPP-IO spec
|
||||||
|
|
||||||
Version 0.16.0 - New IO Utils, removal of Utils#bind, add Adaptor#_noop method.
|
Version 0.16.0 - New IO Utils, removal of Utils#bind, add Adaptor#_noop method.
|
||||||
|
|
||||||
Version 0.15.1 - Fixed issue with the API on Tessel
|
Version 0.15.1 - Fixed issue with the API on Tessel
|
||||||
|
|
|
@ -12,11 +12,6 @@ var Basestar = require('./basestar'),
|
||||||
Logger = require('./logger'),
|
Logger = require('./logger'),
|
||||||
Utils = require('./utils');
|
Utils = require('./utils');
|
||||||
|
|
||||||
// The Adaptor class is a base class for Adaptor classes in external Cylon
|
|
||||||
// modules to use. It offers basic functions for connecting/disconnecting that
|
|
||||||
// descendant classes can use.
|
|
||||||
var Adaptor;
|
|
||||||
|
|
||||||
// Public: Creates a new Adaptor
|
// Public: Creates a new Adaptor
|
||||||
//
|
//
|
||||||
// opts - hash of acceptable params
|
// opts - hash of acceptable params
|
||||||
|
@ -24,10 +19,8 @@ var Adaptor;
|
||||||
// connection - Connection the adaptor will use to proxy commands/events
|
// connection - Connection the adaptor will use to proxy commands/events
|
||||||
//
|
//
|
||||||
// Returns a new Adaptor
|
// Returns a new Adaptor
|
||||||
module.exports = Adaptor = function Adaptor(opts) {
|
var Adaptor = module.exports = function Adaptor(opts) {
|
||||||
if (opts == null) {
|
opts = opts || {};
|
||||||
opts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.connection = opts.connection;
|
this.connection = opts.connection;
|
||||||
|
@ -46,7 +39,6 @@ Adaptor.prototype.commands = [];
|
||||||
Adaptor.prototype.connect = function(callback) {
|
Adaptor.prototype.connect = function(callback) {
|
||||||
Logger.info("Connecting to adaptor '" + this.name + "'.");
|
Logger.info("Connecting to adaptor '" + this.name + "'.");
|
||||||
callback(null);
|
callback(null);
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public: Disconnects from the adaptor
|
// Public: Disconnects from the adaptor
|
||||||
|
@ -57,7 +49,7 @@ Adaptor.prototype.connect = function(callback) {
|
||||||
Adaptor.prototype.disconnect = function(callback) {
|
Adaptor.prototype.disconnect = function(callback) {
|
||||||
Logger.info("Disconnecting from adaptor '" + this.name + "'.");
|
Logger.info("Disconnecting from adaptor '" + this.name + "'.");
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
callback();
|
callback(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public: Voids all command functions so they do not interact
|
// Public: Voids all command functions so they do not interact
|
||||||
|
@ -65,7 +57,7 @@ Adaptor.prototype.disconnect = function(callback) {
|
||||||
//
|
//
|
||||||
// Returns nothing
|
// Returns nothing
|
||||||
Adaptor.prototype._noop = function() {
|
Adaptor.prototype._noop = function() {
|
||||||
this.commands.forEach((function(command) {
|
this.commands.forEach(function(command) {
|
||||||
this[command] = function() { return null; };
|
this[command] = function() {};
|
||||||
}).bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
20
lib/api.js
20
lib/api.js
|
@ -43,26 +43,22 @@ var API = module.exports = function API(opts) {
|
||||||
|
|
||||||
// extracts command params from request
|
// extracts command params from request
|
||||||
this.express.use(function(req, res, next) {
|
this.express.use(function(req, res, next) {
|
||||||
var method = req.method.toLowerCase(),
|
|
||||||
container = {};
|
|
||||||
|
|
||||||
req.commandParams = [];
|
req.commandParams = [];
|
||||||
|
|
||||||
if (method === 'get' || Object.keys(req.query).length > 0) {
|
for (var p in req.body) {
|
||||||
container = req.query;
|
req.commandParams.push(req.body[p]);
|
||||||
} else if (typeof(req.body) === 'object') {
|
|
||||||
container = req.body;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var p in container) {
|
|
||||||
req.commandParams.push(container[p]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// load route definitions
|
// load route definitions
|
||||||
this.express.use('/', require('./api/routes'));
|
this.express.use('/api', require('./api/routes'));
|
||||||
|
|
||||||
|
// error handling
|
||||||
|
this.express.use(function(err, req, res, next) {
|
||||||
|
res.json(500, { error: err.message || "An error occured."})
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
API.prototype.defaults = {
|
API.prototype.defaults = {
|
||||||
|
|
|
@ -20,58 +20,65 @@ var load = function load(req, res, next) {
|
||||||
if (robot) {
|
if (robot) {
|
||||||
req.robot = Cylon.robots[robot];
|
req.robot = Cylon.robots[robot];
|
||||||
if (!req.robot) {
|
if (!req.robot) {
|
||||||
return res.json({ error: "No Robot found with the name " + robot });
|
return res.json(404, { error: "No Robot found with the name " + robot });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device) {
|
if (device) {
|
||||||
req.device = req.robot.devices[device];
|
req.device = req.robot.devices[device];
|
||||||
if (!req.device) {
|
if (!req.device) {
|
||||||
return res.json({ error: "No device found with the name " + device });
|
return res.json(404, { error: "No device found with the name " + device });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection) {
|
if (connection) {
|
||||||
req.connection = req.robot.connections[connection];
|
req.connection = req.robot.connections[connection];
|
||||||
if (!req.connection) {
|
if (!req.connection) {
|
||||||
return res.json({ error: "No connection found with the name " + connection });
|
return res.json(404, { error: "No connection found with the name " + connection });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
router.get("/", function(req, res) {
|
||||||
|
res.json({ MCP: Cylon });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/commands", function(req, res) {
|
||||||
|
res.json({ commands: Object.keys(Cylon.commands) });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/commands/:command", function(req, res) {
|
||||||
|
var command = req.params.command;
|
||||||
|
var result = Cylon.commands[command].apply(Cylon, req.commandParams);
|
||||||
|
res.json({ result: result });
|
||||||
|
});
|
||||||
|
|
||||||
router.get("/robots", function(req, res) {
|
router.get("/robots", function(req, res) {
|
||||||
var data = [];
|
res.json({ robots: Cylon.toJSON().robots });
|
||||||
|
|
||||||
for (var bot in Cylon.robots) {
|
|
||||||
data.push(Cylon.robots[bot]);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(data);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot", load, function(req, res) {
|
router.get("/robots/:robot", load, function(req, res) {
|
||||||
res.json(req.robot);
|
res.json({ robot: req.robot });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/commands", load, function(req, res) {
|
router.get("/robots/:robot/commands", load, function(req, res) {
|
||||||
res.json(req.robot.toJSON().commands);
|
res.json({ commands: req.robot.commands });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.all("/robots/:robot/commands/:command", load, function(req, res) {
|
router.all("/robots/:robot/commands/:command", load, function(req, res) {
|
||||||
var command = req.params.command;
|
var command = req.params.command;
|
||||||
|
|
||||||
var result = req.robot[command].apply(req.robot, req.commandParams);
|
var result = req.robot[command].apply(req.robot, req.commandParams);
|
||||||
res.json({ result: result });
|
res.json({ result: result });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/devices", load, function(req, res) {
|
router.get("/robots/:robot/devices", load, function(req, res) {
|
||||||
res.json(req.robot.toJSON().devices);
|
res.json({ devices: req.robot.toJSON().devices });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/devices/:device", load, function(req, res) {
|
router.get("/robots/:robot/devices/:device", load, function(req, res) {
|
||||||
res.json(req.device);
|
res.json({ device: req.device });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/devices/:device/events/:event", load, function(req, res) {
|
router.get("/robots/:robot/devices/:device/events/:event", load, function(req, res) {
|
||||||
|
@ -95,7 +102,7 @@ router.get("/robots/:robot/devices/:device/events/:event", load, function(req, r
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/devices/:device/commands", load, function(req, res) {
|
router.get("/robots/:robot/devices/:device/commands", load, function(req, res) {
|
||||||
res.json(req.device.toJSON().commands);
|
res.json({ commands: req.device.toJSON().commands });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.all("/robots/:robot/devices/:device/commands/:command", load, function(req, res) {
|
router.all("/robots/:robot/devices/:device/commands/:command", load, function(req, res) {
|
||||||
|
@ -106,9 +113,9 @@ router.all("/robots/:robot/devices/:device/commands/:command", load, function(re
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/connections", load, function(req, res) {
|
router.get("/robots/:robot/connections", load, function(req, res) {
|
||||||
res.json(req.robot.toJSON().connections);
|
res.json({ connections: req.robot.toJSON().connections });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/robots/:robot/connections/:connection", load, function(req, res) {
|
router.get("/robots/:robot/connections/:connection", load, function(req, res) {
|
||||||
res.json(req.connection);
|
res.json({ connection: req.connection });
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,39 +8,8 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Public: Fetches a variable from the environment, returning a provided value if
|
var Utils = require('./utils');
|
||||||
// it's not set.
|
|
||||||
//
|
|
||||||
// variable - variable to fetch from the environment
|
|
||||||
// defaultValue - value to return if the ENV variable isn't set
|
|
||||||
//
|
|
||||||
// Examples
|
|
||||||
//
|
|
||||||
// process.env["CYLON_TEST"] #=> undefined
|
|
||||||
// fetch("CYLON_TEST", "not set")
|
|
||||||
// #=> "not set"
|
|
||||||
//
|
|
||||||
// process.env["CYLON_TEST"] #=> false
|
|
||||||
// fetch("CYLON_TEST", true)
|
|
||||||
// #=> false
|
|
||||||
//
|
|
||||||
// process.env["CYLON_TEST"] #=> true
|
|
||||||
// fetch("CYLON_TEST", false)
|
|
||||||
// #=> true
|
|
||||||
//
|
|
||||||
// Returns the env var or default value
|
|
||||||
var fetch = function(variable, defaultValue) {
|
|
||||||
if (defaultValue == null) {
|
|
||||||
defaultValue = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env[variable] != null) {
|
|
||||||
return process.env[variable];
|
|
||||||
} else {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
testing_mode: fetch('CYLON_TEST')
|
testing_mode: Utils.fetch(process.env, 'CYLON_TEST', false)
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,38 +13,33 @@ var EventEmitter = require('events').EventEmitter;
|
||||||
var Logger = require('./logger'),
|
var Logger = require('./logger'),
|
||||||
Utils = require('./utils');
|
Utils = require('./utils');
|
||||||
|
|
||||||
// The Connection class represents the interface to
|
|
||||||
// a specific group of hardware devices. Examples would be an
|
|
||||||
// Arduino, a Sphero, or an ARDrone.
|
|
||||||
var Connection;
|
|
||||||
|
|
||||||
// Public: Creates a new Connection
|
// Public: Creates a new Connection
|
||||||
//
|
//
|
||||||
// opts - hash of acceptable params:
|
// opts - hash of acceptable params:
|
||||||
// id - string ID for the connection
|
|
||||||
// robot - Robot the Connection belongs to
|
// robot - Robot the Connection belongs to
|
||||||
// name - name for the connection
|
// name - name for the connection
|
||||||
// adaptor - string module name of the adaptor to be set up
|
// adaptor - string module name of the adaptor to be set up
|
||||||
// port - string port to use for the Connection
|
// port - string port to use for the Connection
|
||||||
//
|
//
|
||||||
// Returns the newly set-up connection
|
// Returns the newly set-up connection
|
||||||
module.exports = Connection = function Connection(opts) {
|
var Connection = module.exports = function Connection(opts) {
|
||||||
if (opts == null) {
|
opts = opts || {};
|
||||||
opts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.id == null) {
|
|
||||||
opts.id = Math.floor(Math.random() * 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.connect = this.connect.bind(this);
|
this.connect = this.connect.bind(this);
|
||||||
|
|
||||||
this.robot = opts.robot;
|
this.robot = opts.robot;
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.connection_id = opts.id;
|
|
||||||
this.port = opts.port;
|
this.port = opts.port;
|
||||||
this.adaptor = this.initAdaptor(opts);
|
this.adaptor = this.initAdaptor(opts);
|
||||||
|
|
||||||
|
this.details = {};
|
||||||
|
|
||||||
|
for (var opt in opts) {
|
||||||
|
if (['robot', 'name', 'adaptor'].indexOf(opt) < 0) {
|
||||||
|
this.details[opt] = opts[opt];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this);
|
Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,9 +51,8 @@ Utils.subclass(Connection, EventEmitter);
|
||||||
Connection.prototype.toJSON = function() {
|
Connection.prototype.toJSON = function() {
|
||||||
return {
|
return {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
port: this.port,
|
|
||||||
adaptor: this.adaptor.constructor.name || this.adaptor.name,
|
adaptor: this.adaptor.constructor.name || this.adaptor.name,
|
||||||
connection_id: this.connection_id
|
details: this.details
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,12 +95,7 @@ Connection.prototype.initAdaptor = function(opts) {
|
||||||
//
|
//
|
||||||
// Returns nothing
|
// Returns nothing
|
||||||
Connection.prototype.halt = function(callback) {
|
Connection.prototype.halt = function(callback) {
|
||||||
var msg = "Halting adaptor " + this.name;
|
var msg = this._logstring("Halting adaptor");
|
||||||
|
|
||||||
if (this.port != null) {
|
|
||||||
msg += " on port " + this.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.info(msg);
|
Logger.info(msg);
|
||||||
return this.disconnect(callback);
|
return this.disconnect(callback);
|
||||||
};
|
};
|
||||||
|
|
16
lib/cylon.js
16
lib/cylon.js
|
@ -29,7 +29,8 @@ var Cylon = module.exports = {
|
||||||
|
|
||||||
api_instance: null,
|
api_instance: null,
|
||||||
|
|
||||||
robots: {}
|
robots: {},
|
||||||
|
commands: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public: Creates a new Robot
|
// Public: Creates a new Robot
|
||||||
|
@ -94,6 +95,19 @@ Cylon.halt = function halt(callback) {
|
||||||
Async.parallel(fns, callback);
|
Async.parallel(fns, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Cylon.toJSON = function() {
|
||||||
|
var robots = [];
|
||||||
|
|
||||||
|
for (var bot in this.robots) {
|
||||||
|
robots.push(this.robots[bot]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
robots: robots,
|
||||||
|
commands: Object.keys(this.commands)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
var readline = require("readline"),
|
var readline = require("readline"),
|
||||||
io = { input: process.stdin, output: process.stdout };
|
io = { input: process.stdin, output: process.stdout };
|
||||||
|
|
|
@ -37,6 +37,14 @@ var Device = module.exports = function Device(opts) {
|
||||||
this.connection = this.determineConnection(opts.connection) || this.defaultConnection();
|
this.connection = this.determineConnection(opts.connection) || this.defaultConnection();
|
||||||
this.driver = this.initDriver(opts);
|
this.driver = this.initDriver(opts);
|
||||||
|
|
||||||
|
this.details = {};
|
||||||
|
|
||||||
|
for (var opt in opts) {
|
||||||
|
if (['robot', 'name', 'connection', 'driver'].indexOf(opt) < 0) {
|
||||||
|
this.details[opt] = opts[opt];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Utils.proxyFunctionsToObject(this.driver.commands, this.driver, this);
|
Utils.proxyFunctionsToObject(this.driver.commands, this.driver, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,9 +85,9 @@ Device.prototype.toJSON = function() {
|
||||||
return {
|
return {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
driver: this.driver.constructor.name || this.driver.name,
|
driver: this.driver.constructor.name || this.driver.name,
|
||||||
pin: this.pin,
|
connection: this.connection.name,
|
||||||
connection: this.connection.toJSON(),
|
commands: this.driver.commands,
|
||||||
commands: this.driver.commands
|
details: this.details
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ Driver.prototype.commands = [];
|
||||||
Driver.prototype.start = function(callback) {
|
Driver.prototype.start = function(callback) {
|
||||||
Logger.info("Driver " + this.name + " started.");
|
Logger.info("Driver " + this.name + " started.");
|
||||||
callback(null);
|
callback(null);
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public: Halts the driver
|
// Public: Halts the driver
|
||||||
|
@ -50,5 +49,5 @@ Driver.prototype.start = function(callback) {
|
||||||
Driver.prototype.halt = function(callback) {
|
Driver.prototype.halt = function(callback) {
|
||||||
Logger.info("Driver " + this.name + " halted.");
|
Logger.info("Driver " + this.name + " halted.");
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
callback();
|
callback(null);
|
||||||
};
|
};
|
||||||
|
|
60
lib/robot.js
60
lib/robot.js
|
@ -49,9 +49,7 @@ var missingModuleError = function(module) {
|
||||||
// Utils.every 1.second(), ->
|
// Utils.every 1.second(), ->
|
||||||
// me.sphero.roll 60, Math.floor(Math.random() * 360//
|
// me.sphero.roll 60, Math.floor(Math.random() * 360//
|
||||||
var Robot = module.exports = function Robot(opts) {
|
var Robot = module.exports = function Robot(opts) {
|
||||||
if (opts == null) {
|
opts = opts || {};
|
||||||
opts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var methods = [
|
var methods = [
|
||||||
"toString",
|
"toString",
|
||||||
|
@ -71,7 +69,7 @@ var Robot = module.exports = function Robot(opts) {
|
||||||
this[method] = this[method].bind(this);
|
this[method] = this[method].bind(this);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.name = opts.name || this.constructor.randomName();
|
this.name = opts.name || Robot.randomName();
|
||||||
this.connections = {};
|
this.connections = {};
|
||||||
this.devices = {};
|
this.devices = {};
|
||||||
this.adaptors = {};
|
this.adaptors = {};
|
||||||
|
@ -89,6 +87,13 @@ var Robot = module.exports = function Robot(opts) {
|
||||||
this.initConnections(opts.connection || opts.connections);
|
this.initConnections(opts.connection || opts.connections);
|
||||||
this.initDevices(opts.device || opts.devices);
|
this.initDevices(opts.device || opts.devices);
|
||||||
|
|
||||||
|
var hasDevices = !!Object.keys(this.devices).length,
|
||||||
|
hasConnections = !!Object.keys(this.connections).length;
|
||||||
|
|
||||||
|
if (hasDevices && !hasConnections) {
|
||||||
|
throw new Error("No connections specified");
|
||||||
|
}
|
||||||
|
|
||||||
for (var n in opts) {
|
for (var n in opts) {
|
||||||
var func = opts[n],
|
var func = opts[n],
|
||||||
reserved = ['connection', 'connections', 'device', 'devices', 'work'];
|
reserved = ['connection', 'connections', 'device', 'devices', 'work'];
|
||||||
|
@ -123,23 +128,16 @@ Robot.randomName = function() {
|
||||||
//
|
//
|
||||||
// Returns an Object containing Robot data
|
// Returns an Object containing Robot data
|
||||||
Robot.prototype.toJSON = function() {
|
Robot.prototype.toJSON = function() {
|
||||||
var connections = (function() {
|
var devices = [],
|
||||||
var results = [];
|
connections = [];
|
||||||
for (var n in this.connections) {
|
|
||||||
var conn = this.connections[n];
|
|
||||||
results.push(conn.toJSON());
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}).call(this);
|
|
||||||
|
|
||||||
var devices = (function() {
|
for (var n in this.connections) {
|
||||||
var results = [];
|
connections.push(this.connections[n]);
|
||||||
for (var n in this.devices) {
|
}
|
||||||
var device = this.devices[n];
|
|
||||||
results.push(device.toJSON());
|
for (var n in this.devices) {
|
||||||
|
devices.push(this.devices[n]);
|
||||||
}
|
}
|
||||||
return results;
|
|
||||||
}).call(this);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
|
@ -163,12 +161,11 @@ Robot.prototype.initConnections = function(connections) {
|
||||||
|
|
||||||
connections = [].concat(connections);
|
connections = [].concat(connections);
|
||||||
|
|
||||||
for (var i = 0; i < connections.length; i++) {
|
connections.forEach(function(conn) {
|
||||||
var connection = connections[i];
|
Logger.info("Initializing connection '" + conn.name + "'.");
|
||||||
Logger.info("Initializing connection '" + connection.name + "'.");
|
conn['robot'] = this;
|
||||||
connection['robot'] = this;
|
this.connections[conn.name] = new Connection(conn);
|
||||||
this.connections[connection.name] = new Connection(connection);
|
}.bind(this));
|
||||||
}
|
|
||||||
|
|
||||||
return this.connections;
|
return this.connections;
|
||||||
};
|
};
|
||||||
|
@ -187,20 +184,15 @@ Robot.prototype.initDevices = function(devices) {
|
||||||
|
|
||||||
devices = [].concat(devices);
|
devices = [].concat(devices);
|
||||||
|
|
||||||
for (var i = 0; i < devices.length; i++) {
|
devices.forEach(function(device) {
|
||||||
var device = devices[i];
|
|
||||||
Logger.info("Initializing device '" + device.name + "'.");
|
Logger.info("Initializing device '" + device.name + "'.");
|
||||||
device['robot'] = this;
|
device['robot'] = this;
|
||||||
this.devices[device.name] = this._createDevice(device);
|
this.devices[device.name] = new Device(device);
|
||||||
}
|
}.bind(this));
|
||||||
|
|
||||||
return this.devices;
|
return this.devices;
|
||||||
};
|
};
|
||||||
|
|
||||||
Robot.prototype._createDevice = function(device) {
|
|
||||||
return new Device(device);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Public: Starts the Robot working.
|
// Public: Starts the Robot working.
|
||||||
//
|
//
|
||||||
// Starts the connections, devices, and work.
|
// Starts the connections, devices, and work.
|
||||||
|
@ -402,7 +394,7 @@ Robot.prototype.requireDriver = function(driverName) {
|
||||||
// moduleName - name of the Node module to require
|
// moduleName - name of the Node module to require
|
||||||
// driverName - name of the driver to register the moduleName under
|
// driverName - name of the driver to register the moduleName under
|
||||||
//
|
//
|
||||||
// Returns the registered module nam//
|
// Returns the registered module name
|
||||||
Robot.prototype.registerDriver = function(moduleName, driverName) {
|
Robot.prototype.registerDriver = function(moduleName, driverName) {
|
||||||
if (this.drivers[driverName] == null) {
|
if (this.drivers[driverName] == null) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -21,6 +21,7 @@ module.exports = Ping = function Ping() {
|
||||||
Utils.subclass(Ping, Driver);
|
Utils.subclass(Ping, Driver);
|
||||||
|
|
||||||
Ping.prototype.ping = function() {
|
Ping.prototype.ping = function() {
|
||||||
|
this.device.emit('ping', 'ping');
|
||||||
return "pong";
|
return "pong";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
30
lib/utils.js
30
lib/utils.js
|
@ -119,7 +119,7 @@ var Utils = module.exports = {
|
||||||
force = false;
|
force = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fn = function(method) {
|
var proxy = function(method) {
|
||||||
return base[method] = function() {
|
return base[method] = function() {
|
||||||
return target[method].apply(target, arguments);
|
return target[method].apply(target, arguments);
|
||||||
};
|
};
|
||||||
|
@ -132,7 +132,7 @@ var Utils = module.exports = {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn(method);
|
proxy(method);
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
},
|
},
|
||||||
|
@ -145,9 +145,7 @@ var Utils = module.exports = {
|
||||||
//
|
//
|
||||||
// Returns base
|
// Returns base
|
||||||
proxyTestStubs: function proxyTestStubs(methods, base) {
|
proxyTestStubs: function proxyTestStubs(methods, base) {
|
||||||
if (base == null) {
|
base = base || this;
|
||||||
base = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
methods.forEach(function(method) {
|
methods.forEach(function(method) {
|
||||||
base[method] = function() {
|
base[method] = function() {
|
||||||
|
@ -290,9 +288,11 @@ var addCoreExtensions = function addCoreExtensions() {
|
||||||
var val = (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end));
|
var val = (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end));
|
||||||
|
|
||||||
if (val > 1) {
|
if (val > 1) {
|
||||||
val = 1;
|
return 1;
|
||||||
} else if (val < 0){
|
}
|
||||||
val = 0;
|
|
||||||
|
if (val < 0){
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
|
@ -312,13 +312,15 @@ var addCoreExtensions = function addCoreExtensions() {
|
||||||
Number.prototype.toScale = function(start, end) {
|
Number.prototype.toScale = function(start, end) {
|
||||||
var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end);
|
var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end);
|
||||||
|
|
||||||
if (i < Math.min(start, end)) {
|
if (i < start) {
|
||||||
return Math.min(start, end);
|
return start;
|
||||||
} else if (i > Math.max(start,end)){
|
|
||||||
return Math.max(start, end);
|
|
||||||
} else {
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i > end) {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cylon",
|
"name": "cylon",
|
||||||
"version": "0.16.0",
|
"version": "0.17.0",
|
||||||
"main": "lib/cylon.js",
|
"main": "lib/cylon.js",
|
||||||
"description": "A JavaScript robotics framework using Node.js",
|
"description": "A JavaScript robotics framework using Node.js",
|
||||||
"homepage": "http://cylonjs.com",
|
"homepage": "http://cylonjs.com",
|
||||||
|
@ -46,6 +46,6 @@
|
||||||
"async": "0.7.0",
|
"async": "0.7.0",
|
||||||
"express": "4.4.1",
|
"express": "4.4.1",
|
||||||
"body-parser": "1.3.0",
|
"body-parser": "1.3.0",
|
||||||
"robeaux": ">= 0.1.0"
|
"robeaux": "0.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,17 +38,12 @@ describe("Connection", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the connection's port", function() {
|
it("contains the connection's port", function() {
|
||||||
expect(json.port).to.be.eql("/dev/null");
|
expect(json.details.port).to.be.eql("/dev/null");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the connection's adaptor name", function() {
|
it("contains the connection's adaptor name", function() {
|
||||||
expect(json.adaptor).to.be.eql("Loopback");
|
expect(json.adaptor).to.be.eql("Loopback");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the connection's ID", function() {
|
|
||||||
var id = connection.connection_id;
|
|
||||||
expect(json.connection_id).to.be.eql(id);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#connect", function() {
|
describe("#connect", function() {
|
||||||
|
@ -113,7 +108,7 @@ describe("Connection", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("logs that it's halting the adaptor", function() {
|
it("logs that it's halting the adaptor", function() {
|
||||||
var message = "Halting adaptor loopback on port /dev/null";
|
var message = "Halting adaptor 'loopback' on port /dev/null.";
|
||||||
expect(Logger.info).to.be.calledWith(message);
|
expect(Logger.info).to.be.calledWith(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ describe("Cylon", function() {
|
||||||
it("sets Driver to the Driver module", function() {
|
it("sets Driver to the Driver module", function() {
|
||||||
expect(Cylon.Driver).to.be.eql(Driver);
|
expect(Cylon.Driver).to.be.eql(Driver);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @api_instance to null by default", function() {
|
it("sets @api_instance to null by default", function() {
|
||||||
expect(Cylon.api_instance).to.be.eql(null);
|
expect(Cylon.api_instance).to.be.eql(null);
|
||||||
|
@ -31,6 +30,11 @@ describe("Cylon", function() {
|
||||||
expect(Cylon.robots).to.be.eql({});
|
expect(Cylon.robots).to.be.eql({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sets @robots to an empty object by default", function() {
|
||||||
|
expect(Cylon.commands).to.be.eql({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#robot", function() {
|
describe("#robot", function() {
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
Cylon.robots = {};
|
Cylon.robots = {};
|
||||||
|
@ -98,4 +102,26 @@ describe("Cylon", function() {
|
||||||
expect(bot2.halt).to.be.called;
|
expect(bot2.halt).to.be.called;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#toJSON", function() {
|
||||||
|
var json, bot1, bot2, echo;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
bot1 = {};
|
||||||
|
bot2 = {};
|
||||||
|
|
||||||
|
Cylon.robots = { 'bot1': bot1, 'bot2': bot2 };
|
||||||
|
Cylon.commands.echo = echo = function(arg) { return arg; };
|
||||||
|
|
||||||
|
json = Cylon.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains all robots the MCP knows about", function() {
|
||||||
|
expect(json.robots).to.be.eql([bot1, bot2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains an array of MCP commands", function() {
|
||||||
|
expect(json.commands).to.be.eql(['echo']);
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,7 +41,7 @@ describe("Device", function() {
|
||||||
expect(device.pin).to.be.eql(13);
|
expect(device.pin).to.be.eql(13);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets @connection to the specified connection on the Robot", function() {
|
it("sets @connection to the name of the specified connection on the Robot", function() {
|
||||||
expect(device.connection).to.be.eql(connection);
|
expect(device.connection).to.be.eql(connection);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -118,15 +118,15 @@ describe("Device", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the device's pin", function() {
|
it("contains the device's pin", function() {
|
||||||
expect(json.pin).to.be.eql(device.pin);
|
expect(json.details.pin).to.be.eql(device.pin);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the device's driver name", function() {
|
it("contains the device's driver name", function() {
|
||||||
expect(json.driver).to.be.eql('Ping');
|
expect(json.driver).to.be.eql('Ping');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the device's connection json", function() {
|
it("contains the device's connection name", function() {
|
||||||
expect(json.connection).to.be.eql(device.connection.toJSON());
|
expect(json.connection).to.be.eql('loopback');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the device's driver commands", function() {
|
it("contains the device's driver commands", function() {
|
||||||
|
|
|
@ -71,6 +71,19 @@ describe("Robot", function() {
|
||||||
expect(robot.extraFunction).to.be.eql(extraFunction);
|
expect(robot.extraFunction).to.be.eql(extraFunction);
|
||||||
expect(robot.extraValue).to.be.eql("Hello World");
|
expect(robot.extraValue).to.be.eql("Hello World");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context("if there are devices but no connections", function() {
|
||||||
|
it('throws an error', function() {
|
||||||
|
var fn = function() {
|
||||||
|
return new Robot({
|
||||||
|
name: 'BrokenBot',
|
||||||
|
device: { name: 'ping', driver: 'ping' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(fn).to.throw(Error, "No connections specified");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("all work and no play", function() {
|
describe("all work and no play", function() {
|
||||||
|
@ -106,13 +119,11 @@ describe("Robot", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the robot's devices", function() {
|
it("contains the robot's devices", function() {
|
||||||
var deviceJSON = bot.devices.ping.toJSON();
|
expect(json.devices).to.eql([bot.devices.ping]);
|
||||||
expect(json.devices).to.eql([deviceJSON]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("contains the robot's connections", function() {
|
it("contains the robot's connections", function() {
|
||||||
var connectionJSON = bot.connections.loopback.toJSON();
|
expect(json.connections).to.eql([bot.connections.loopback]);
|
||||||
expect(json.connections).to.eql([connectionJSON]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -229,6 +240,7 @@ describe("Robot", function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
bot = new Robot({
|
bot = new Robot({
|
||||||
|
connection: [{ name: 'loopback', adaptor: 'loopback' }],
|
||||||
devices: [
|
devices: [
|
||||||
{ name: 'alpha', driver: 'ping' },
|
{ name: 'alpha', driver: 'ping' },
|
||||||
{ name: 'bravo', driver: 'ping' }
|
{ name: 'bravo', driver: 'ping' }
|
||||||
|
|
Loading…
Reference in New Issue