Merge pull request #253 from hybridgroup/feature/cppp-io-updates

Add event listings to API
This commit is contained in:
Ron Evans 2015-01-06 14:17:48 -08:00
commit fe4c899f7f
9 changed files with 137 additions and 22 deletions

View File

@ -35,7 +35,7 @@ var Adaptor = module.exports = function Adaptor(opts) {
this.details = {}; this.details = {};
_.forEach(opts, function(opt, name) { _.forEach(opts, function(opt, name) {
if (_.include(["robot", "name", "adaptor"], name)) { if (_.include(["robot", "name", "adaptor", "events"], name)) {
return; return;
} }

View File

@ -14,6 +14,12 @@ var Cylon = require("../cylon");
var router = module.exports = require("express").Router(); var router = module.exports = require("express").Router();
var eventHeaders = {
"Content-Type": "text/event-stream",
"Connection": "keep-alive",
"Cache-Control": "no-cache"
};
// Loads up the appropriate Robot/Device/Connection instances, if they are // Loads up the appropriate Robot/Device/Connection instances, if they are
// present in the route params. // present in the route params.
var load = function load(req, res, next) { var load = function load(req, res, next) {
@ -55,6 +61,26 @@ router.get("/", function(req, res) {
res.json({ MCP: Cylon.toJSON() }); res.json({ MCP: Cylon.toJSON() });
}); });
router.get("/events", function(req, res) {
res.json({ events: Cylon.events });
});
router.get("/events/:event", function(req, res) {
var event = req.params.event;
res.writeHead(200, eventHeaders);
var writeData = function(data) {
res.write("data: " + JSON.stringify(data) + "\n\n");
};
Cylon.on(event, writeData);
res.on("close", function() {
Cylon.removeListener(event, writeData);
});
});
router.get("/commands", function(req, res) { router.get("/commands", function(req, res) {
res.json({ commands: Object.keys(Cylon.commands) }); res.json({ commands: Object.keys(Cylon.commands) });
}); });
@ -81,6 +107,26 @@ router.all("/robots/:robot/commands/:command", load, function(req, res) {
router.runCommand(req, res, req.robot, command); router.runCommand(req, res, req.robot, command);
}); });
router.get("/robots/:robot/events", load, function(req, res) {
res.json({ events: req.robot.events });
});
router.all("/robots/:robot/events/:event", load, function(req, res) {
var event = req.params.event;
res.writeHead(200, eventHeaders);
var writeData = function(data) {
res.write("data: " + JSON.stringify(data) + "\n\n");
};
req.robot.on(event, writeData);
res.on("close", function() {
req.robot.removeListener(event, writeData);
});
});
router.get("/robots/:robot/devices", load, function(req, res) { router.get("/robots/:robot/devices", load, function(req, res) {
res.json({ devices: req.robot.toJSON().devices }); res.json({ devices: req.robot.toJSON().devices });
}); });
@ -89,14 +135,14 @@ router.get("/robots/:robot/devices/:device", load, function(req, res) {
res.json({ device: req.device }); res.json({ device: req.device });
}); });
router.get("/robots/:robot/devices/:device/events", load, function(req, res) {
res.json({ events: req.device.events });
});
router.get("/robots/:robot/devices/:device/events/:event", load, function(req, res) { router.get("/robots/:robot/devices/:device/events/:event", load, function(req, res) {
var event = req.params.event; var event = req.params.event;
res.writeHead(200, { res.writeHead(200, eventHeaders);
"Content-Type": "text/event-stream",
"Connection": "keep-alive",
"Cache-Control": "no-cache"
});
var writeData = function(data) { var writeData = function(data) {
res.write("data: " + JSON.stringify(data) + "\n\n"); res.write("data: " + JSON.stringify(data) + "\n\n");

View File

@ -16,23 +16,27 @@ var Logger = require("./logger"),
Utils = require("./utils"), Utils = require("./utils"),
_ = require("./lodash"); _ = require("./lodash");
var Cylon = module.exports = { var EventEmitter = require("events").EventEmitter;
Logger: Logger,
Driver: require("./driver"),
Adaptor: require("./adaptor"),
Utils: Utils,
IO: { var Cylon = module.exports = new EventEmitter();
Cylon.Logger = Logger;
Cylon.Driver = require("./driver");
Cylon.Adaptor = require("./adaptor");
Cylon.Utils = Utils;
Cylon.IO = {
DigitalPin: require("./io/digital-pin"), DigitalPin: require("./io/digital-pin"),
Utils: require("./io/utils") Utils: require("./io/utils")
},
api_instance: null,
robots: {},
commands: {}
}; };
Cylon.api_instance = null;
Cylon.robots = {};
Cylon.commands = {};
Cylon.events = [ "robot_added", "robot_removed" ];
// Public: Creates a new Robot // Public: Creates a new Robot
// //
// opts - hash of Robot attributes // opts - hash of Robot attributes
@ -61,6 +65,8 @@ Cylon.robot = function robot(opts) {
var bot = new Robot(opts); var bot = new Robot(opts);
this.robots[bot.name] = bot; this.robots[bot.name] = bot;
this.emit("robot_added", bot.name);
return bot; return bot;
}; };
@ -134,7 +140,8 @@ Cylon.halt = function halt(callback) {
Cylon.toJSON = function() { Cylon.toJSON = function() {
return { return {
robots: _.invoke(this.robots, "toJSON"), robots: _.invoke(this.robots, "toJSON"),
commands: _.keys(this.commands) commands: _.keys(this.commands),
events: this.events
}; };
}; };

View File

@ -28,6 +28,7 @@ var Driver = module.exports = function Driver(opts) {
this.connection = opts.connection; this.connection = opts.connection;
this.commands = {}; this.commands = {};
this.events = [];
// some default options // some default options
this.pin = opts.pin; this.pin = opts.pin;
@ -36,7 +37,7 @@ var Driver = module.exports = function Driver(opts) {
this.details = {}; this.details = {};
_.forEach(opts, function(opt, name) { _.forEach(opts, function(opt, name) {
if (_.include(["robot", "name", "connection", "driver"], name)) { if (_.include(["robot", "name", "connection", "driver", "events"], name)) {
return; return;
} }
@ -74,6 +75,7 @@ Driver.prototype.toJSON = function() {
driver: this.constructor.name || this.name, driver: this.constructor.name || this.name,
connection: this.connection.name, connection: this.connection.name,
commands: _.keys(this.commands), commands: _.keys(this.commands),
events: this.events,
details: this.details details: this.details
}; };
}; };

View File

@ -120,7 +120,8 @@ Robot.prototype.toJSON = function() {
name: this.name, name: this.name,
connections: _.invoke(this.connections, "toJSON"), connections: _.invoke(this.connections, "toJSON"),
devices: _.invoke(this.devices, "toJSON"), devices: _.invoke(this.devices, "toJSON"),
commands: _.keys(this.commands) commands: _.keys(this.commands),
events: _.isArray(this.events) ? this.events : []
}; };
}; };

View File

@ -17,6 +17,8 @@ var Ping = module.exports = function Ping() {
this.commands = { this.commands = {
ping: this.ping ping: this.ping
}; };
this.events = ["ping"];
}; };
Utils.subclass(Ping, Driver); Utils.subclass(Ping, Driver);

View File

@ -181,5 +181,9 @@ describe("Cylon", function() {
it("contains an array of MCP commands", function() { it("contains an array of MCP commands", function() {
expect(json.commands).to.be.eql(["echo"]); expect(json.commands).to.be.eql(["echo"]);
}); });
it("contains an array of MCP events", function() {
expect(json.events).to.be.eql(["robot_added", "robot_removed"]);
});
}); });
}); });

View File

@ -30,6 +30,10 @@ describe("Driver", function() {
expect(driver.commands).to.be.eql({}); expect(driver.commands).to.be.eql({});
}); });
it("sets @events to an empty array by default", function() {
expect(driver.events).to.be.eql([]);
});
it("sets @interval to 10ms by default, or the provided value", function() { it("sets @interval to 10ms by default, or the provided value", function() {
expect(driver.interval).to.be.eql(10); expect(driver.interval).to.be.eql(10);
@ -43,6 +47,46 @@ describe("Driver", function() {
}); });
}); });
describe("#toJSON", function() {
var driver, json;
beforeEach(function() {
driver = new Driver({
connection: { name: "conn" },
name: "name",
port: "3000"
});
json = driver.toJSON();
});
it("returns an object", function() {
expect(json).to.be.a("object");
});
it("contains the driver's name", function() {
expect(json.name).to.eql("name");
});
it("contains the driver's constructor name", function() {
expect(json.driver).to.eql("Driver");
});
it("contains the driver's connection name", function() {
expect(json.connection).to.eql("conn");
});
it("contains the driver's commands", function() {
expect(json.commands).to.eql(Object.keys(driver.commands));
});
it("contains the driver's events, or an empty array", function() {
expect(json.events).to.eql([]);
driver.events = ["hello", "world"];
expect(driver.toJSON().events).to.be.eql(["hello", "world"]);
});
});
describe("#setupCommands", function() { describe("#setupCommands", function() {
beforeEach(function() { beforeEach(function() {
driver.proxyMethods = spy(); driver.proxyMethods = spy();

View File

@ -215,7 +215,9 @@ describe("Robot", function() {
devices: { devices: {
ping: { driver: "ping" } ping: { driver: "ping" }
} },
events: ["hello", "world"]
}); });
var json = bot.toJSON(); var json = bot.toJSON();
@ -239,6 +241,13 @@ describe("Robot", function() {
it("contains the robot's connections", function() { it("contains the robot's connections", function() {
expect(json.connections).to.eql([bot.connections.loopback.toJSON()]); expect(json.connections).to.eql([bot.connections.loopback.toJSON()]);
}); });
it("contains the robot's events, or an empty array", function() {
expect(json.events).to.eql(["hello", "world"]);
var bot = new Robot();
expect(bot.toJSON().events).to.be.eql([]);
});
}); });
describe("#connection", function() { describe("#connection", function() {