2013-10-25 05:25:42 +08:00
|
|
|
/*
|
|
|
|
* cylon
|
|
|
|
* cylonjs.com
|
|
|
|
*
|
2015-01-08 04:58:50 +08:00
|
|
|
* Copyright (c) 2013-2015 The Hybrid Group
|
2013-10-25 05:25:42 +08:00
|
|
|
* Licensed under the Apache 2.0 license.
|
|
|
|
*/
|
|
|
|
|
2014-02-28 05:31:54 +08:00
|
|
|
"use strict";
|
2013-10-25 05:25:42 +08:00
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
var Async = require("async");
|
2014-06-13 06:31:49 +08:00
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
var Logger = require("./logger"),
|
|
|
|
Robot = require("./robot"),
|
|
|
|
Config = require("./config"),
|
2015-02-21 10:49:31 +08:00
|
|
|
Utils = require("./utils"),
|
|
|
|
_ = require("./utils/helpers");
|
2014-05-14 10:20:54 +08:00
|
|
|
|
2015-01-06 04:20:53 +08:00
|
|
|
var EventEmitter = require("events").EventEmitter;
|
2014-05-14 10:20:54 +08:00
|
|
|
|
2015-01-06 04:20:53 +08:00
|
|
|
var Cylon = module.exports = new EventEmitter();
|
2014-05-14 10:20:54 +08:00
|
|
|
|
2015-01-06 04:20:53 +08:00
|
|
|
Cylon.Logger = Logger;
|
|
|
|
Cylon.Driver = require("./driver");
|
|
|
|
Cylon.Adaptor = require("./adaptor");
|
|
|
|
Cylon.Utils = Utils;
|
2014-05-14 10:20:54 +08:00
|
|
|
|
2015-01-06 04:20:53 +08:00
|
|
|
Cylon.IO = {
|
|
|
|
DigitalPin: require("./io/digital-pin"),
|
|
|
|
Utils: require("./io/utils")
|
2014-05-14 10:20:54 +08:00
|
|
|
};
|
|
|
|
|
2015-01-08 07:59:35 +08:00
|
|
|
Cylon.apiInstances = [];
|
2015-01-06 04:20:53 +08:00
|
|
|
|
|
|
|
Cylon.robots = {};
|
|
|
|
Cylon.commands = {};
|
|
|
|
|
|
|
|
Cylon.events = [ "robot_added", "robot_removed" ];
|
|
|
|
|
2014-05-14 10:20:54 +08:00
|
|
|
// Public: Creates a new Robot
|
|
|
|
//
|
|
|
|
// opts - hash of Robot attributes
|
|
|
|
//
|
|
|
|
// Returns a shiny new Robot
|
|
|
|
// Examples:
|
|
|
|
// Cylon.robot
|
2014-12-16 03:15:29 +08:00
|
|
|
// connection: { name: "arduino", adaptor: "firmata" }
|
|
|
|
// device: { name: "led", driver: "led", pin: 13 }
|
2014-05-14 10:20:54 +08:00
|
|
|
//
|
|
|
|
// work: (me) ->
|
|
|
|
// me.led.toggle()
|
|
|
|
Cylon.robot = function robot(opts) {
|
2014-11-21 00:43:08 +08:00
|
|
|
opts = opts || {};
|
|
|
|
|
2014-09-05 05:51:44 +08:00
|
|
|
// check if a robot with the same name exists already
|
|
|
|
if (opts.name && this.robots[opts.name]) {
|
|
|
|
var original = opts.name;
|
|
|
|
opts.name = Utils.makeUnique(original, Object.keys(this.robots));
|
2014-12-16 03:15:29 +08:00
|
|
|
|
|
|
|
var str = "Robot names must be unique. Renaming '";
|
|
|
|
str += original + "' to '" + opts.name + "'";
|
|
|
|
|
|
|
|
Logger.warn(str);
|
2014-09-05 05:51:44 +08:00
|
|
|
}
|
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
var bot = new Robot(opts);
|
|
|
|
this.robots[bot.name] = bot;
|
2015-01-06 04:20:53 +08:00
|
|
|
this.emit("robot_added", bot.name);
|
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
return bot;
|
2014-05-14 10:20:54 +08:00
|
|
|
};
|
|
|
|
|
2015-01-08 07:59:35 +08:00
|
|
|
// Public: Initializes an API instance based on provided options.
|
2014-05-14 10:20:54 +08:00
|
|
|
//
|
2014-06-05 02:37:15 +08:00
|
|
|
// Returns nothing
|
2015-01-08 07:59:35 +08:00
|
|
|
Cylon.api = function api(Server, opts) {
|
|
|
|
// if only passed options (or nothing), assume HTTP server
|
2015-02-21 10:49:31 +08:00
|
|
|
if (Server == null || _.isObject(Server) && !_.isFunction(Server)) {
|
2015-01-08 07:59:35 +08:00
|
|
|
opts = Server;
|
|
|
|
Server = "http";
|
2014-11-15 04:57:09 +08:00
|
|
|
}
|
|
|
|
|
2015-01-08 07:59:35 +08:00
|
|
|
opts = opts || {};
|
|
|
|
|
2015-02-21 10:49:31 +08:00
|
|
|
if (_.isString(Server)) {
|
2015-01-08 07:59:35 +08:00
|
|
|
var req = "cylon-api-" + Server;
|
|
|
|
|
|
|
|
try {
|
|
|
|
Server = require(req);
|
|
|
|
} catch (e) {
|
|
|
|
if (e.code === "MODULE_NOT_FOUND") {
|
2015-01-14 02:13:16 +08:00
|
|
|
var messages;
|
|
|
|
|
|
|
|
if (req === "cylon-api-http") {
|
|
|
|
messages = [
|
|
|
|
"The HTTP API is no longer included in Cylon by default.",
|
|
|
|
"To use it, install the plugin module: `npm install cylon-api-http`"
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
messages = [
|
|
|
|
"Cannot find the " + req + " API module.",
|
|
|
|
"You may be able to install it: `npm install " + req + "`"
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2015-04-03 05:10:22 +08:00
|
|
|
_.each(messages, _.arity(Logger.fatal, 1));
|
|
|
|
throw new Error("Missing API plugin - cannot proceed");
|
2015-01-08 07:59:35 +08:00
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-28 05:31:54 +08:00
|
|
|
|
2015-01-08 07:59:35 +08:00
|
|
|
opts.mcp = this;
|
|
|
|
var instance = new Server(opts);
|
|
|
|
this.apiInstances.push(instance);
|
2015-01-31 04:45:50 +08:00
|
|
|
instance.start();
|
2014-05-14 10:20:54 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Public: Starts up the API and the robots
|
|
|
|
//
|
|
|
|
// Returns nothing
|
|
|
|
Cylon.start = function start() {
|
2015-02-21 10:49:31 +08:00
|
|
|
var starters = _.pluck(this.robots, "start");
|
2014-12-18 06:42:34 +08:00
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
Async.parallel(starters, function() {
|
|
|
|
var mode = Utils.fetch(Config, "workMode", "async");
|
|
|
|
|
|
|
|
if (mode === "sync") {
|
2015-02-21 10:49:31 +08:00
|
|
|
_.invoke(this.robots, "startWork");
|
2014-12-16 03:15:29 +08:00
|
|
|
}
|
2014-10-04 15:56:08 +08:00
|
|
|
}.bind(this));
|
2014-05-14 10:20:54 +08:00
|
|
|
};
|
|
|
|
|
2014-09-04 06:04:36 +08:00
|
|
|
// Public: Sets the internal configuration, based on passed options
|
|
|
|
//
|
|
|
|
// opts - object containing configuration key/value pairs
|
|
|
|
//
|
2014-09-16 05:46:24 +08:00
|
|
|
// Returns the current config
|
|
|
|
Cylon.config = function(opts) {
|
2015-02-20 09:23:41 +08:00
|
|
|
var loggingChanged = (
|
2015-02-21 10:49:31 +08:00
|
|
|
opts.logging && Config.logging !== _.extend(Config.logging, opts.logging)
|
2015-02-20 09:23:41 +08:00
|
|
|
);
|
2014-10-29 06:53:03 +08:00
|
|
|
|
2015-02-21 10:49:31 +08:00
|
|
|
if (_.isObject(opts)) {
|
|
|
|
Config = _.extend(Config, opts);
|
2014-09-04 06:04:36 +08:00
|
|
|
}
|
|
|
|
|
2015-02-20 09:23:41 +08:00
|
|
|
if (loggingChanged) {
|
2014-10-29 06:53:03 +08:00
|
|
|
Logger.setup();
|
|
|
|
}
|
|
|
|
|
2014-09-04 06:04:36 +08:00
|
|
|
return Config;
|
|
|
|
};
|
|
|
|
|
2014-05-14 10:20:54 +08:00
|
|
|
// Public: Halts the API and the robots
|
|
|
|
//
|
2014-06-13 06:31:49 +08:00
|
|
|
// callback - callback to be triggered when Cylon is ready to shutdown
|
|
|
|
//
|
2014-05-14 10:20:54 +08:00
|
|
|
// Returns nothing
|
2014-06-13 06:31:49 +08:00
|
|
|
Cylon.halt = function halt(callback) {
|
2014-12-16 03:15:29 +08:00
|
|
|
callback = callback || function() {};
|
2014-12-18 06:42:34 +08:00
|
|
|
|
2015-02-21 10:49:31 +08:00
|
|
|
var fns = _.pluck(this.robots, "halt");
|
2014-12-18 06:42:34 +08:00
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
// if robots can"t shut down quickly enough, forcefully self-terminate
|
|
|
|
var timeout = Config.haltTimeout || 3000;
|
2014-09-04 06:39:54 +08:00
|
|
|
Utils.after(timeout, callback);
|
2014-05-14 10:20:54 +08:00
|
|
|
|
2014-06-13 06:31:49 +08:00
|
|
|
Async.parallel(fns, callback);
|
2014-05-14 10:20:54 +08:00
|
|
|
};
|
|
|
|
|
2014-07-16 03:27:16 +08:00
|
|
|
Cylon.toJSON = function() {
|
|
|
|
return {
|
2015-02-21 10:49:31 +08:00
|
|
|
robots: _.invoke(this.robots, "toJSON"),
|
2015-02-20 09:23:41 +08:00
|
|
|
commands: Object.keys(this.commands),
|
2015-01-06 04:20:53 +08:00
|
|
|
events: this.events
|
2014-12-16 03:15:29 +08:00
|
|
|
};
|
2014-07-16 03:27:16 +08:00
|
|
|
};
|
|
|
|
|
2014-05-14 10:20:54 +08:00
|
|
|
if (process.platform === "win32") {
|
|
|
|
var readline = require("readline"),
|
|
|
|
io = { input: process.stdin, output: process.stdout };
|
|
|
|
|
|
|
|
readline.createInterface(io).on("SIGINT", function() {
|
|
|
|
process.emit("SIGINT");
|
|
|
|
});
|
2014-06-17 04:09:13 +08:00
|
|
|
}
|
2014-05-14 10:20:54 +08:00
|
|
|
|
|
|
|
process.on("SIGINT", function() {
|
2014-06-13 06:31:49 +08:00
|
|
|
Cylon.halt(function() {
|
|
|
|
process.kill(process.pid);
|
|
|
|
});
|
2014-05-14 10:20:54 +08:00
|
|
|
});
|