commit
333327aa63
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* api
|
||||
* cylonjs.com
|
||||
*
|
||||
* Copyright (c) 2013 The Hybrid Group
|
||||
* Licensed under the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var express, namespace;
|
||||
|
||||
express = require('express.io');
|
||||
|
||||
namespace = require('node-namespace');
|
||||
|
||||
namespace("Api", function() {
|
||||
return this.Server = (function() {
|
||||
var master;
|
||||
|
||||
master = null;
|
||||
|
||||
function Server(opts) {
|
||||
var _this = this;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
this.host = opts.host || "127.0.0.1";
|
||||
this.port = opts.port || "3000";
|
||||
master = opts.master;
|
||||
this.server = express().http().io();
|
||||
this.server.set('name', 'Cylon API Server');
|
||||
this.server.use(express.bodyParser());
|
||||
this.server.get("/*", function(req, res, next) {
|
||||
res.set('Content-Type', 'application/json');
|
||||
return next();
|
||||
});
|
||||
this.routes(this.server);
|
||||
this.server.listen(this.port, this.host, function() {
|
||||
return Logger.info("" + _this.server.name + " is listening at " + _this.host + ":" + _this.port);
|
||||
});
|
||||
}
|
||||
|
||||
Server.prototype.routes = function(server) {
|
||||
server.get("/robots", this.getRobots);
|
||||
server.get("/robots/:robotid", this.getRobotByName);
|
||||
server.get("/robots/:robotid/devices", this.getDevices);
|
||||
server.get("/robots/:robotid/devices/:deviceid", this.getDeviceByName);
|
||||
server.get("/robots/:robotid/devices/:deviceid/commands", this.getDeviceCommands);
|
||||
server.post("/robots/:robotid/devices/:deviceid/commands/:commandid", this.runDeviceCommand);
|
||||
server.get("/robots/:robotid/connections", this.getConnections);
|
||||
server.get("/robots/:robotid/connections/:connectionid", this.getConnectionByName);
|
||||
server.get("/robots/:robotid/devices/:deviceid/events", function(req, res) {
|
||||
return req.io.route('events');
|
||||
});
|
||||
return server.io.route('events', this.ioSetupDeviceEventClient);
|
||||
};
|
||||
|
||||
Server.prototype.getRobots = function(req, res) {
|
||||
var robot;
|
||||
return res.json((function() {
|
||||
var _i, _len, _ref, _results;
|
||||
_ref = master.robots();
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
robot = _ref[_i];
|
||||
_results.push(robot.data());
|
||||
}
|
||||
return _results;
|
||||
})());
|
||||
};
|
||||
|
||||
Server.prototype.getRobotByName = function(req, res) {
|
||||
return master.findRobot(req.params.robotid, function(err, robot) {
|
||||
return res.json(err ? err : robot.data());
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.getDevices = function(req, res) {
|
||||
return master.findRobot(req.params.robotid, function(err, robot) {
|
||||
return res.json(err ? err : robot.data().devices);
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.getDeviceByName = function(req, res) {
|
||||
var deviceid, robotid;
|
||||
robotid = req.params.robotid;
|
||||
deviceid = req.params.deviceid;
|
||||
return master.findRobotDevice(robotid, deviceid, function(err, device) {
|
||||
return res.json(err ? err : device.data());
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.getDeviceCommands = function(req, res) {
|
||||
var deviceid, robotid;
|
||||
robotid = req.params.robotid;
|
||||
deviceid = req.params.deviceid;
|
||||
return master.findRobotDevice(robotid, deviceid, function(err, device) {
|
||||
return res.json(err ? err : device.data().commands);
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.runDeviceCommand = function(req, res) {
|
||||
var commandid, deviceid, key, params, robotid, value, _ref;
|
||||
robotid = req.params.robotid;
|
||||
deviceid = req.params.deviceid;
|
||||
commandid = req.params.commandid;
|
||||
params = [];
|
||||
if (typeof req.body === 'object') {
|
||||
_ref = req.body;
|
||||
for (key in _ref) {
|
||||
value = _ref[key];
|
||||
params.push(value);
|
||||
}
|
||||
}
|
||||
return master.findRobotDevice(robotid, deviceid, function(err, device) {
|
||||
var result;
|
||||
if (err) {
|
||||
return res.json(err);
|
||||
}
|
||||
result = device[commandid].apply(device, params);
|
||||
return res.json({
|
||||
result: result
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.getConnections = function(req, res) {
|
||||
return master.findRobot(req.params.robotid, function(err, robot) {
|
||||
return res.json(err ? err : robot.data().connections);
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.getConnectionByName = function(req, res) {
|
||||
var connectionid, robotid;
|
||||
robotid = req.params.robotid;
|
||||
connectionid = req.params.connectionid;
|
||||
return master.findRobotConnection(robotid, connectionid, function(err, connection) {
|
||||
return res.json(err ? err : connection.data());
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.ioSetupDeviceEventClient = function(req) {
|
||||
var deviceid, robotid;
|
||||
robotid = req.params.robotid;
|
||||
deviceid = req.params.deviceid;
|
||||
return master.findRobotDevice(robotid, deviceid, function(err, device) {
|
||||
if (err) {
|
||||
res.io.respond(err);
|
||||
}
|
||||
return device.on('update', function(data) {
|
||||
return res.io.respond({
|
||||
data: data
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return Server;
|
||||
|
||||
})();
|
||||
});
|
||||
|
||||
}).call(this);
|
|
@ -32,14 +32,27 @@
|
|||
opts = {};
|
||||
}
|
||||
this.connect = __bind(this.connect, this);
|
||||
if (opts.id == null) {
|
||||
opts.id = Math.floor(Math.random() * 10000);
|
||||
}
|
||||
this.self = this;
|
||||
this.robot = opts.robot;
|
||||
this.name = opts.name;
|
||||
this.connection_id = opts.id;
|
||||
this.adaptor = this.requireAdaptor(opts.adaptor);
|
||||
this.port = new Port(opts.port);
|
||||
proxyFunctionsToObject(this.adaptor.commands(), this.adaptor, klass);
|
||||
}
|
||||
|
||||
Connection.prototype.data = function() {
|
||||
return {
|
||||
name: this.name,
|
||||
port: this.port.toString(),
|
||||
adaptor: this.adaptor.constructor.name || this.adaptor.name,
|
||||
connection_id: this.connection_id
|
||||
};
|
||||
};
|
||||
|
||||
Connection.prototype.connect = function(callback) {
|
||||
var msg;
|
||||
msg = "Connecting to '" + this.name + "'";
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
require('./logger');
|
||||
|
||||
require('./api/api');
|
||||
|
||||
Logger.setup();
|
||||
|
||||
Cylon = (function() {
|
||||
|
@ -39,14 +41,17 @@
|
|||
};
|
||||
|
||||
Master = (function() {
|
||||
var robots;
|
||||
var api, robots;
|
||||
|
||||
robots = [];
|
||||
|
||||
api = null;
|
||||
|
||||
function Master() {
|
||||
this.robot = __bind(this.robot, this);
|
||||
this.self = this;
|
||||
}
|
||||
|
||||
robots = [];
|
||||
|
||||
Master.prototype.robot = function(opts) {
|
||||
var robot;
|
||||
opts.master = this;
|
||||
|
@ -55,8 +60,78 @@
|
|||
return robot;
|
||||
};
|
||||
|
||||
Master.prototype.robots = function() {
|
||||
return robots;
|
||||
};
|
||||
|
||||
Master.prototype.findRobot = function(name, callback) {
|
||||
var bot, error, robot, _i, _len;
|
||||
robot = null;
|
||||
for (_i = 0, _len = robots.length; _i < _len; _i++) {
|
||||
bot = robots[_i];
|
||||
if (bot.name === name) {
|
||||
robot = bot;
|
||||
}
|
||||
}
|
||||
if (robot == null) {
|
||||
error = {
|
||||
error: "No Robot found with the name " + name
|
||||
};
|
||||
}
|
||||
if (callback) {
|
||||
return callback(error, robot);
|
||||
} else {
|
||||
return robot;
|
||||
}
|
||||
};
|
||||
|
||||
Master.prototype.findRobotDevice = function(robotid, deviceid, callback) {
|
||||
return this.findRobot(robotid, function(err, robot) {
|
||||
var device, error;
|
||||
if (err) {
|
||||
callback(err, robot);
|
||||
}
|
||||
if (robot.devices[deviceid]) {
|
||||
device = robot.devices[deviceid];
|
||||
}
|
||||
if (device == null) {
|
||||
error = {
|
||||
error: "No device found with the name " + device + "."
|
||||
};
|
||||
}
|
||||
if (callback) {
|
||||
return callback(error, device);
|
||||
} else {
|
||||
return device;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Master.prototype.findRobotConnection = function(robotid, connid, callback) {
|
||||
return this.findRobot(robotid, function(err, robot) {
|
||||
var connection, error;
|
||||
if (err) {
|
||||
callback(err, robot);
|
||||
}
|
||||
if (robot.connections[connid]) {
|
||||
connection = robot.connections[connid];
|
||||
}
|
||||
if (connection == null) {
|
||||
error = {
|
||||
error: "No connection found with the name " + connection + "."
|
||||
};
|
||||
}
|
||||
if (callback) {
|
||||
return callback(error, connection);
|
||||
} else {
|
||||
return connection;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Master.prototype.start = function() {
|
||||
var robot, _i, _len, _results;
|
||||
this.startAPI();
|
||||
_results = [];
|
||||
for (_i = 0, _len = robots.length; _i < _len; _i++) {
|
||||
robot = robots[_i];
|
||||
|
@ -65,6 +140,12 @@
|
|||
return _results;
|
||||
};
|
||||
|
||||
Master.prototype.startAPI = function() {
|
||||
return api != null ? api : api = new Api.Server({
|
||||
master: this.self
|
||||
});
|
||||
};
|
||||
|
||||
return Master;
|
||||
|
||||
})();
|
||||
|
|
|
@ -49,6 +49,16 @@
|
|||
return this.driver.start(callback);
|
||||
};
|
||||
|
||||
Device.prototype.data = function() {
|
||||
return {
|
||||
name: this.name,
|
||||
driver: this.driver.constructor.name || this.driver.name,
|
||||
pin: this.pin != null ? this.pin.toString : null,
|
||||
connection: this.connection.data(),
|
||||
commands: this.driver.commands()
|
||||
};
|
||||
};
|
||||
|
||||
Device.prototype.determineConnection = function(c) {
|
||||
if (c) {
|
||||
return this.robot.connections[c];
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
var Async, Connection, Device, Robot,
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
require('./cylon');
|
||||
|
||||
|
@ -28,7 +29,7 @@
|
|||
klass = Robot;
|
||||
|
||||
function Robot(opts) {
|
||||
var func, n;
|
||||
var func, n, reserved;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
@ -54,7 +55,8 @@
|
|||
};
|
||||
for (n in opts) {
|
||||
func = opts[n];
|
||||
if (n !== 'connection' && n !== 'connections' && n !== 'device' && n !== 'devices' && n !== 'work') {
|
||||
reserved = ['connection', 'connections', 'device', 'devices', 'work'];
|
||||
if (__indexOf.call(reserved, n) < 0) {
|
||||
this.robot[n] = func;
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +66,33 @@
|
|||
return "Robot " + (Math.floor(Math.random() * 100000));
|
||||
};
|
||||
|
||||
Robot.prototype.data = function() {
|
||||
var connection, device, n;
|
||||
return {
|
||||
name: this.name,
|
||||
connections: (function() {
|
||||
var _ref, _results;
|
||||
_ref = this.connections;
|
||||
_results = [];
|
||||
for (n in _ref) {
|
||||
connection = _ref[n];
|
||||
_results.push(connection.data());
|
||||
}
|
||||
return _results;
|
||||
}).call(this),
|
||||
devices: (function() {
|
||||
var _ref, _results;
|
||||
_ref = this.devices;
|
||||
_results = [];
|
||||
for (n in _ref) {
|
||||
device = _ref[n];
|
||||
_results.push(device.data());
|
||||
}
|
||||
return _results;
|
||||
}).call(this)
|
||||
};
|
||||
};
|
||||
|
||||
Robot.prototype.initConnections = function(connections) {
|
||||
var connection, _i, _len, _results;
|
||||
Logger.info("Initializing connections...");
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"node-namespace": "~1.0.0"
|
||||
"node-namespace": "~1.0.0",
|
||||
"express.io": "~1.1.13"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
###
|
||||
* api
|
||||
* cylonjs.com
|
||||
*
|
||||
* Copyright (c) 2013 The Hybrid Group
|
||||
* Licensed under the Apache 2.0 license.
|
||||
###
|
||||
|
||||
express = require('express.io')
|
||||
namespace = require 'node-namespace'
|
||||
|
||||
namespace "Api", ->
|
||||
# The Cylon API Server provides an interface to communicate with master class
|
||||
# and retrieve information about the robots being controlled.
|
||||
class @Server
|
||||
master = null
|
||||
|
||||
constructor: (opts = {}) ->
|
||||
@host = opts.host || "127.0.0.1"
|
||||
@port = opts.port || "3000"
|
||||
|
||||
master = opts.master
|
||||
|
||||
@server = express().http().io()
|
||||
@server.set 'name', 'Cylon API Server'
|
||||
@server.use(express.bodyParser())
|
||||
|
||||
@server.get "/*", (req, res, next) ->
|
||||
res.set 'Content-Type', 'application/json'
|
||||
do next
|
||||
|
||||
@routes @server
|
||||
|
||||
@server.listen @port, @host, =>
|
||||
Logger.info "#{@server.name} is listening at #{@host}:#{@port}"
|
||||
|
||||
routes: (server) ->
|
||||
server.get "/robots", @getRobots
|
||||
server.get "/robots/:robotid", @getRobotByName
|
||||
server.get "/robots/:robotid/devices", @getDevices
|
||||
server.get "/robots/:robotid/devices/:deviceid", @getDeviceByName
|
||||
server.get "/robots/:robotid/devices/:deviceid/commands", @getDeviceCommands
|
||||
server.post "/robots/:robotid/devices/:deviceid/commands/:commandid", @runDeviceCommand
|
||||
server.get "/robots/:robotid/connections", @getConnections
|
||||
server.get "/robots/:robotid/connections/:connectionid", @getConnectionByName
|
||||
|
||||
server.get "/robots/:robotid/devices/:deviceid/events", (req, res) ->
|
||||
req.io.route 'events'
|
||||
server.io.route 'events', @ioSetupDeviceEventClient
|
||||
|
||||
getRobots: (req, res) ->
|
||||
res.json (robot.data() for robot in master.robots())
|
||||
|
||||
getRobotByName: (req, res) ->
|
||||
master.findRobot req.params.robotid, (err, robot) ->
|
||||
res.json if err then err else robot.data()
|
||||
|
||||
getDevices: (req, res) ->
|
||||
master.findRobot req.params.robotid, (err, robot) ->
|
||||
res.json if err then err else robot.data().devices
|
||||
|
||||
getDeviceByName: (req, res) ->
|
||||
robotid = req.params.robotid
|
||||
deviceid = req.params.deviceid
|
||||
|
||||
master.findRobotDevice robotid, deviceid, (err, device) ->
|
||||
res.json if err then err else device.data()
|
||||
|
||||
getDeviceCommands: (req, res) ->
|
||||
robotid = req.params.robotid
|
||||
deviceid = req.params.deviceid
|
||||
|
||||
master.findRobotDevice robotid, deviceid, (err, device) ->
|
||||
res.json if err then err else device.data().commands
|
||||
|
||||
runDeviceCommand: (req, res) ->
|
||||
robotid = req.params.robotid
|
||||
deviceid = req.params.deviceid
|
||||
commandid = req.params.commandid
|
||||
|
||||
params = []
|
||||
if typeof req.body is 'object'
|
||||
params.push(value) for key, value of req.body
|
||||
|
||||
master.findRobotDevice robotid, deviceid, (err, device) ->
|
||||
if err then return res.json err
|
||||
result = device[commandid](params...)
|
||||
res.json result: result
|
||||
|
||||
getConnections: (req, res) ->
|
||||
master.findRobot req.params.robotid, (err, robot) ->
|
||||
res.json if err then err else robot.data().connections
|
||||
|
||||
getConnectionByName: (req, res) ->
|
||||
robotid = req.params.robotid
|
||||
connectionid = req.params.connectionid
|
||||
|
||||
master.findRobotConnection robotid, connectionid, (err, connection) ->
|
||||
res.json if err then err else connection.data()
|
||||
|
||||
ioSetupDeviceEventClient: (req) ->
|
||||
robotid = req.params.robotid
|
||||
deviceid = req.params.deviceid
|
||||
|
||||
master.findRobotDevice robotid, deviceid, (err, device) ->
|
||||
res.io.respond(err) if err
|
||||
device.on 'update', (data) -> res.io.respond { data: data }
|
|
@ -16,13 +16,23 @@ module.exports = class Connection extends EventEmitter
|
|||
klass = this
|
||||
|
||||
constructor: (opts = {}) ->
|
||||
opts.id ?= Math.floor(Math.random() * 10000)
|
||||
@self = this
|
||||
@robot = opts.robot
|
||||
@name = opts.name
|
||||
@connection_id = opts.id
|
||||
@adaptor = @requireAdaptor(opts.adaptor) # or 'loopback')
|
||||
@port = new Port(opts.port)
|
||||
proxyFunctionsToObject @adaptor.commands(), @adaptor, klass
|
||||
|
||||
data: ->
|
||||
{
|
||||
name: @name,
|
||||
port: @port.toString()
|
||||
adaptor: @adaptor.constructor.name || @adaptor.name
|
||||
connection_id: @connection_id
|
||||
}
|
||||
|
||||
connect: (callback) =>
|
||||
msg = "Connecting to '#{@name}'"
|
||||
msg += " on port '#{@port.toString()}'" if @port?
|
||||
|
|
|
@ -13,6 +13,8 @@ Robot = require("./robot")
|
|||
require('./utils')
|
||||
require('./logger')
|
||||
|
||||
require('./api/api')
|
||||
|
||||
Logger.setup()
|
||||
|
||||
class Cylon
|
||||
|
@ -23,6 +25,10 @@ class Cylon
|
|||
|
||||
class Master
|
||||
robots = []
|
||||
api = null
|
||||
|
||||
constructor: ->
|
||||
@self = this
|
||||
|
||||
robot: (opts) =>
|
||||
opts.master = this
|
||||
|
@ -30,7 +36,42 @@ class Cylon
|
|||
robots.push robot
|
||||
robot
|
||||
|
||||
robots: -> robots
|
||||
|
||||
findRobot: (name, callback) ->
|
||||
robot = null
|
||||
for bot in robots
|
||||
robot = bot if bot.name is name
|
||||
|
||||
error = { error: "No Robot found with the name #{name}" } unless robot?
|
||||
|
||||
if callback then callback(error, robot) else robot
|
||||
|
||||
findRobotDevice: (robotid, deviceid, callback) ->
|
||||
@findRobot robotid, (err, robot) ->
|
||||
callback(err, robot) if err
|
||||
|
||||
device = robot.devices[deviceid] if robot.devices[deviceid]
|
||||
unless device?
|
||||
error = { error: "No device found with the name #{device}." }
|
||||
|
||||
if callback then callback(error, device) else device
|
||||
|
||||
findRobotConnection: (robotid, connid, callback) ->
|
||||
@findRobot robotid, (err, robot) ->
|
||||
callback(err, robot) if err
|
||||
|
||||
connection = robot.connections[connid] if robot.connections[connid]
|
||||
unless connection?
|
||||
error = { error: "No connection found with the name #{connection}." }
|
||||
|
||||
if callback then callback(error, connection) else connection
|
||||
|
||||
start: ->
|
||||
do @startAPI
|
||||
robot.start() for robot in robots
|
||||
|
||||
startAPI: ->
|
||||
api ?= new Api.Server(master: @self)
|
||||
|
||||
module.exports = Cylon.getInstance()
|
||||
|
|
|
@ -29,6 +29,15 @@ module.exports = class Device extends EventEmitter
|
|||
Logger.info msg
|
||||
@driver.start(callback)
|
||||
|
||||
data: ->
|
||||
{
|
||||
name: @name
|
||||
driver: @driver.constructor.name || @driver.name
|
||||
pin: if @pin? then @pin.toString else null
|
||||
connection: @connection.data()
|
||||
commands: @driver.commands()
|
||||
}
|
||||
|
||||
determineConnection: (c) ->
|
||||
@robot.connections[c] if c
|
||||
|
||||
|
|
|
@ -32,13 +32,21 @@ module.exports = class Robot
|
|||
@initConnections(opts.connection or opts.connections)
|
||||
@initDevices(opts.device or opts.devices)
|
||||
@work = opts.work or -> (Logger.info "No work yet")
|
||||
|
||||
|
||||
for n, func of opts
|
||||
@robot[n] = func unless n in ['connection', 'connections', 'device', 'devices', 'work']
|
||||
reserved = ['connection', 'connections', 'device', 'devices', 'work']
|
||||
@robot[n] = func unless n in reserved
|
||||
|
||||
@randomName: ->
|
||||
"Robot #{ Math.floor(Math.random() * 100000) }"
|
||||
|
||||
data: ->
|
||||
{
|
||||
name: @name
|
||||
connections: (connection.data() for n, connection of @connections)
|
||||
devices: (device.data() for n, device of @devices)
|
||||
}
|
||||
|
||||
initConnections: (connections) =>
|
||||
Logger.info "Initializing connections..."
|
||||
return unless connections?
|
||||
|
@ -76,7 +84,7 @@ module.exports = class Robot
|
|||
for n, device of @devices
|
||||
@robot[n] = device
|
||||
starters[n] = device.start
|
||||
|
||||
|
||||
Async.parallel starters, callback
|
||||
|
||||
requireAdaptor: (adaptorName, connection) ->
|
||||
|
|
Loading…
Reference in New Issue