Merge branch 'master' into servo-level-up
This commit is contained in:
commit
c4b10e66f4
|
@ -1,10 +1,7 @@
|
|||
language: node_js
|
||||
script:
|
||||
- npm install -g codeclimate-test-reporter
|
||||
- npm install -g istanbul codeclimate-test-reporter
|
||||
- make cover
|
||||
- CODECLIMATE_REPO_TOKEN=d3aad610220b6eaf4f51e38393c1b62586b1d68b898b42e418d9c2a8e0a7cb0d codeclimate < coverage/lcov.info
|
||||
node_js:
|
||||
- '0.10'
|
||||
branches:
|
||||
except:
|
||||
- cylonjs.com
|
||||
|
|
|
@ -10,6 +10,9 @@ Cylon.js exists thanks to the efforts of these hardworking humans:
|
|||
- Adrian Zankich ([@zankich](https://github.com/zankich))
|
||||
- Javier Cervantes ([@solojavier](https://github.com/solojavier))
|
||||
- Chris Mattheiu ([@chrismatthieu](https://github.com/chrismatthieu))
|
||||
- Fábio Franco Uechi ([@fabito](https://github.com/fabito))
|
||||
- Andrew Nesbitt ([@andrew](https://github.com/andrew))
|
||||
- Chris Boette ([@chrisbodhi](https://github.com/chrisbodhi))
|
||||
|
||||
Thank you!
|
||||
|
||||
|
|
8
Makefile
8
Makefile
|
@ -8,14 +8,14 @@ VERSION := $(shell node -e "console.log(require('./package.json').version)")
|
|||
test:
|
||||
@$(BIN)/mocha --colors $(TEST_FILES)
|
||||
|
||||
cover:
|
||||
@$(BIN)/istanbul cover $(BIN)/_mocha $(TEST_FILES) --report lcovonly -- -R spec
|
||||
|
||||
bdd:
|
||||
@$(BIN)/mocha --colors -R spec $(TEST_FILES)
|
||||
|
||||
cover:
|
||||
@istanbul cover $(BIN)/_mocha $(TEST_FILES) --report lcovonly -- -R spec
|
||||
|
||||
lint:
|
||||
@$(BIN)/jshint ./lib
|
||||
@jshint ./lib
|
||||
|
||||
release:
|
||||
@git tag -m "$(VERSION)" v$(VERSION)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[![Cylon.js](https://raw.github.com/hybridgroup/cylon/gh-pages/images/elements/logo.png)](http://cylonjs.com)
|
||||
[![Cylon.js](https://cdn.rawgit.com/hybridgroup/cylon-site/master/source/images/elements/cylon.png)](http://cylonjs.com)
|
||||
|
||||
Cylon.js is a JavaScript framework for robotics and physical computing built on
|
||||
top of Node.js.
|
||||
|
@ -16,7 +16,7 @@ Want to use Golang to power your robots? Check out our sister project,
|
|||
|
||||
## Build Status:
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/hybridgroup/cylon.png?branch=master)](http://travis-ci.org/hybridgroup/cylon) [![Code Climate](https://codeclimate.com/github/hybridgroup/cylon/coverage.png)](https://codeclimate.com/github/hybridgroup/cylon)
|
||||
[![Build Status](https://secure.travis-ci.org/hybridgroup/cylon.png?branch=master)](http://travis-ci.org/hybridgroup/cylon) [![Code Climate](https://codeclimate.com/github/hybridgroup/cylon.png)](https://codeclimate.com/github/hybridgroup/cylon) [![Code Climate](https://codeclimate.com/github/hybridgroup/cylon/coverage.png)](https://codeclimate.com/github/hybridgroup/cylon)
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -222,11 +222,11 @@ Cylon itself. Check it out at https://github.com/hybridgroup/cylon-cli.
|
|||
We're busy adding documentation to our website, check it out at
|
||||
[cylonjs.com/documentation][docs].
|
||||
|
||||
If you want to help with documentation, you can find some helpful instructions
|
||||
on the [cylonjs.com branch][cylonjs-branch].
|
||||
If you want to help with documentation, you can find the code for our website at
|
||||
on the [https://github.com/hybridgroup/cylon-site](https://github.com/hybridgroup/cylon-site).
|
||||
|
||||
[docs]: http://cylonjs.com/documentation
|
||||
[cylonjs-branch]: https://github.com/hybridgroup/cylon/tree/cylonjs.com
|
||||
[docs site]: https://github.com/hybridgroup/cylon-site
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -244,6 +244,11 @@ on the [cylonjs.com branch][cylonjs-branch].
|
|||
|
||||
## Release History
|
||||
|
||||
Version 0.15.1 - Fixed issue with the API on Tessel
|
||||
|
||||
Version 0.15.0 - Better halting, cleaner startup, removed 'connect' and 'start'
|
||||
events, and misc other cleanups/refactors.
|
||||
|
||||
Version 0.14.0 - Removal of node-namespace and misc. cleanup
|
||||
|
||||
Version 0.13.3 - Fixes bug with disconnect functions not being called.
|
||||
|
|
|
@ -28,16 +28,16 @@ skynet = {
|
|||
console.log(data);
|
||||
if (data.payload != null){
|
||||
var robot,
|
||||
bot,
|
||||
robots = data.payload.robots;
|
||||
for(var index in robots){
|
||||
robot = robots[index];
|
||||
console.log(robot);
|
||||
my.master.findRobot(robot.name, function(err, bot){
|
||||
if (robot.cmd == 'on')
|
||||
bot.devices[robot.device].turnOn();
|
||||
else
|
||||
bot.devices[robot.device].turnOff();
|
||||
});
|
||||
bot = Cylon.robots[robot.name];
|
||||
if (robot.cmd == 'on')
|
||||
bot.devices[robot.device].turnOn();
|
||||
else
|
||||
bot.devices[robot.device].turnOff();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -19,8 +19,8 @@ It will have a single connection and device, both to the keyboard.
|
|||
When we tell this robot to work, it's going to listen to the 'a' key on the
|
||||
keyboard and let us know when it's been pressed.
|
||||
|
||||
work: (my) function() {
|
||||
my.keyboard.on('a', function(key) { console.log "A PRESSED!" });
|
||||
work: function(my) {
|
||||
my.keyboard.on('a', function(key) { console.log("A PRESSED!") });
|
||||
}
|
||||
|
||||
With that done, let's get started!
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* leap_ardrone.js
|
||||
*
|
||||
* Written by Giuliano Sposito and Fábio Uechi
|
||||
* Copyright (c) 2013-2014 CI&T Software
|
||||
* Licensed under the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
Cylon = require('cylon');
|
||||
|
||||
// constants
|
||||
var lastS = 0,
|
||||
handStartPosition = [],
|
||||
handStartDirection = [],
|
||||
UP_CONTROL_THRESHOLD = 50,
|
||||
UP_SPEED_FACTOR = 0.01,
|
||||
CIRCLE_THRESHOLD = 1.5,
|
||||
DIRECTION_THRESHOLD = 0.25,
|
||||
DIRECTION_SPEED_FACTOR = 0.05,
|
||||
TURN_SPEED = 0.5,
|
||||
TURN_TRESHOLD = 0.2,
|
||||
TURN_SPEED_FACTOR = 2.0;
|
||||
|
||||
Cylon.robot({
|
||||
|
||||
connections: [
|
||||
{ name: 'leapmotion', adaptor: 'leapmotion', port: '127.0.0.1:6437' },
|
||||
{ name: 'ardrone', adaptor: 'ardrone', port: '192.168.1.1' },
|
||||
{ name: 'keyboard', adaptor: 'keyboard' }
|
||||
],
|
||||
|
||||
devices: [
|
||||
{ name: 'drone', driver: 'ardrone', connection:'ardrone' },
|
||||
{ name: 'leapmotion', driver: 'leapmotion', connection:'leapmotion' },
|
||||
{ name: 'keyboard', driver: 'keyboard', connection:'keyboard'}
|
||||
],
|
||||
|
||||
work: function(my) {
|
||||
|
||||
// HAND
|
||||
my.leapmotion.on('hand', function(hand) {
|
||||
|
||||
// detects open hand ==> reset
|
||||
if (hand.s>1.5 && lastS<=1.5) {
|
||||
handStartPosition = hand.palmPosition;
|
||||
handStartDirection = hand.direction;
|
||||
};
|
||||
|
||||
// TURNS
|
||||
if(hand.s>1.5 && Math.abs(handStartDirection[0]-hand.direction[0]) > TURN_TRESHOLD ) {
|
||||
var signal = handStartDirection[0]-hand.direction[0];
|
||||
var value = (Math.abs(handStartDirection[0]-hand.direction[0])-TURN_TRESHOLD)*TURN_SPEED_FACTOR;
|
||||
if (signal>0){
|
||||
my.drone.counterClockwise(value);
|
||||
}
|
||||
|
||||
if (signal<0){
|
||||
my.drone.clockwise(value);
|
||||
}
|
||||
}
|
||||
|
||||
// UP and DOWN
|
||||
if (hand.s>1.5 && Math.abs(hand.palmPosition[1]-handStartPosition[1]) > UP_CONTROL_THRESHOLD) {
|
||||
var signal = (hand.palmPosition[1]-handStartPosition[1]) >= 0 ? 1 : -1;
|
||||
var value = Math.round(Math.abs((hand.palmPosition[1]-handStartPosition[1]))-UP_CONTROL_THRESHOLD) * UP_SPEED_FACTOR;
|
||||
|
||||
if (signal>0) {
|
||||
my.drone.up(value);
|
||||
};
|
||||
|
||||
if (signal<0) {
|
||||
my.drone.down(value);
|
||||
}
|
||||
}
|
||||
|
||||
// DIRECTION FRONT/BACK
|
||||
if (hand.s>1.5 && (Math.abs(hand.palmNormal[2])>DIRECTION_THRESHOLD)) {
|
||||
if (hand.palmNormal[2]>0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[2]*10 + DIRECTION_THRESHOLD )*DIRECTION_SPEED_FACTOR);
|
||||
my.drone.forward( value );
|
||||
};
|
||||
|
||||
if (hand.palmNormal[2]<0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[2]*10 - DIRECTION_THRESHOLD )*DIRECTION_SPEED_FACTOR);
|
||||
my.drone.back( value );
|
||||
};
|
||||
}
|
||||
|
||||
// DIRECTION LEFT/RIGHT
|
||||
if (hand.s>1.5 && (Math.abs(hand.palmNormal[0])>DIRECTION_THRESHOLD)) {
|
||||
if (hand.palmNormal[0]>0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[0]*10 + DIRECTION_THRESHOLD )*DIRECTION_SPEED_FACTOR);
|
||||
my.drone.left( value );
|
||||
};
|
||||
|
||||
if (hand.palmNormal[0]<0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[0]*10 - DIRECTION_THRESHOLD )*DIRECTION_SPEED_FACTOR);
|
||||
my.drone.right( value );
|
||||
};
|
||||
}
|
||||
|
||||
// AUTO FREEZE
|
||||
if ( hand.s>1.5 && // open hand
|
||||
(Math.abs(hand.palmNormal[0])<DIRECTION_THRESHOLD) && // within left/right threshold
|
||||
(Math.abs(hand.palmNormal[2])<DIRECTION_THRESHOLD) && // within forward/back threshold
|
||||
Math.abs(hand.palmPosition[1]-handStartPosition[1]) < UP_CONTROL_THRESHOLD && // within up/down threshold
|
||||
Math.abs(handStartDirection[0]-hand.direction[0]) < TURN_TRESHOLD) // within turn threshold
|
||||
{
|
||||
my.drone.stop();
|
||||
}
|
||||
|
||||
// COMMAND FREEZE
|
||||
if (hand.s<=1.5 && lastS > 1.5) { // closed hand
|
||||
my.drone.stop();
|
||||
}
|
||||
|
||||
lastS = hand.s;
|
||||
|
||||
});// end hand
|
||||
|
||||
// Gestures
|
||||
my.leapmotion.on('gesture', function(gesture) {
|
||||
if (gesture.type=='circle' && gesture.state=='stop' && gesture.progress > CIRCLE_THRESHOLD ){
|
||||
if (gesture.normal[2] < 0) {
|
||||
my.drone.takeoff();
|
||||
};
|
||||
|
||||
if (gesture.normal[2] > 0) {
|
||||
my.drone.land();
|
||||
}
|
||||
}
|
||||
|
||||
// EMERGENCE STOP
|
||||
if (gesture.type=='keyTap' || gesture.type=='screenTap') {
|
||||
my.drone.stop();
|
||||
};
|
||||
}); // end gesture
|
||||
|
||||
//KEYBOARD
|
||||
my.keyboard.on('right', function(key) {
|
||||
my.drone.rightFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('left', function(key) {
|
||||
my.drone.leftFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('up', function(key) {
|
||||
my.drone.frontFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('down', function(key) {
|
||||
my.drone.backFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('w', function(key) {
|
||||
my.drone.wave();
|
||||
});
|
||||
|
||||
my.keyboard.on('s', function(key) {
|
||||
my.drone.stop();
|
||||
});
|
||||
|
||||
my.keyboard.on('l', function(key) {
|
||||
my.drone.land();
|
||||
});
|
||||
|
||||
} // end work
|
||||
}).start();
|
|
@ -0,0 +1,142 @@
|
|||
# Leapmotion Ardrone 2.0
|
||||
|
||||
This example illustrates how to use Leap Motion and the keyboard to control an ARDrone. We basically use one hand to drive the drone (takeoff, land, up, down, etc) and the arrow keys to perform some cool stunts (flips).
|
||||
|
||||
First, let's import Cylon:
|
||||
|
||||
var Cylon = require('../..');
|
||||
|
||||
Now that we have Cylon imported, we can start defining our robot
|
||||
|
||||
Cylon.robot({
|
||||
|
||||
Let's define the connections and devices:
|
||||
|
||||
connections: [
|
||||
{ name: 'leapmotion', adaptor: 'leapmotion', port: '127.0.0.1:6437' },
|
||||
{ name: 'ardrone', adaptor: 'ardrone', port: '192.168.1.1' },
|
||||
{ name: 'keyboard', adaptor: 'keyboard' }
|
||||
],
|
||||
devices: [
|
||||
{ name: 'drone', driver: 'ardrone', connection:'ardrone' },
|
||||
{ name: 'leapmotion', driver: 'leapmotion', connection:'leapmotion' },
|
||||
{ name: 'keyboard', driver: 'keyboard', connection:'keyboard'}
|
||||
],
|
||||
|
||||
Now that Cylon knows about the necessary hardware we're going to be using, we'll
|
||||
tell it what work we want to do:
|
||||
|
||||
work: function(my) {
|
||||
|
||||
Lets use the circle gesture to take off and land : two rounds clockwise will trigger the takeoff() and counter clockwise will tell the drone to land:
|
||||
|
||||
|
||||
my.leapmotion.on('gesture', function(gesture) {
|
||||
if (gesture.type=='circle' && gesture.state=='stop' && gesture.progress > CIRCLE_THRESHOLD ){
|
||||
if (gesture.normal[2] < 0) {
|
||||
my.drone.takeoff();
|
||||
};
|
||||
|
||||
if (gesture.normal[2] > 0) {
|
||||
my.drone.land();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Whenever we get a 'hand' gesture event from Leap Motion we need to tell ARDrone what to do.
|
||||
|
||||
|
||||
my.leapmotion.on('hand', function(hand) {
|
||||
|
||||
|
||||
In case we turn our hand to the right or left we want the drone to rotate clockwise or counter clockwise respectively:
|
||||
|
||||
|
||||
if(hand.s>1.5 && Math.abs(handStartDirection[0]-hand.direction[0]) > TURN_TRESHOLD ) {
|
||||
var signal = handStartDirection[0]-hand.direction[0];
|
||||
var value = (Math.abs(handStartDirection[0]-hand.direction[0])-TURN_TRESHOLD) * TURN_SPEED_FACTOR;
|
||||
if (signal>0){
|
||||
my.drone.counterClockwise(value);
|
||||
}
|
||||
|
||||
if (signal<0){
|
||||
my.drone.clockwise(value);
|
||||
}
|
||||
}
|
||||
|
||||
In case we raise our hand up or lower it down we tell the drone to go up or down respectively:
|
||||
|
||||
if (hand.s>1.5 && Math.abs(hand.palmPosition[1]-handStartPosition[1]) > UP_CONTROL_THRESHOLD) {
|
||||
var signal = (hand.palmPosition[1]-handStartPosition[1]) >= 0 ? 1 : -1;
|
||||
var value = Math.round(Math.abs((hand.palmPosition[1]-handStartPosition[1]))-UP_CONTROL_THRESHOLD) * UP_SPEED_FACTOR;
|
||||
|
||||
if (signal>0) {
|
||||
my.drone.up(value);
|
||||
};
|
||||
|
||||
if (signal<0) {
|
||||
my.drone.down(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
In order to move the drone forward, backward, left or right we need to detect the hand inclination. Imagine your hand is the drone and lean it towards the direction we want it to move.
|
||||
|
||||
|
||||
if (hand.s>1.5 && (Math.abs(hand.palmNormal[2])>DIRECTION_THRESHOLD)) {
|
||||
if (hand.palmNormal[2]>0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[2] * 10 + DIRECTION_THRESHOLD ) * DIRECTION_SPEED_FACTOR);
|
||||
my.drone.forward( value );
|
||||
};
|
||||
|
||||
if (hand.palmNormal[2]<0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[2] * 10 - DIRECTION_THRESHOLD ) * DIRECTION_SPEED_FACTOR);
|
||||
my.drone.back( value );
|
||||
};
|
||||
}
|
||||
|
||||
if (hand.s>1.5 && (Math.abs(hand.palmNormal[0])>DIRECTION_THRESHOLD)) {
|
||||
if (hand.palmNormal[0]>0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[0] * 10 + DIRECTION_THRESHOLD ) * DIRECTION_SPEED_FACTOR);
|
||||
my.drone.left( value );
|
||||
};
|
||||
|
||||
if (hand.palmNormal[0]<0) {
|
||||
var value = Math.abs(Math.round( hand.palmNormal[0] * 10 - DIRECTION_THRESHOLD ) * DIRECTION_SPEED_FACTOR);
|
||||
my.drone.right( value );
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Whenever we close our hand we tell the drone no stop:
|
||||
|
||||
if (hand.s<=1.5 && lastS > 1.5) { // closed hand
|
||||
my.drone.stop();
|
||||
}
|
||||
|
||||
|
||||
And finally lets use the arrow keys to tell the drone to do some cool stunts:
|
||||
|
||||
|
||||
my.keyboard.on('right', function(key) {
|
||||
my.drone.rightFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('left', function(key) {
|
||||
my.drone.leftFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('up', function(key) {
|
||||
my.drone.frontFlip();
|
||||
});
|
||||
|
||||
my.keyboard.on('down', function(key) {
|
||||
my.drone.backFlip();
|
||||
});
|
||||
|
||||
|
||||
Now that our robot knows what work to do, and the work it will be doing that
|
||||
hardware with, we can start it:
|
||||
|
||||
}).start();
|
|
@ -29,9 +29,8 @@ var SalesforceRobot = (function() {
|
|||
msg += "Bucks: " + data.sobject.Bucks__c + ",";
|
||||
msg += "SM_Id: " + data.sobject.Id;
|
||||
console.log(msg);
|
||||
me.master.findRobot(data.sobject.Sphero_Name__c, function(err, spheroBot) {
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
});
|
||||
var spheroBot = Cylon.robots[data.sobject.Sphero_Name__c];
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -70,9 +69,8 @@ var SpheroRobot = (function() {
|
|||
spheroName: "" + me.name,
|
||||
bucks: "" + (me.totalBucks++)
|
||||
});
|
||||
me.master.findRobot('salesforce', function(err, sf) {
|
||||
sf.devices.salesforce.push('SpheroController', 'POST', data);
|
||||
});
|
||||
var sf = Cylon.robots['salesforce'];
|
||||
sf.devices.salesforce.push('SpheroController', 'POST', data);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -42,9 +42,8 @@ tell it what work we want to do:
|
|||
msg += "Bucks: " + data.sobject.Bucks__c + ",";
|
||||
msg += "SM_Id: " + data.sobject.Id;
|
||||
console.log(msg);
|
||||
me.master.findRobot(data.sobject.Sphero_Name__c, function(err, spheroBot) {
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
});
|
||||
var spheroBot = Cylon.robots[data.sobject.Sphero_Name__c];
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -83,9 +82,8 @@ tell it what work we want to do:
|
|||
spheroName: "" + me.name,
|
||||
bucks: "" + (me.totalBucks++)
|
||||
});
|
||||
me.master.findRobot('salesforce', function(err, sf) {
|
||||
sf.devices.salesforce.push('SpheroController', 'POST', data);
|
||||
});
|
||||
var sf = Cylon.robots['salesforce'];
|
||||
sf.devices.salesforce.push('SpheroController', 'POST', data);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -57,9 +57,8 @@ var SalesforceRobot = (function() {
|
|||
|
||||
console.log(msg);
|
||||
|
||||
me.master.findRobot(name, function(err, spheroBot) {
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
});
|
||||
var spheroBot = Cylon.robots[name];
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
|
||||
me.spheroReport[name] = bucks;
|
||||
toPebble = "";
|
||||
|
@ -71,9 +70,8 @@ var SalesforceRobot = (function() {
|
|||
toPebble += "" + key + ": $" + val + "\n";
|
||||
}
|
||||
|
||||
me.master.findRobot('pebble', function(error, pebbleBot) {
|
||||
pebbleBot.message(pebbleBot.devices.pebble, toPebble);
|
||||
});
|
||||
var pebbleBot = Cylon.robots['pebble'];
|
||||
pebbleBot.message(pebbleBot.devices.pebble, toPebble);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -141,9 +139,8 @@ var SpheroRobot = (function() {
|
|||
bucks: "" + (me.totalBucks++)
|
||||
});
|
||||
|
||||
me.master.findRobot('salesforce', function(err, sf) {
|
||||
sf.devices.salesforce.push("SpheroController", "POST", data);
|
||||
});
|
||||
var sf = Cylon.robots['salesforce'];
|
||||
sf.devices.salesforce.push("SpheroController", "POST", data);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -78,9 +78,8 @@ Tell it what work we want to do:
|
|||
|
||||
console.log(msg);
|
||||
|
||||
me.master.findRobot(name, function(err, spheroBot) {
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
});
|
||||
var spheroBot = Cylon.robots[name];
|
||||
spheroBot.react(spheroBot.devices.sphero);
|
||||
|
||||
me.spheroReport[name] = bucks;
|
||||
toPebble = "";
|
||||
|
@ -92,9 +91,8 @@ Tell it what work we want to do:
|
|||
toPebble += "" + key + ": $" + val + "\n";
|
||||
}
|
||||
|
||||
me.master.findRobot('pebble', function(error, pebbleBot) {
|
||||
pebbleBot.message(pebbleBot.devices.pebble, toPebble);
|
||||
});
|
||||
var pebbleBot = Cylon.robots['pebble'];
|
||||
pebbleBot.message(pebbleBot.devices.pebble, toPebble);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -166,9 +164,8 @@ Tell it what work we want to do:
|
|||
bucks: "" + (me.totalBucks++)
|
||||
});
|
||||
|
||||
me.master.findRobot('salesforce', function(err, sf) {
|
||||
sf.devices.salesforce.push("SpheroController", "POST", data);
|
||||
});
|
||||
var sf = Cylon.robots['salesforce'];
|
||||
sf.devices.salesforce.push("SpheroController", "POST", data);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"use strict";
|
||||
|
||||
var Basestar = require('./basestar'),
|
||||
Logger = require('./logger');
|
||||
Logger = require('./logger'),
|
||||
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
|
||||
|
@ -31,33 +32,31 @@ module.exports = Adaptor = function Adaptor(opts) {
|
|||
this.self = this;
|
||||
this.name = opts.name;
|
||||
this.connection = opts.connection;
|
||||
this.commandList = [];
|
||||
};
|
||||
|
||||
subclass(Adaptor, Basestar);
|
||||
Utils.subclass(Adaptor, Basestar);
|
||||
|
||||
// Public: Exposes all commands the adaptor will respond to/proxy
|
||||
//
|
||||
// Returns an array of string method names
|
||||
Adaptor.prototype.commands = function() {
|
||||
return this.commandList;
|
||||
};
|
||||
Adaptor.prototype.commands = [];
|
||||
|
||||
// Public: Connects to the adaptor, and emits 'connect' from the @connection
|
||||
// when done.
|
||||
// Public: Connects to the adaptor, and triggers the provided callback when
|
||||
// done.
|
||||
//
|
||||
// callback - function to run when the adaptor is connected
|
||||
//
|
||||
// Returns nothing
|
||||
Adaptor.prototype.connect = function(callback) {
|
||||
Logger.info("Connecting to adaptor '" + this.name + "'...");
|
||||
Logger.info("Connecting to adaptor '" + this.name + "'.");
|
||||
callback(null);
|
||||
return this.connection.emit('connect');
|
||||
return true;
|
||||
};
|
||||
|
||||
// Public: Disconnects from the adaptor
|
||||
//
|
||||
// callback - function to run when the adaptor is disconnected
|
||||
//
|
||||
// Returns nothing
|
||||
Adaptor.prototype.disconnect = function() {
|
||||
return Logger.info("Disconnecting from adaptor '" + this.name + "'...");
|
||||
Adaptor.prototype.disconnect = function(callback) {
|
||||
Logger.info("Disconnecting from adaptor '" + this.name + "'.");
|
||||
this.removeAllListeners();
|
||||
callback();
|
||||
};
|
||||
|
|
257
lib/api.js
257
lib/api.js
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* api
|
||||
* Cylon API
|
||||
* cylonjs.com
|
||||
*
|
||||
* Copyright (c) 2013-2014 The Hybrid Group
|
||||
|
@ -8,9 +8,11 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var fs = require('fs');
|
||||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
var express = require('express');
|
||||
var express = require('express'),
|
||||
bodyParser = require('body-parser');
|
||||
|
||||
var Logger = require('./logger');
|
||||
|
||||
|
@ -21,198 +23,103 @@ var API = module.exports = function API(opts) {
|
|||
opts = {};
|
||||
}
|
||||
|
||||
this.opts = opts;
|
||||
this.host = opts.host || "127.0.0.1";
|
||||
this.port = opts.port || "3000";
|
||||
this.ssl = opts.ssl;
|
||||
this.master = opts.master;
|
||||
this.server = express();
|
||||
for (var d in this.defaults) {
|
||||
this[d] = opts.hasOwnProperty(d) ? opts[d] : this.defaults[d];
|
||||
}
|
||||
|
||||
this.createServer();
|
||||
|
||||
this.express.set('title', 'Cylon API Server');
|
||||
|
||||
this.express.use(self.setupAuth());
|
||||
this.express.use(bodyParser());
|
||||
this.express.use(express["static"](__dirname + "/../node_modules/robeaux/"));
|
||||
|
||||
// set CORS headers for API requests
|
||||
this.express.use(function(req, res, next) {
|
||||
res.set("Access-Control-Allow-Origin", self.CORS || "*");
|
||||
res.set("Access-Control-Allow-Headers", "Content-Type");
|
||||
res.set('Content-Type', 'application/json');
|
||||
return next();
|
||||
});
|
||||
|
||||
// extracts command params from request
|
||||
this.express.use(function(req, res, next) {
|
||||
var method = req.method.toLowerCase(),
|
||||
container = {};
|
||||
|
||||
req.commandParams = [];
|
||||
|
||||
if (method === 'get' || Object.keys(req.query).length > 0) {
|
||||
container = req.query;
|
||||
} else if (typeof(req.body) === 'object') {
|
||||
container = req.body;
|
||||
}
|
||||
|
||||
for (var p in container) {
|
||||
req.commandParams.push(container[p]);
|
||||
};
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
// load route definitions
|
||||
this.express.use('/', require('./api/routes'))
|
||||
};
|
||||
|
||||
API.prototype.defaults = {
|
||||
host: '127.0.0.1',
|
||||
port: '3000',
|
||||
auth: false,
|
||||
CORS: '',
|
||||
ssl: {
|
||||
key: path.normalize(__dirname + "/api/ssl/server.key"),
|
||||
cert: path.normalize(__dirname + "/api/ssl/server.crt")
|
||||
}
|
||||
};
|
||||
|
||||
API.prototype.createServer = function createServer() {
|
||||
this.express = express();
|
||||
|
||||
//configure ssl if requested
|
||||
if (this.ssl && typeof(this.ssl) === 'object') {
|
||||
var https = require('https');
|
||||
|
||||
var options = {
|
||||
key: fs.readFileSync(this.ssl.key || __dirname + "/ssl/server.key"),
|
||||
cert: fs.readFileSync(this.ssl.cert || __dirname + "/ssl/server.crt")
|
||||
};
|
||||
|
||||
this.server.node = https.createServer(options, this.server);
|
||||
this.server = https.createServer({
|
||||
key: fs.readFileSync(this.ssl.key),
|
||||
cert: fs.readFileSync(this.ssl.cert)
|
||||
}, this.express);
|
||||
} else {
|
||||
Logger.warn("API using insecure connection. We recommend using an SSL certificate with Cylon.")
|
||||
this.server.node = this.server;
|
||||
this.server = this.express;
|
||||
}
|
||||
};
|
||||
|
||||
// configure basic auth, if requested
|
||||
if (opts.auth && opts.auth.type && opts.auth.type === 'basic') {
|
||||
var user = opts.auth.user,
|
||||
pass = opts.auth.pass;
|
||||
API.prototype.setupAuth = function setupAuth() {
|
||||
var authfn = function auth(req, res, next) { next(); };
|
||||
|
||||
if (user && pass) {
|
||||
this.server.use(express.basicAuth(user, pass));
|
||||
if (typeof(this.auth) === 'object' && this.auth.type) {
|
||||
var type = this.auth.type,
|
||||
module = "./api/auth/" + type,
|
||||
filename = path.normalize(__dirname + "/" + module + ".js"),
|
||||
exists = fs.existsSync(filename);
|
||||
|
||||
if (exists) {
|
||||
authfn = require(filename)(this.auth);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.server.set('title', 'Cylon API Server');
|
||||
this.server.use(express.json());
|
||||
this.server.use(express.urlencoded());
|
||||
this.server.use(express["static"](__dirname + "/../node_modules/robeaux/"));
|
||||
return authfn;
|
||||
};
|
||||
|
||||
API.prototype.listen = function() {
|
||||
var self = this;
|
||||
|
||||
this.server.node.listen(this.port, this.host, null, function() {
|
||||
var title = self.server.get('title');
|
||||
this.server.listen(this.port, this.host, null, function() {
|
||||
var title = self.express.get('title');
|
||||
var protocol = self.ssl ? "https" : "http";
|
||||
|
||||
Logger.info(title + " is now online.");
|
||||
Logger.info("Listening at " + protocol + "://" + self.host + ":" + self.port);
|
||||
});
|
||||
};
|
||||
|
||||
// Parses req to extract params to be used for commands.
|
||||
//
|
||||
// Returns an array of params
|
||||
API.prototype.parseCommandParams = function(req) {
|
||||
var param_container = {},
|
||||
params = [];
|
||||
|
||||
if (req.method === 'GET' || Object.keys(req.query).length > 0) {
|
||||
param_container = req.query;
|
||||
} else if (typeof req.body === 'object') {
|
||||
param_container = req.body;
|
||||
}
|
||||
|
||||
for (var p in param_container) { params.push(param_container[p]); }
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
// Sets all routes for the server
|
||||
API.prototype.configureRoutes = function() {
|
||||
var self = this;
|
||||
|
||||
var master = this.master;
|
||||
|
||||
this.server.all("/*", function(req, res, next) {
|
||||
res.set("Access-Control-Allow-Origin", self.opts.CORS || "*");
|
||||
res.set("Access-Control-Allow-Headers", "Content-Type");
|
||||
res.set('Content-Type', 'application/json');
|
||||
return next();
|
||||
});
|
||||
|
||||
this.server.get("/robots", function(req, res) {
|
||||
var data = [];
|
||||
|
||||
for (var i = 0; i < master.robots.length; i++) {
|
||||
var robot = master.robots[i];
|
||||
data.push(robot.data());
|
||||
}
|
||||
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot", function(req, res) {
|
||||
master.findRobot(req.params.robot, function(err, robot) {
|
||||
res.json(err ? err : robot.data());
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/commands", function(req, res) {
|
||||
master.findRobot(req.params.robot, function(err, robot) {
|
||||
res.json(err ? err : robot.data().commands);
|
||||
});
|
||||
});
|
||||
|
||||
this.server.all("/robots/:robot/commands/:command", function(req, res) {
|
||||
var params = self.parseCommandParams(req);
|
||||
|
||||
master.findRobot(req.params.robot, function(err, robot) {
|
||||
if (err) { return res.json(err); }
|
||||
|
||||
var result = robot[req.params.command].apply(robot, params);
|
||||
res.json({ result: result });
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/devices", function(req, res) {
|
||||
master.findRobot(req.params.robot, function(err, robot) {
|
||||
res.json(err ? err : robot.data().devices);
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/devices/:device", function(req, res) {
|
||||
var robot = req.params.robot,
|
||||
device = req.params.device;
|
||||
|
||||
master.findRobotDevice(robot, device, function(err, device) {
|
||||
res.json(err ? err : device.data());
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/devices/:device/events/:event", function(req, res) {
|
||||
var robot = req.params.robot,
|
||||
device = req.params.device,
|
||||
event = req.params.event;
|
||||
|
||||
master.findRobotDevice(robot, device, function(err, device) {
|
||||
if (err) { res.json(err); }
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Connection': 'keep-alive',
|
||||
'Cache-Control': 'no-cache'
|
||||
});
|
||||
|
||||
var writeData = function(data) {
|
||||
res.write("data: " + JSON.stringify(data) + "\n\n")
|
||||
};
|
||||
|
||||
device.on(event, writeData);
|
||||
|
||||
res.on('close', function() {
|
||||
device.removeListener(event, writeData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/devices/:device/commands", function(req, res) {
|
||||
var robot = req.params.robot,
|
||||
device = req.params.device;
|
||||
|
||||
master.findRobotDevice(robot, device, function(err, device) {
|
||||
res.json(err ? err : device.data().commands);
|
||||
});
|
||||
});
|
||||
|
||||
this.server.all("/robots/:robot/devices/:device/commands/:command", function(req, res) {
|
||||
var robot = req.params.robot,
|
||||
device = req.params.device,
|
||||
command = req.params.command;
|
||||
|
||||
var params = self.parseCommandParams(req);
|
||||
|
||||
master.findRobotDevice(robot, device, function(err, device) {
|
||||
if (err) { return res.json(err); }
|
||||
|
||||
var result = device[command].apply(device, params);
|
||||
res.json({ result: result });
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/connections", function(req, res) {
|
||||
master.findRobot(req.params.robot, function(err, robot) {
|
||||
res.json(err ? err : robot.data().connections);
|
||||
});
|
||||
});
|
||||
|
||||
this.server.get("/robots/:robot/connections/:connection", function(req, res) {
|
||||
var robot = req.params.robot,
|
||||
connection = req.params.connection;
|
||||
|
||||
master.findRobotConnection(robot, connection, function(err, connection) {
|
||||
res.json(err ? err : connection.data());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Cylon API - Basic Auth
|
||||
* cylonjs.com
|
||||
*
|
||||
* Copyright (c) 2013-2014 The Hybrid Group
|
||||
* Licensed under the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
|
||||
module.exports = function(config) {
|
||||
var user = config.user,
|
||||
pass = config.pass;
|
||||
|
||||
return function auth(req, res, next) {
|
||||
var auth = req.headers.authorization;
|
||||
|
||||
if (!auth) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
// malformed
|
||||
var parts = auth.split(' ');
|
||||
|
||||
if ('basic' != parts[0].toLowerCase() || !parts[1]) {
|
||||
return next(error(400));
|
||||
}
|
||||
|
||||
auth = parts[1];
|
||||
|
||||
// credentials
|
||||
auth = new Buffer(auth, 'base64').toString();
|
||||
auth = auth.match(/^([^:]+):(.+)$/);
|
||||
|
||||
if (!auth) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
if (auth[1] === user && auth[2] === pass) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return unauthorized(res);
|
||||
};
|
||||
};
|
||||
|
||||
var unauthorized = function unauthorized(res) {
|
||||
res.statusCode = 401;
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="Authorization Required"');
|
||||
res.end('Unauthorized');
|
||||
};
|
||||
|
||||
var error = function error(code, msg){
|
||||
var err = new Error(msg || http.STATUS_CODES[code]);
|
||||
err.status = code;
|
||||
return err;
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Cylon API - Route Definitions
|
||||
* cylonjs.com
|
||||
*
|
||||
* Copyright (c) 2013-2014 The Hybrid Group
|
||||
* Licensed under the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
var Cylon = require('../cylon');
|
||||
|
||||
var router = module.exports = require('express').Router();
|
||||
|
||||
// Loads up the appropriate Robot/Device/Connection instances, if they are
|
||||
// present in the route params.
|
||||
var load = function load(req, res, next) {
|
||||
var robot = req.params.robot,
|
||||
device = req.params.device,
|
||||
connection = req.params.connection;
|
||||
|
||||
if (robot) {
|
||||
req.robot = Cylon.robots[robot];
|
||||
if (!req.robot) {
|
||||
return res.json({ error: "No Robot found with the name " + robot });
|
||||
}
|
||||
}
|
||||
|
||||
if (device) {
|
||||
req.device = req.robot.devices[device];
|
||||
if (!req.device) {
|
||||
return res.json({ error: "No device found with the name " + device });
|
||||
}
|
||||
}
|
||||
|
||||
if (connection) {
|
||||
req.connection = req.robot.connections[connection];
|
||||
if (!req.connection) {
|
||||
return res.json({ error: "No connection found with the name " + connection });
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
router.get("/robots", function(req, res) {
|
||||
var data = [];
|
||||
|
||||
for (var bot in Cylon.robots) {
|
||||
data.push(Cylon.robots[bot]);
|
||||
}
|
||||
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
router.get("/robots/:robot", load, function(req, res) {
|
||||
res.json(req.robot);
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/commands", load, function(req, res) {
|
||||
res.json(req.robot.toJSON().commands);
|
||||
});
|
||||
|
||||
router.all("/robots/:robot/commands/:command", load, function(req, res) {
|
||||
var command = req.params.command;
|
||||
|
||||
var result = req.robot[command].apply(req.robot, req.commandParams);
|
||||
res.json({ result: result });
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/devices", load, function(req, res) {
|
||||
res.json(req.robot.toJSON().devices);
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/devices/:device", load, function(req, res) {
|
||||
res.json(req.device);
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/devices/:device/events/:event", load, function(req, res) {
|
||||
var event = req.params.event;
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Connection': 'keep-alive',
|
||||
'Cache-Control': 'no-cache'
|
||||
});
|
||||
|
||||
var writeData = function(data) {
|
||||
res.write("data: " + JSON.stringify(data) + "\n\n");
|
||||
};
|
||||
|
||||
req.device.on(event, writeData);
|
||||
|
||||
res.on('close', function() {
|
||||
req.device.removeListener(event, writeData);
|
||||
});
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/devices/:device/commands", load, function(req, res) {
|
||||
res.json(req.device.toJSON().commands);
|
||||
});
|
||||
|
||||
router.all("/robots/:robot/devices/:device/commands/:command", load, function(req, res) {
|
||||
var command = req.params.command;
|
||||
|
||||
var result = req.device[command].apply(req.device, req.commandParams);
|
||||
res.json({ result: result });
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/connections", load, function(req, res) {
|
||||
res.json(req.robot.toJSON().connections);
|
||||
});
|
||||
|
||||
router.get("/robots/:robot/connections/:connection", load, function(req, res) {
|
||||
res.json(req.connection);
|
||||
});
|
|
@ -8,22 +8,19 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
require('./utils');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Utils = require('./utils');
|
||||
|
||||
// Basestar is a base class to be used when writing external Cylon adaptors and
|
||||
// drivers. It provides some useful base methods and functionality
|
||||
//
|
||||
// It also extends EventEmitter, so child classes are capable of emitting events
|
||||
// for other parts of the system to handle.
|
||||
var Basestar;
|
||||
var Basestar = module.exports = function Basestar() {
|
||||
};
|
||||
|
||||
module.exports = Basestar = function Basestar(opts) {
|
||||
this.self = this;
|
||||
}
|
||||
|
||||
subclass(Basestar, EventEmitter);
|
||||
Utils.subclass(Basestar, EventEmitter);
|
||||
|
||||
// Public: Proxies calls from all methods in the object to a target object
|
||||
//
|
||||
|
@ -34,8 +31,11 @@ subclass(Basestar, EventEmitter);
|
|||
//
|
||||
// Returns the klass where the methods have been proxied
|
||||
Basestar.prototype.proxyMethods = function(methods, target, source, force) {
|
||||
if (force == null) { force = false; }
|
||||
return proxyFunctionsToObject(methods, target, source, force);
|
||||
if (force == null) {
|
||||
force = false;
|
||||
}
|
||||
|
||||
return Utils.proxyFunctionsToObject(methods, target, source, force);
|
||||
};
|
||||
|
||||
// Public: Defines an event handler that proxies events from a source object
|
||||
|
@ -50,8 +50,6 @@ Basestar.prototype.proxyMethods = function(methods, target, source, force) {
|
|||
//
|
||||
// Returns the source
|
||||
Basestar.prototype.defineEvent = function(opts) {
|
||||
var self = this;
|
||||
|
||||
opts.sendUpdate = opts.sendUpdate || false;
|
||||
opts.targetEventName = opts.targetEventName || opts.eventName;
|
||||
|
||||
|
@ -77,7 +75,9 @@ Basestar.prototype.defineEvent = function(opts) {
|
|||
//
|
||||
// Returns this.connector
|
||||
Basestar.prototype.defineAdaptorEvent = function(opts) {
|
||||
if (typeof opts === 'string') { opts = { eventName: opts }; }
|
||||
if (typeof opts === 'string') {
|
||||
opts = { eventName: opts };
|
||||
}
|
||||
|
||||
opts['source'] = this.connector;
|
||||
opts['target'] = this.connection;
|
||||
|
@ -93,7 +93,9 @@ Basestar.prototype.defineAdaptorEvent = function(opts) {
|
|||
//
|
||||
// Returns this.connection
|
||||
Basestar.prototype.defineDriverEvent = function(opts) {
|
||||
if (typeof opts === 'string') { opts = { eventName: opts }; }
|
||||
if (typeof opts === 'string') {
|
||||
opts = { eventName: opts };
|
||||
}
|
||||
|
||||
opts['source'] = this.connection;
|
||||
opts['target'] = this.device;
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
require("./utils");
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Logger = require('./logger');
|
||||
var Logger = require('./logger'),
|
||||
Utils = require('./utils');
|
||||
|
||||
// The Connection class represents the interface to
|
||||
// a specific group of hardware devices. Examples would be an
|
||||
|
@ -38,7 +37,7 @@ module.exports = Connection = function Connection(opts) {
|
|||
opts.id = Math.floor(Math.random() * 10000);
|
||||
}
|
||||
|
||||
this.connect = bind(this.connect, this);
|
||||
this.connect = Utils.bind(this.connect, this);
|
||||
|
||||
this.self = this;
|
||||
this.robot = opts.robot;
|
||||
|
@ -47,15 +46,15 @@ module.exports = Connection = function Connection(opts) {
|
|||
this.port = opts.port;
|
||||
this.adaptor = this.initAdaptor(opts);
|
||||
|
||||
proxyFunctionsToObject(this.adaptor.commands(), this.adaptor, this.self);
|
||||
Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this.self);
|
||||
}
|
||||
|
||||
subclass(Connection, EventEmitter);
|
||||
Utils.subclass(Connection, EventEmitter);
|
||||
|
||||
// Public: Exports basic data for the Connection
|
||||
// Public: Expresses the Connection in JSON format
|
||||
//
|
||||
// Returns an Object containing Connection data
|
||||
Connection.prototype.data = function() {
|
||||
Connection.prototype.toJSON = function() {
|
||||
return {
|
||||
name: this.name,
|
||||
port: this.port,
|
||||
|
@ -71,27 +70,34 @@ Connection.prototype.data = function() {
|
|||
// Returns the result of the supplied callback function
|
||||
Connection.prototype.connect = function(callback) {
|
||||
var msg = "Connecting to " + this.name;
|
||||
var msg = "Connecting to '" + this.name + "'";
|
||||
|
||||
if (this.port != null) {
|
||||
msg += " on port " + this.port;
|
||||
}
|
||||
|
||||
msg += ".";
|
||||
|
||||
Logger.info(msg);
|
||||
return this.adaptor.connect(callback);
|
||||
};
|
||||
|
||||
// Public: Disconnect the adaptor's connection
|
||||
//
|
||||
// callback - function to be triggered then the adaptor has disconnected
|
||||
//
|
||||
// Returns nothing
|
||||
Connection.prototype.disconnect = function() {
|
||||
var msg = "Disconnecting from " + this.name;
|
||||
Connection.prototype.disconnect = function(callback) {
|
||||
var msg = "Disconnecting from '" + this.name + "'";
|
||||
|
||||
if (this.port != null) {
|
||||
msg += " on port " + this.port;
|
||||
}
|
||||
|
||||
msg += ".";
|
||||
|
||||
Logger.info(msg);
|
||||
return this.adaptor.disconnect();
|
||||
return this.adaptor.disconnect(callback);
|
||||
};
|
||||
|
||||
// Public: sets up adaptor with @robot
|
||||
|
@ -101,14 +107,16 @@ Connection.prototype.disconnect = function() {
|
|||
//
|
||||
// Returns the set-up adaptor
|
||||
Connection.prototype.initAdaptor = function(opts) {
|
||||
Logger.debug("Loading adaptor '" + opts.adaptor + "'");
|
||||
Logger.debug("Loading adaptor '" + opts.adaptor + "'.");
|
||||
return this.robot.initAdaptor(opts.adaptor, this.self, opts);
|
||||
};
|
||||
|
||||
// Public: Halt the adaptor's connection
|
||||
//
|
||||
// callback - function to be triggered when the connection has halted
|
||||
//
|
||||
// Returns nothing
|
||||
Connection.prototype.halt = function() {
|
||||
Connection.prototype.halt = function(callback) {
|
||||
var msg = "Halting adaptor " + this.name;
|
||||
|
||||
if (this.port != null) {
|
||||
|
@ -116,5 +124,5 @@ Connection.prototype.halt = function() {
|
|||
}
|
||||
|
||||
Logger.info(msg);
|
||||
return this.disconnect();
|
||||
return this.disconnect(callback);
|
||||
};
|
||||
|
|
146
lib/cylon.js
146
lib/cylon.js
|
@ -8,6 +8,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var Async = require('async');
|
||||
|
||||
var Logger = require('./logger'),
|
||||
Robot = require('./robot'),
|
||||
Utils = require('./utils');
|
||||
|
@ -18,6 +20,7 @@ var Cylon = module.exports = {
|
|||
Logger: Logger,
|
||||
Driver: require('./driver'),
|
||||
Adaptor: require('./adaptor'),
|
||||
Utils: Utils,
|
||||
|
||||
IO: {
|
||||
DigitalPin: require('./io/digital-pin'),
|
||||
|
@ -26,15 +29,7 @@ var Cylon = module.exports = {
|
|||
|
||||
api_instance: null,
|
||||
|
||||
robots: [],
|
||||
|
||||
api_config: {
|
||||
host: '127.0.0.1',
|
||||
port: '3000',
|
||||
auth: {},
|
||||
CORS: null,
|
||||
ssl: {}
|
||||
}
|
||||
robots: {}
|
||||
};
|
||||
|
||||
// Public: Creates a new Robot
|
||||
|
@ -50,143 +45,53 @@ var Cylon = module.exports = {
|
|||
// work: (me) ->
|
||||
// me.led.toggle()
|
||||
Cylon.robot = function robot(opts) {
|
||||
opts.master = this;
|
||||
var robot = new Robot(opts);
|
||||
this.robots.push(robot);
|
||||
this.robots[robot.name] = robot;
|
||||
return robot;
|
||||
};
|
||||
|
||||
// Public: Configures the API host and port based on passed options
|
||||
// Public: Creates a new API based on passed options
|
||||
//
|
||||
// opts - object containing API options
|
||||
// host - host address API should serve from
|
||||
// port - port API should listen for requests on
|
||||
//
|
||||
// Returns the API configuration
|
||||
// Returns nothing
|
||||
Cylon.api = function api(opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
var keys = ['host', 'port', 'auth', 'CORS', 'ssl'];
|
||||
var API = require('./api');
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
if (typeof opts[key] !== "undefined") {
|
||||
this.api_config[key] = opts[key];
|
||||
}
|
||||
}
|
||||
|
||||
return this.api_config;
|
||||
};
|
||||
|
||||
// Public: Finds a particular robot by name
|
||||
//
|
||||
// name - name of the robot to find
|
||||
// callback - optional callback to be executed
|
||||
//
|
||||
// Returns the found Robot or result of the callback if it's supplied
|
||||
Cylon.findRobot = function findRobot(name, callback) {
|
||||
var error,
|
||||
robot = null;
|
||||
|
||||
for (var i = 0; i < this.robots.length; i++) {
|
||||
var bot = this.robots[i];
|
||||
if (bot.name === name) { robot = bot; }
|
||||
}
|
||||
|
||||
if (robot == null) {
|
||||
error = { error: "No Robot found with the name " + name };
|
||||
}
|
||||
|
||||
return callback ? callback(error, robot) : robot;
|
||||
};
|
||||
|
||||
// Public: Finds a particular Robot's device by name
|
||||
//
|
||||
// robotid - name of the robot to find
|
||||
// deviceid - name of the device to find
|
||||
// callback - optional callback to be executed
|
||||
//
|
||||
// Returns the found Device or result of the callback if it's supplied
|
||||
Cylon.findRobotDevice = function findRobotDevice(robotid, deviceid, callback) {
|
||||
return this.findRobot(robotid, function(err, robot) {
|
||||
var error,
|
||||
device = null;
|
||||
|
||||
if (err) { return callback ? callback(err, robot) : robot }
|
||||
|
||||
if (robot.devices[deviceid]) { device = robot.devices[deviceid]; }
|
||||
|
||||
if (device == null) {
|
||||
error = { error: "No device found with the name " + deviceid + "." };
|
||||
}
|
||||
|
||||
return callback ? callback(error, device) : device;
|
||||
});
|
||||
};
|
||||
|
||||
// Public: Finds a particular Robot's connection by name
|
||||
//
|
||||
// robotid - name of the robot to find
|
||||
// connid - name of the device to find
|
||||
// callback - optional callback to be executed
|
||||
//
|
||||
// Returns the found Connection or result of the callback if it's supplied
|
||||
Cylon.findRobotConnection = function findRobotConnection(robotid, connid, callback) {
|
||||
return this.findRobot(robotid, function(err, robot) {
|
||||
var error,
|
||||
connection = null;
|
||||
|
||||
if (err) { return callback ? callback(err, robot) : robot }
|
||||
|
||||
if (robot.connections[connid]) { connection = robot.connections[connid]; }
|
||||
|
||||
if (connection == null) {
|
||||
error = { error: "No connection found with the name " + connid + "." };
|
||||
}
|
||||
|
||||
return callback ? callback(error, connection) : connection;
|
||||
});
|
||||
this.api_instance = new API(opts);
|
||||
this.api_instance.listen();
|
||||
};
|
||||
|
||||
// Public: Starts up the API and the robots
|
||||
//
|
||||
// Returns nothing
|
||||
Cylon.start = function start() {
|
||||
this.startAPI();
|
||||
|
||||
for (var i = 0; i < this.robots.length; i++) {
|
||||
var robot = this.robots[i];
|
||||
robot.start();
|
||||
for (var bot in this.robots) {
|
||||
this.robots[bot].start();
|
||||
}
|
||||
};
|
||||
|
||||
// Public: Halts the API and the robots
|
||||
//
|
||||
// Returns nothing
|
||||
Cylon.halt = function halt() {
|
||||
for (var i = 0; i < this.robots.length; i++) {
|
||||
var robot = this.robots[i];
|
||||
robot.halt();
|
||||
}
|
||||
};
|
||||
|
||||
// Creates a new instance of the Cylon API server, or returns the
|
||||
// already-existing one.
|
||||
// callback - callback to be triggered when Cylon is ready to shutdown
|
||||
//
|
||||
// Returns an Cylon.ApiServer instance
|
||||
Cylon.startAPI = function startAPI() {
|
||||
var API = require('./api');
|
||||
this.api_config.master = this;
|
||||
// Returns nothing
|
||||
Cylon.halt = function halt(callback) {
|
||||
// set a timeout, in case trying to shut everything down nicely doesn't work
|
||||
Utils.after((1.5).seconds(), callback);
|
||||
|
||||
if (this.api_instance === null) {
|
||||
this.api_instance = new API(this.api_config);
|
||||
this.api_instance.configureRoutes();
|
||||
this.api_instance.listen();
|
||||
var fns = [];
|
||||
|
||||
for (var bot in this.robots) {
|
||||
var robot = this.robots[bot];
|
||||
fns.push(robot.halt.bind(robot));
|
||||
}
|
||||
|
||||
return this.api_instance;
|
||||
Async.parallel(fns, callback);
|
||||
};
|
||||
|
||||
if (process.platform === "win32") {
|
||||
|
@ -199,6 +104,7 @@ if (process.platform === "win32") {
|
|||
};
|
||||
|
||||
process.on("SIGINT", function() {
|
||||
Cylon.halt();
|
||||
process.kill(process.pid);
|
||||
Cylon.halt(function() {
|
||||
process.kill(process.pid);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,16 +8,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
require('./utils');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Logger = require('./logger');
|
||||
|
||||
// The Artoo::Device class represents the interface to
|
||||
// a specific individual hardware devices. Examples would be a digital
|
||||
// thermometer connected to an Arduino, or a Sphero's accelerometer
|
||||
var Device;
|
||||
var Logger = require('./logger'),
|
||||
Utils = require('./utils');
|
||||
|
||||
// Public: Creates a new Device
|
||||
//
|
||||
|
@ -29,25 +23,24 @@ var Device;
|
|||
// driver - string name of the module the device driver logic lives in
|
||||
//
|
||||
// Returns a new Device
|
||||
module.exports = Device = function Device(opts) {
|
||||
var Device = module.exports = function Device(opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
this.halt = bind(this.halt, this);
|
||||
this.start = bind(this.start, this);
|
||||
this.halt = Utils.bind(this.halt, this);
|
||||
this.start = Utils.bind(this.start, this);
|
||||
|
||||
this.self = this;
|
||||
this.robot = opts.robot;
|
||||
this.name = opts.name;
|
||||
this.pin = opts.pin;
|
||||
this.connection = this.determineConnection(opts.connection) || this.defaultConnection();
|
||||
this.driver = this.initDriver(opts);
|
||||
|
||||
proxyFunctionsToObject(this.driver.commands(), this.driver, this.self);
|
||||
Utils.proxyFunctionsToObject(this.driver.commands, this.driver, this);
|
||||
};
|
||||
|
||||
subclass(Device, EventEmitter);
|
||||
Utils.subclass(Device, EventEmitter);
|
||||
|
||||
// Public: Starts the device driver
|
||||
//
|
||||
|
@ -55,44 +48,48 @@ subclass(Device, EventEmitter);
|
|||
//
|
||||
// Returns result of supplied callback
|
||||
Device.prototype.start = function(callback) {
|
||||
var msg = "Starting device " + this.name;
|
||||
var msg = "Starting device '" + this.name + "'";
|
||||
|
||||
if (this.pin != null) {
|
||||
msg += " on pin " + this.pin;
|
||||
}
|
||||
|
||||
msg += ".";
|
||||
|
||||
Logger.info(msg);
|
||||
return this.driver.start(callback);
|
||||
};
|
||||
|
||||
// Public: Halt the device driver
|
||||
//
|
||||
// callback - function to trigger when the device has been halted
|
||||
//
|
||||
// Returns result of supplied callback
|
||||
Device.prototype.halt = function() {
|
||||
Logger.info("Halting device " + this.name);
|
||||
return this.driver.halt();
|
||||
Device.prototype.halt = function(callback) {
|
||||
Logger.info("Halting device '" + this.name + "'.");
|
||||
this.driver.halt(callback);
|
||||
};
|
||||
|
||||
// Public: Exports basic data for the Connection
|
||||
// Public: Expresses the Device in JSON format
|
||||
//
|
||||
// Returns an Object containing Connection data
|
||||
Device.prototype.data = function() {
|
||||
Device.prototype.toJSON = function() {
|
||||
return {
|
||||
name: this.name,
|
||||
driver: this.driver.constructor.name || this.driver.name,
|
||||
pin: this.pin,
|
||||
connection: this.connection.data(),
|
||||
commands: this.driver.commands()
|
||||
connection: this.connection.toJSON(),
|
||||
commands: this.driver.commands
|
||||
};
|
||||
};
|
||||
|
||||
// Public: Retrieves the connections from the parent Robot instances
|
||||
//
|
||||
// c - name of the connection to fetch
|
||||
// conn - name of the connection to fetch
|
||||
//
|
||||
// Returns a Connection instance
|
||||
Device.prototype.determineConnection = function(c) {
|
||||
if (c) { return this.robot.connections[c]; }
|
||||
Device.prototype.determineConnection = function(conn) {
|
||||
return this.robot.connections[conn];
|
||||
};
|
||||
|
||||
// Public: Returns a default Connection to use
|
||||
|
@ -116,7 +113,10 @@ Device.prototype.defaultConnection = function() {
|
|||
//
|
||||
// Returns the set-up driver
|
||||
Device.prototype.initDriver = function(opts) {
|
||||
if (opts == null) { opts = {}; }
|
||||
Logger.debug("Loading driver '" + opts.driver + "'");
|
||||
return this.robot.initDriver(opts.driver, this.self, opts);
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
Logger.debug("Loading driver '" + opts.driver + "'.");
|
||||
return this.robot.initDriver(opts.driver, this, opts);
|
||||
};
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
'use strict';
|
||||
|
||||
var Basestar = require('./basestar'),
|
||||
Logger = require('./logger');
|
||||
Logger = require('./logger'),
|
||||
Utils = require('./utils');
|
||||
|
||||
// The Driver class is a base class for Driver classes in external Cylon
|
||||
// modules to use. It offers basic functions for starting/halting that
|
||||
|
@ -32,34 +33,30 @@ module.exports = Driver = function Driver(opts) {
|
|||
this.name = opts.name;
|
||||
this.device = opts.device;
|
||||
this.connection = this.device.connection;
|
||||
this.commandList = [];
|
||||
};
|
||||
|
||||
subclass(Driver, Basestar);
|
||||
Utils.subclass(Driver, Basestar);
|
||||
|
||||
// Public: Exposes all commands the driver will respond to/proxy
|
||||
//
|
||||
// Returns an array of string method names
|
||||
Driver.prototype.commands = function() {
|
||||
return this.commandList;
|
||||
};
|
||||
Driver.prototype.commands = [];
|
||||
|
||||
// Public: Starts up the driver, and emits 'connect' from the @device
|
||||
// when done.
|
||||
// Public: Starts up the driver, and triggers the provided callback when done.
|
||||
//
|
||||
// callback - function to run when the driver is started
|
||||
//
|
||||
// Returns nothing
|
||||
Driver.prototype.start = function(callback) {
|
||||
Logger.info("Driver " + this.name + " started");
|
||||
Logger.info("Driver " + this.name + " started.");
|
||||
callback(null);
|
||||
this.device.emit('start');
|
||||
return true;
|
||||
};
|
||||
|
||||
// Public: Halts the driver
|
||||
//
|
||||
// callback - function to be triggered when the driver is halted
|
||||
//
|
||||
// Returns nothing
|
||||
Driver.prototype.halt = function() {
|
||||
return Logger.info("Driver " + this.name + " halted");
|
||||
Driver.prototype.halt = function(callback) {
|
||||
Logger.info("Driver " + this.name + " halted.");
|
||||
this.removeAllListeners();
|
||||
callback();
|
||||
};
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
var FS = require('fs'),
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Utils = require('../utils');
|
||||
|
||||
var GPIO_PATH = "/sys/class/gpio";
|
||||
|
||||
var GPIO_READ = "in";
|
||||
|
@ -28,7 +30,7 @@ var DigitalPin = module.exports = function DigitalPin(opts) {
|
|||
this.mode = opts.mode;
|
||||
}
|
||||
|
||||
subclass(DigitalPin, EventEmitter);
|
||||
Utils.subclass(DigitalPin, EventEmitter);
|
||||
|
||||
DigitalPin.prototype.connect = function(mode) {
|
||||
var _this = this;
|
||||
|
@ -81,7 +83,7 @@ DigitalPin.prototype.digitalRead = function(interval) {
|
|||
|
||||
if (this.mode !== 'r') { this._setMode('r'); }
|
||||
|
||||
every(interval, function() {
|
||||
Utils.every(interval, function() {
|
||||
FS.readFile(_this._valuePath(), function(err, data) {
|
||||
if (err) {
|
||||
var error = "Error occurred while reading from pin " + _this.pinNum;
|
||||
|
|
|
@ -1,50 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
// The BasicLogger pushes stuff to console.log. Nothing more, nothing less.
|
||||
var BasicLogger = module.exports = function BasicLogger() {}
|
||||
var BasicLogger = module.exports = function BasicLogger() {};
|
||||
|
||||
BasicLogger.prototype.toString = function() {
|
||||
return "BasicLogger";
|
||||
};
|
||||
|
||||
BasicLogger.prototype.debug = function() {
|
||||
var args = getArgs(arguments),
|
||||
string = ["D, [" + (new Date().toISOString()) + "] DEBUG -- :"],
|
||||
data = string.concat(args.slice());
|
||||
|
||||
return console.log.apply(console, data);
|
||||
};
|
||||
|
||||
BasicLogger.prototype.info = function() {
|
||||
var args = getArgs(arguments),
|
||||
string = ["I, [" + (new Date().toISOString()) + "] INFO -- :"],
|
||||
data = string.concat(args.slice());
|
||||
|
||||
return console.log.apply(console, data);
|
||||
};
|
||||
|
||||
BasicLogger.prototype.warn = function() {
|
||||
var args = getArgs(arguments),
|
||||
string = ["W, [" + (new Date().toISOString()) + "] WARN -- :"],
|
||||
data = string.concat(args.slice());
|
||||
|
||||
return console.log.apply(console, data);
|
||||
};
|
||||
|
||||
BasicLogger.prototype.error = function() {
|
||||
var args = getArgs(arguments),
|
||||
string = ["E, [" + (new Date().toISOString()) + "] ERROR -- :"],
|
||||
data = string.concat(args.slice());
|
||||
|
||||
return console.log.apply(console, data);
|
||||
};
|
||||
|
||||
BasicLogger.prototype.fatal = function() {
|
||||
var args = getArgs(arguments),
|
||||
string = ["F, [" + (new Date().toISOString()) + "] FATAL -- :"],
|
||||
data = string.concat(args.slice());
|
||||
|
||||
return console.log.apply(console, data);
|
||||
};
|
||||
|
||||
var getArgs = function(args) {
|
||||
return args.length >= 1 ? [].slice.call(args, 0) : [];
|
||||
};
|
||||
|
||||
var logString = function(type) {
|
||||
var upcase = String(type).toUpperCase(),
|
||||
time = new Date().toISOString();
|
||||
|
||||
var padded = String(" " + upcase).slice(-5);
|
||||
|
||||
return upcase[0] + ", [" + time + "] " + padded + " -- :";
|
||||
};
|
||||
|
||||
|
||||
['debug', 'info', 'warn', 'error', 'fatal'].forEach(function(type) {
|
||||
BasicLogger.prototype[type] = function() {
|
||||
var args = getArgs(arguments);
|
||||
return console.log.apply(console, [].concat(logString(type), args));
|
||||
};
|
||||
});
|
||||
|
|
190
lib/robot.js
190
lib/robot.js
|
@ -8,25 +8,28 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
require('./utils');
|
||||
|
||||
var Connection = require("./connection"),
|
||||
Device = require("./device"),
|
||||
Logger = require('./logger'),
|
||||
Utils = require('./utils'),
|
||||
config = require('./config');
|
||||
|
||||
var Async = require("async"),
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
// A Robot is the primary interface for interacting with a collection of physical
|
||||
// computing capabilities.
|
||||
var Robot;
|
||||
var missingModuleError = function(module) {
|
||||
var string = "Cannot find the '" + module + "' module. ";
|
||||
string += "Please install it with 'npm install " + module + "' and try again.";
|
||||
|
||||
console.log(string);
|
||||
|
||||
process.emit('SIGINT');
|
||||
};
|
||||
|
||||
// Public: Creates a new Robot
|
||||
//
|
||||
// opts - object containing Robot options
|
||||
// name - optional, string name of the robot
|
||||
// master - Cylon.Master class that orchestrates robots
|
||||
// connection/connections - object connections to connect to
|
||||
// device/devices - object devices to connect to
|
||||
// work - work to be performed when the Robot is started
|
||||
|
@ -43,13 +46,15 @@ var Robot;
|
|||
// name: 'sphero', driver: 'sphero'
|
||||
//
|
||||
// work: (me) ->
|
||||
// every 1.second(), ->
|
||||
// Utils.every 1.second(), ->
|
||||
// me.sphero.roll 60, Math.floor(Math.random() * 360//
|
||||
module.exports = Robot = function Robot(opts) {
|
||||
var Robot = module.exports = function Robot(opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var methods = [
|
||||
"toString",
|
||||
"registerDriver",
|
||||
|
@ -64,42 +69,50 @@ module.exports = Robot = function Robot(opts) {
|
|||
"initConnections"
|
||||
];
|
||||
|
||||
for (var i = 0; i < methods.length ; i++) {
|
||||
var method = methods[i];
|
||||
this[method] = bind(this[method], this);
|
||||
}
|
||||
methods.forEach(function(method) {
|
||||
self[method] = Utils.bind(self[method], self);
|
||||
});
|
||||
|
||||
this.robot = this;
|
||||
this.name = opts.name || this.constructor.randomName();
|
||||
this.master = opts.master;
|
||||
|
||||
this.connections = {};
|
||||
this.devices = {};
|
||||
this.adaptors = {};
|
||||
this.drivers = {};
|
||||
this.commands = [];
|
||||
|
||||
this.running = false;
|
||||
this.work = opts.work || opts.play;
|
||||
|
||||
this.registerAdaptor("./test/loopback", "loopback");
|
||||
this.registerAdaptor("./test/test-adaptor", "test");
|
||||
this.registerDriver("./test/ping", "ping");
|
||||
this.registerDriver("./test/test-driver", "test");
|
||||
if (!this.work) {
|
||||
this.work = function() { console.log("No work yet."); }
|
||||
}
|
||||
|
||||
this.registerDefaults();
|
||||
|
||||
this.initConnections(opts.connection || opts.connections);
|
||||
this.initDevices(opts.device || opts.devices);
|
||||
|
||||
this.work = opts.work || function() { Logger.info("No work yet"); };
|
||||
|
||||
for (var n in opts) {
|
||||
var func = opts[n],
|
||||
reserved = ['connection', 'connections', 'device', 'devices', 'work'];
|
||||
|
||||
if (reserved.indexOf(n) < 0) { this.robot[n] = func; }
|
||||
if (reserved.indexOf(n) < 0) {
|
||||
this[n] = func;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
subclass(Robot, EventEmitter);
|
||||
Utils.subclass(Robot, EventEmitter);
|
||||
|
||||
// Public: Registers the default Drivers and Adaptors with Cylon.
|
||||
//
|
||||
// Returns nothing.
|
||||
Robot.prototype.registerDefaults = function registerDefaults() {
|
||||
this.registerAdaptor("./test/loopback", "loopback");
|
||||
this.registerAdaptor("./test/test-adaptor", "test");
|
||||
|
||||
this.registerDriver("./test/ping", "ping");
|
||||
this.registerDriver("./test/test-driver", "test");
|
||||
};
|
||||
|
||||
// Public: Generates a random name for a Robot.
|
||||
//
|
||||
|
@ -108,15 +121,15 @@ Robot.randomName = function() {
|
|||
return "Robot " + (Math.floor(Math.random() * 100000));
|
||||
};
|
||||
|
||||
// Public: Exports basic data for the Robot
|
||||
// Public: Expresses the Robot in a JSON-serializable format
|
||||
//
|
||||
// Returns an Object containing Robot data
|
||||
Robot.prototype.data = function() {
|
||||
Robot.prototype.toJSON = function() {
|
||||
var connections = (function() {
|
||||
var results = [];
|
||||
for (var n in this.connections) {
|
||||
var conn = this.connections[n];
|
||||
results.push(conn.data());
|
||||
results.push(conn.toJSON());
|
||||
}
|
||||
return results;
|
||||
}).call(this);
|
||||
|
@ -125,7 +138,7 @@ Robot.prototype.data = function() {
|
|||
var results = [];
|
||||
for (var n in this.devices) {
|
||||
var device = this.devices[n];
|
||||
results.push(device.data());
|
||||
results.push(device.toJSON());
|
||||
}
|
||||
return results;
|
||||
}).call(this);
|
||||
|
@ -144,14 +157,17 @@ Robot.prototype.data = function() {
|
|||
//
|
||||
// Returns initialized connections
|
||||
Robot.prototype.initConnections = function(connections) {
|
||||
Logger.info("Initializing connections...");
|
||||
if (connections == null) { return; }
|
||||
Logger.info("Initializing connections.");
|
||||
|
||||
if (connections == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
connections = [].concat(connections);
|
||||
|
||||
for (var i = 0; i < connections.length; i++) {
|
||||
var connection = connections[i];
|
||||
Logger.info("Initializing connection '" + connection.name + "'...");
|
||||
Logger.info("Initializing connection '" + connection.name + "'.");
|
||||
connection['robot'] = this;
|
||||
this.connections[connection.name] = new Connection(connection);
|
||||
}
|
||||
|
@ -165,14 +181,17 @@ Robot.prototype.initConnections = function(connections) {
|
|||
//
|
||||
// Returns initialized devices
|
||||
Robot.prototype.initDevices = function(devices) {
|
||||
Logger.info("Initializing devices...");
|
||||
if (devices == null) { return; }
|
||||
Logger.info("Initializing devices.");
|
||||
|
||||
if (devices == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
devices = [].concat(devices);
|
||||
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
var device = devices[i];
|
||||
Logger.info("Initializing device '" + device.name + "'...");
|
||||
Logger.info("Initializing device '" + device.name + "'.");
|
||||
device['robot'] = this;
|
||||
this.devices[device.name] = this._createDevice(device);
|
||||
}
|
||||
|
@ -191,17 +210,26 @@ Robot.prototype._createDevice = function(device) {
|
|||
// Returns the result of the work
|
||||
Robot.prototype.start = function() {
|
||||
var self = this;
|
||||
return this.startConnections(function() {
|
||||
return self.robot.startDevices(function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}else{
|
||||
self.robot.work.call(self.robot, self.robot);
|
||||
self.running = true;
|
||||
Logger.info("Working...");
|
||||
self.robot.emit('working');
|
||||
}
|
||||
});
|
||||
|
||||
var begin = function(callback) {
|
||||
self.work.call(self, self);
|
||||
self.running = true;
|
||||
self.emit('working');
|
||||
|
||||
Logger.info('Working.');
|
||||
|
||||
callback(null, true);
|
||||
};
|
||||
|
||||
Async.series([
|
||||
self.startConnections,
|
||||
self.startDevices,
|
||||
begin
|
||||
], function(err) {
|
||||
if (!!err) {
|
||||
Logger.fatal("An error occured while trying to start the robot:");
|
||||
Logger.fatal(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -213,11 +241,11 @@ Robot.prototype.start = function() {
|
|||
Robot.prototype.startConnections = function(callback) {
|
||||
var starters = {};
|
||||
|
||||
Logger.info("Starting connections...");
|
||||
Logger.info("Starting connections.");
|
||||
|
||||
for (var n in this.connections) {
|
||||
var connection = this.connections[n];
|
||||
this.robot[n] = connection;
|
||||
this[n] = connection;
|
||||
starters[n] = connection.connect;
|
||||
}
|
||||
|
||||
|
@ -232,11 +260,11 @@ Robot.prototype.startConnections = function(callback) {
|
|||
Robot.prototype.startDevices = function(callback) {
|
||||
var starters = {};
|
||||
|
||||
Logger.info("Starting devices...");
|
||||
Logger.info("Starting devices.");
|
||||
|
||||
for (var n in this.devices) {
|
||||
var device = this.devices[n];
|
||||
this.robot[n] = device;
|
||||
this[n] = device;
|
||||
starters[n] = device.start;
|
||||
}
|
||||
|
||||
|
@ -247,10 +275,23 @@ Robot.prototype.startDevices = function(callback) {
|
|||
//
|
||||
// Halts the devices, disconnects the connections.
|
||||
//
|
||||
// callback - callback to be triggered when the Robot is stopped
|
||||
//
|
||||
// Returns nothing
|
||||
Robot.prototype.halt = function() {
|
||||
for (var d in this.devices) { this.devices[d].halt(); }
|
||||
for (var c in this.connections) { this.connections[c].halt(); }
|
||||
Robot.prototype.halt = function(callback) {
|
||||
var fns = [];
|
||||
|
||||
for (var d in this.devices) {
|
||||
var device = this.devices[d];
|
||||
fns.push(device.halt.bind(device));
|
||||
}
|
||||
|
||||
for (var c in this.connections) {
|
||||
var connection = this.connections[c];
|
||||
fns.push(connection.halt.bind(connection));
|
||||
}
|
||||
|
||||
Async.parallel(fns, callback);
|
||||
};
|
||||
|
||||
// Public: Initialize an adaptor and adds it to @robot.adaptors
|
||||
|
@ -260,21 +301,23 @@ Robot.prototype.halt = function() {
|
|||
//
|
||||
// Returns the adaptor
|
||||
Robot.prototype.initAdaptor = function(adaptorName, connection, opts) {
|
||||
if (opts == null) { opts = {}; }
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
var adaptor = this.robot.requireAdaptor(adaptorName, opts).adaptor({
|
||||
var adaptor = this.requireAdaptor(adaptorName, opts).adaptor({
|
||||
name: adaptorName,
|
||||
connection: connection,
|
||||
extraParams: opts
|
||||
});
|
||||
|
||||
if (config.testing_mode) {
|
||||
var testAdaptor = this.robot.requireAdaptor('test').adaptor({
|
||||
var testAdaptor = this.requireAdaptor('test').adaptor({
|
||||
name: adaptorName,
|
||||
connection: connection,
|
||||
extraParams: opts
|
||||
});
|
||||
return proxyTestStubs(adaptor.commands(), testAdaptor);
|
||||
return Utils.proxyTestStubs(adaptor.commands, testAdaptor);
|
||||
} else {
|
||||
return adaptor;
|
||||
}
|
||||
|
@ -286,12 +329,12 @@ Robot.prototype.initAdaptor = function(adaptorName, connection, opts) {
|
|||
//
|
||||
// Returns the module for the adaptor
|
||||
Robot.prototype.requireAdaptor = function(adaptorName, opts) {
|
||||
if (this.robot.adaptors[adaptorName] == null) {
|
||||
if (this.adaptors[adaptorName] == null) {
|
||||
var moduleName = opts.module || adaptorName;
|
||||
this.robot.registerAdaptor("cylon-" + moduleName, adaptorName);
|
||||
this.robot.adaptors[adaptorName].register(this);
|
||||
this.registerAdaptor("cylon-" + moduleName, adaptorName);
|
||||
this.adaptors[adaptorName].register(this);
|
||||
}
|
||||
return this.robot.adaptors[adaptorName];
|
||||
return this.adaptors[adaptorName];
|
||||
};
|
||||
|
||||
// Public: Registers an Adaptor with the Robot
|
||||
|
@ -322,22 +365,24 @@ Robot.prototype.registerAdaptor = function(moduleName, adaptorName) {
|
|||
//
|
||||
// Returns the new driver
|
||||
Robot.prototype.initDriver = function(driverName, device, opts) {
|
||||
if (opts == null) { opts = {}; }
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
var driver = this.robot.requireDriver(driverName).driver({
|
||||
var driver = this.requireDriver(driverName).driver({
|
||||
name: driverName,
|
||||
device: device,
|
||||
extraParams: opts
|
||||
});
|
||||
|
||||
if (config.testing_mode) {
|
||||
var testDriver = this.robot.requireDriver('test').driver({
|
||||
var testDriver = this.requireDriver('test').driver({
|
||||
name: driverName,
|
||||
device: device,
|
||||
extraParams: opts
|
||||
});
|
||||
|
||||
return proxyTestStubs(driver.commands(), testDriver);
|
||||
return Utils.proxyTestStubs(driver.commands, testDriver);
|
||||
} else {
|
||||
return driver;
|
||||
}
|
||||
|
@ -349,11 +394,11 @@ Robot.prototype.initDriver = function(driverName, device, opts) {
|
|||
//
|
||||
// Returns the module for driver
|
||||
Robot.prototype.requireDriver = function(driverName) {
|
||||
if (this.robot.drivers[driverName] == null) {
|
||||
this.robot.registerDriver("cylon-" + driverName, driverName);
|
||||
this.robot.drivers[driverName].register(this);
|
||||
if (this.drivers[driverName] == null) {
|
||||
this.registerDriver("cylon-" + driverName, driverName);
|
||||
this.drivers[driverName].register(this);
|
||||
}
|
||||
return this.robot.drivers[driverName];
|
||||
return this.drivers[driverName];
|
||||
};
|
||||
|
||||
// Public: Registers an Driver with the Robot
|
||||
|
@ -382,12 +427,3 @@ Robot.prototype.registerDriver = function(moduleName, driverName) {
|
|||
Robot.prototype.toString = function() {
|
||||
return "[Robot name='" + this.name + "']";
|
||||
};
|
||||
|
||||
var missingModuleError = function(module) {
|
||||
var string = "Cannot find the '" + module + "' module. ";
|
||||
string += "Please install it with 'npm install " + module + "' and try again.";
|
||||
|
||||
console.log(string);
|
||||
|
||||
process.emit('SIGINT');
|
||||
};
|
||||
|
|
|
@ -8,18 +8,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var Adaptor = require('../adaptor');
|
||||
var Adaptor = require('../adaptor'),
|
||||
Utils = require('../utils');
|
||||
|
||||
var Loopback;
|
||||
|
||||
module.exports = Loopback = function Loopback() {
|
||||
Loopback.__super__.constructor.apply(this, arguments);
|
||||
this.commands = ['ping'];
|
||||
};
|
||||
|
||||
subclass(Loopback, Adaptor);
|
||||
|
||||
Loopback.prototype.commands = function() {
|
||||
return ['ping'];
|
||||
};
|
||||
Utils.subclass(Loopback, Adaptor);
|
||||
|
||||
Loopback.adaptor = function(opts) { return new Loopback(opts); };
|
||||
|
|
|
@ -8,19 +8,17 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var Driver = require('../driver');
|
||||
var Driver = require('../driver'),
|
||||
Utils = require('../utils');
|
||||
|
||||
var Ping;
|
||||
|
||||
module.exports = Ping = function Ping() {
|
||||
Ping.__super__.constructor.apply(this, arguments);
|
||||
this.commands = ['ping'];
|
||||
};
|
||||
|
||||
subclass(Ping, Driver);
|
||||
|
||||
Ping.prototype.commands = function() {
|
||||
return ['ping'];
|
||||
};
|
||||
Utils.subclass(Ping, Driver);
|
||||
|
||||
Ping.prototype.ping = function() {
|
||||
return "pong";
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var Adaptor = require('../adaptor')
|
||||
var Adaptor = require('../adaptor'),
|
||||
Utils = require('../utils');
|
||||
|
||||
var TestAdaptor;
|
||||
|
||||
|
@ -16,6 +17,6 @@ module.exports = TestAdaptor = function TestAdaptor() {
|
|||
TestAdaptor.__super__.constructor.apply(this, arguments);
|
||||
};
|
||||
|
||||
subclass(TestAdaptor, Adaptor);
|
||||
Utils.subclass(TestAdaptor, Adaptor);
|
||||
|
||||
TestAdaptor.adaptor = function(opts) { return new TestAdaptor(opts); };
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var Driver = require('../driver');
|
||||
var Driver = require('../driver'),
|
||||
Utils = require('../utils');
|
||||
|
||||
var TestDriver;
|
||||
|
||||
|
@ -16,6 +17,6 @@ module.exports = TestDriver = function TestDriver() {
|
|||
TestDriver.__super__.constructor.apply(this, arguments);
|
||||
};
|
||||
|
||||
subclass(TestDriver, Driver);
|
||||
Utils.subclass(TestDriver, Driver);
|
||||
|
||||
TestDriver.driver = function(opts) { return new TestDriver(opts); };
|
||||
|
|
254
lib/utils.js
254
lib/utils.js
|
@ -1,82 +1,12 @@
|
|||
/*
|
||||
* utils
|
||||
* Cylon - Utils
|
||||
* cylonjs.com
|
||||
*
|
||||
* Copyright (c) 2013 The Hybrid Group
|
||||
* Licensed under the Apache 2.0 license.
|
||||
*/
|
||||
|
||||
// Public: Monkey-patches Number to have Rails-like //seconds() function. Warning,
|
||||
// due to the way the Javascript parser works, applying functions on numbers is
|
||||
// kind of weird. See examples for details.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 2.seconds()
|
||||
// //=> SyntaxError: Unexpected token ILLEGAL
|
||||
//
|
||||
// 10..seconds()
|
||||
// //=> 10000
|
||||
//
|
||||
// (5).seconds()
|
||||
// //=> 5000
|
||||
//
|
||||
// Returns an integer representing time in milliseconds
|
||||
Number.prototype.seconds = function() {
|
||||
return this * 1000;
|
||||
};
|
||||
|
||||
// Public: Alias for Number::seconds, see comments for that method
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 1.second()
|
||||
// //=> 1000
|
||||
//
|
||||
// Returns an integer representing time in milliseconds
|
||||
Number.prototype.second = function() {
|
||||
return this.seconds(this);
|
||||
};
|
||||
|
||||
// Public: Convert value from old scale (start, end) to (0..1) scale
|
||||
//
|
||||
// start - low point of scale to convert value from
|
||||
// end - high point of scale to convert value from
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 5..fromScale(0, 10)
|
||||
// //=> 0.5
|
||||
//
|
||||
// Returns an integer representing the scaled value
|
||||
Number.prototype.fromScale = function(start, end) {
|
||||
return (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end));
|
||||
};
|
||||
|
||||
// Public: Convert value from (0..1) scale to new (start, end) scale
|
||||
//
|
||||
// start - low point of scale to convert value to
|
||||
// end - high point of scale to convert value to
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 0.5.toScale(0, 10)
|
||||
// //=> 5
|
||||
//
|
||||
// Returns an integer representing the scaled value
|
||||
Number.prototype.toScale = function(start, end) {
|
||||
var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end);
|
||||
|
||||
if (i < Math.min(start, end)) {
|
||||
return Math.min(start, end);
|
||||
} else if (i > Math.max(start,end)){
|
||||
return Math.max(start, end);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
global.Utils = {
|
||||
var Utils = module.exports = {
|
||||
// Public: Alias to setInterval, combined with Number monkeypatches below to
|
||||
// create an artoo-like syntax.
|
||||
//
|
||||
|
@ -85,7 +15,9 @@ global.Utils = {
|
|||
//
|
||||
// Examples
|
||||
//
|
||||
// every 5.seconds(), -> console.log("hello world (and again in 5 seconds)!")
|
||||
// every((5).seconds(), function() {
|
||||
// console.log('Hello world (and again in 5 seconds)!');
|
||||
// });
|
||||
//
|
||||
// Returns an interval
|
||||
every: function every(interval, action) {
|
||||
|
@ -100,7 +32,9 @@ global.Utils = {
|
|||
//
|
||||
// Examples
|
||||
//
|
||||
// after 10.seconds(), -> console.log("hello world from ten seconds ago!")
|
||||
// after((10).seconds(), function() {
|
||||
// console.log('Hello world from ten seconds ago!');
|
||||
// });
|
||||
//
|
||||
// Returns an interval
|
||||
after: function after(delay, action) {
|
||||
|
@ -110,7 +44,9 @@ global.Utils = {
|
|||
// Public: Alias to the `every` function, but passing 0
|
||||
// Examples
|
||||
//
|
||||
// constantly -> console.log("hello world (and again and again)!")
|
||||
// constantly(function() {
|
||||
// console.log('hello world (and again and again)!');
|
||||
// });
|
||||
//
|
||||
// Returns an interval
|
||||
constantly: function constantly(action) {
|
||||
|
@ -121,9 +57,11 @@ global.Utils = {
|
|||
//
|
||||
// ms - number of ms to sleep for
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// sleep((1).second());
|
||||
//
|
||||
// Returns a function
|
||||
// Examples:
|
||||
// sleep 1.second()
|
||||
sleep: function sleep(ms) {
|
||||
var start = Date.now();
|
||||
|
||||
|
@ -132,27 +70,28 @@ global.Utils = {
|
|||
}
|
||||
},
|
||||
|
||||
// Copies
|
||||
slice: [].slice,
|
||||
hasProp: {}.hasOwnProperty,
|
||||
|
||||
// Public: Function to use for class inheritance. Copy of a CoffeeScript helper
|
||||
// function.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// var Sphero = (function(klass) {
|
||||
// subclass(Sphero, klass);
|
||||
// // Sphero is now a subclass of Parent, and can access it's methods through
|
||||
// // Sphero.__super__
|
||||
// })(Parent);
|
||||
// var Sphero = function Sphero() {};
|
||||
//
|
||||
// subclass(Sphero, ParentClass);
|
||||
//
|
||||
// // Sphero is now a subclass of Parent, and can access parent methods
|
||||
// // through Sphero.__super__
|
||||
//
|
||||
// Returns subclass
|
||||
subclass: function subclass(child, parent) {
|
||||
var ctor = function() { this.constructor = child; };
|
||||
var ctor = function() {
|
||||
this.constructor = child;
|
||||
};
|
||||
|
||||
for (var key in parent) {
|
||||
if (hasProp.call(parent, key)) { child[key] = parent[key]; }
|
||||
if (Object.hasOwnProperty.call(parent, key)) {
|
||||
child[key] = parent[key];
|
||||
}
|
||||
}
|
||||
|
||||
ctor.prototype = parent.prototype;
|
||||
|
@ -172,8 +111,13 @@ global.Utils = {
|
|||
//
|
||||
// Returns base
|
||||
proxyFunctionsToObject: function proxyFunctionsToObject(methods, target, base, force) {
|
||||
if (base == null) { base = this; }
|
||||
if (force == null) { force = false; }
|
||||
if (base == null) {
|
||||
base = this;
|
||||
}
|
||||
|
||||
if (force == null) {
|
||||
force = false;
|
||||
}
|
||||
|
||||
var fn = function(method) {
|
||||
return base[method] = function() {
|
||||
|
@ -184,8 +128,9 @@ global.Utils = {
|
|||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
var method = methods[i];
|
||||
if (!force) {
|
||||
if (typeof base[method] === 'function') { continue; }
|
||||
|
||||
if (!force && typeof(base[method]) === 'function') {
|
||||
continue;
|
||||
}
|
||||
|
||||
fn(method);
|
||||
|
@ -201,11 +146,16 @@ global.Utils = {
|
|||
//
|
||||
// Returns base
|
||||
proxyTestStubs: function proxyTestStubs(methods, base) {
|
||||
if (base == null) { base = this; }
|
||||
if (base == null) {
|
||||
base = this;
|
||||
}
|
||||
|
||||
methods.forEach(function(method) {
|
||||
base[method] = function() { return true; };
|
||||
base.commandList.push(method);
|
||||
base[method] = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
base.commands.push(method);
|
||||
});
|
||||
|
||||
return base;
|
||||
|
@ -220,34 +170,124 @@ global.Utils = {
|
|||
//
|
||||
// var me = { hello: "Hello World" };
|
||||
// var proxy = { boundMethod: function() { return this.hello; } };
|
||||
//
|
||||
// proxy.boundMethod = bind(proxy.boundMethod, me);
|
||||
// proxy.boundMethod();
|
||||
//
|
||||
// //=> "Hello World"
|
||||
//
|
||||
// Returns a function wrapper
|
||||
bind: function bind(fn, me) {
|
||||
return function() { return fn.apply(me, arguments); };
|
||||
return function() {
|
||||
return fn.apply(me, arguments);
|
||||
};
|
||||
},
|
||||
|
||||
// Public: Adds all methods from Cylon.Utils directly to the global
|
||||
// namespace.
|
||||
// Public: Adds necessary utils to global namespace, along with base class
|
||||
// extensions.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// Cylon.Utils.bootstrap();
|
||||
// (after === Cylon.Utils.After) // true
|
||||
// Number.prototype.seconds // undefined
|
||||
// after // undefined
|
||||
//
|
||||
// Utils.bootstrap();
|
||||
//
|
||||
// Number.prototype.seconds // [function]
|
||||
// (after === Utils.after) // true
|
||||
//
|
||||
// Returns Cylon.Utils
|
||||
bootstrap: function bootstrap() {
|
||||
for (util in this) {
|
||||
// we're not going to attach the 'bootstrap' method
|
||||
if (!(util === "bootstrap")) {
|
||||
global[util] = this[util];
|
||||
}
|
||||
}
|
||||
global.every = this.every;
|
||||
global.after = this.after;
|
||||
global.constantly = this.constantly;
|
||||
|
||||
addCoreExtensions();
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Utils.bootstrap();
|
||||
var addCoreExtensions = function addCoreExtensions() {
|
||||
// Public: Monkey-patches Number to have Rails-like //seconds() function.
|
||||
// Warning, due to the way the Javascript parser works, applying functions on
|
||||
// numbers is kind of weird. See examples for details.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 2.seconds()
|
||||
// //=> SyntaxError: Unexpected token ILLEGAL
|
||||
//
|
||||
// 10..seconds()
|
||||
// //=> 10000
|
||||
//
|
||||
// (5).seconds()
|
||||
// //=> 5000
|
||||
// // This is the preferred way to represent numbers when calling these
|
||||
// // methods on them
|
||||
//
|
||||
// Returns an integer representing time in milliseconds
|
||||
Number.prototype.seconds = function() {
|
||||
return this * 1000;
|
||||
};
|
||||
|
||||
// Public: Alias for Number::seconds, see comments for that method
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 1.second()
|
||||
// //=> 1000
|
||||
//
|
||||
// Returns an integer representing time in milliseconds
|
||||
Number.prototype.second = function() {
|
||||
return this.seconds(this);
|
||||
};
|
||||
|
||||
// Public: Convert value from old scale (start, end) to (0..1) scale
|
||||
//
|
||||
// start - low point of scale to convert value from
|
||||
// end - high point of scale to convert value from
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// (5).fromScale(0, 10)
|
||||
// //=> 0.5
|
||||
//
|
||||
// Returns an integer representing the scaled value
|
||||
Number.prototype.fromScale = function(start, end) {
|
||||
var val = (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end));
|
||||
|
||||
if (val > 1) {
|
||||
val = 1;
|
||||
} else if (val < 0){
|
||||
val = 0;
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
// Public: Convert value from (0..1) scale to new (start, end) scale
|
||||
//
|
||||
// start - low point of scale to convert value to
|
||||
// end - high point of scale to convert value to
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// (0.5).toScale(0, 10)
|
||||
// //=> 5
|
||||
//
|
||||
// Returns an integer representing the scaled value
|
||||
Number.prototype.toScale = function(start, end) {
|
||||
var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end);
|
||||
|
||||
if (i < Math.min(start, end)) {
|
||||
return Math.min(start, end);
|
||||
} else if (i > Math.max(start,end)){
|
||||
return Math.max(start, end);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Utils.bootstrap();
|
||||
|
|
13
package.json
13
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cylon",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.1",
|
||||
"main": "lib/cylon.js",
|
||||
"description": "A JavaScript robotics framework using Node.js",
|
||||
"homepage": "http://cylonjs.com",
|
||||
|
@ -39,14 +39,13 @@
|
|||
"sinon-chai": "2.5.0",
|
||||
"chai": "1.9.1",
|
||||
"mocha": "1.18.2",
|
||||
"sinon": "1.9.1",
|
||||
"jshint": "2.5.0",
|
||||
"istanbul": "0.2.10"
|
||||
"sinon": "1.9.1"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"async": "0.7.0",
|
||||
"express": "3.5.1",
|
||||
"robeaux": ">= 0.1.0"
|
||||
"async": "0.7.0",
|
||||
"express": "4.4.1",
|
||||
"body-parser": "1.3.0",
|
||||
"robeaux": ">= 0.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Adaptor = source("adaptor"),
|
||||
Logger = source('logger');
|
||||
Logger = source('logger'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("Adaptor", function() {
|
||||
var connection = new EventEmitter;
|
||||
|
@ -22,23 +23,8 @@ describe("Adaptor", function() {
|
|||
expect(adaptor.connection).to.be.eql(connection);
|
||||
});
|
||||
|
||||
it("sets @commandList to an empty array by default", function() {
|
||||
expect(adaptor.commandList).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#commands", function() {
|
||||
var commands = ['list', 'of', 'commands']
|
||||
before(function() {
|
||||
adaptor.commandList = commands;
|
||||
});
|
||||
|
||||
after(function() {
|
||||
adaptor.commandList = [];
|
||||
});
|
||||
|
||||
it("returns the adaptor's @commandList", function() {
|
||||
expect(adaptor.commands()).to.be.eql(commands);
|
||||
it("sets @commands to an empty array by default", function() {
|
||||
expect(adaptor.commands).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -46,34 +32,31 @@ describe("Adaptor", function() {
|
|||
var callback = spy();
|
||||
|
||||
before(function() {
|
||||
stub(connection, 'emit');
|
||||
stub(Logger, 'info');
|
||||
adaptor.connect(callback);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
connection.emit.restore();
|
||||
Logger.info.restore();
|
||||
});
|
||||
|
||||
it("logs that it's connecting to the adaptor", function() {
|
||||
var string = "Connecting to adaptor 'adaptor'...";
|
||||
var string = "Connecting to adaptor 'adaptor'.";
|
||||
expect(Logger.info).to.be.calledWith(string);
|
||||
});
|
||||
|
||||
it("triggers the provided callback", function() {
|
||||
expect(callback).to.be.called;
|
||||
});
|
||||
|
||||
it("tells the connection to emit the 'connect' event", function() {
|
||||
expect(connection.emit).to.be.calledWith('connect');
|
||||
});
|
||||
});
|
||||
|
||||
describe("#disconnect", function() {
|
||||
var callback;
|
||||
|
||||
before(function() {
|
||||
stub(Logger, 'info');
|
||||
adaptor.disconnect();
|
||||
callback = spy();
|
||||
adaptor.disconnect(callback);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
|
@ -81,8 +64,12 @@ describe("Adaptor", function() {
|
|||
});
|
||||
|
||||
it("logs that it's disconnecting to the adaptor", function() {
|
||||
var string = "Disconnecting from adaptor 'adaptor'...";
|
||||
var string = "Disconnecting from adaptor 'adaptor'.";
|
||||
expect(Logger.info).to.be.calledWith(string);
|
||||
});
|
||||
|
||||
it("triggers the callback", function() {
|
||||
expect(callback).to.be.called;
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,8 @@ var express = require('express'),
|
|||
https = require('https'),
|
||||
fs = require('fs');
|
||||
|
||||
var API = source('api');
|
||||
var API = source('api'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("API", function() {
|
||||
var api, opts;
|
||||
|
@ -14,8 +15,6 @@ describe("API", function() {
|
|||
beforeEach(function() {
|
||||
stub(https, 'createServer').returns({ listen: spy() });
|
||||
|
||||
opts = { master: { name: 'master' }, ssl: {} }
|
||||
|
||||
api = new API(opts);
|
||||
});
|
||||
|
||||
|
@ -35,17 +34,13 @@ describe("API", function() {
|
|||
expect(api.port).to.be.eql("3000")
|
||||
});
|
||||
|
||||
it("sets @master to the passed master", function() {
|
||||
expect(api.master).to.be.eql(opts.master)
|
||||
});
|
||||
|
||||
it("sets @server to an Express server instance", function() {
|
||||
expect(api.server).to.be.a('function');
|
||||
it("sets @express to an Express server instance", function() {
|
||||
expect(api.express).to.be.a('function');
|
||||
|
||||
var methods = ['get', 'post', 'put', 'delete'];
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
expect(api.server[methods[i]]).to.be.a('function');
|
||||
expect(api.express[methods[i]]).to.be.a('function');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -54,7 +49,7 @@ describe("API", function() {
|
|||
});
|
||||
|
||||
it("sets the server's title", function() {
|
||||
var title = api.server.get('title');
|
||||
var title = api.express.get('title');
|
||||
expect(title).to.be.eql("Cylon API Server");
|
||||
});
|
||||
|
||||
|
@ -80,72 +75,4 @@ describe("API", function() {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#configureRoutes", function() {
|
||||
var server;
|
||||
var methods = ['all', 'get', 'post'];
|
||||
|
||||
beforeEach(function() {
|
||||
server = api.server;
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
stub(server, methods[i]);
|
||||
}
|
||||
|
||||
api.configureRoutes();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
server[methods[i]].restore();
|
||||
}
|
||||
});
|
||||
|
||||
it("ALL /*", function() {
|
||||
expect(server.all).to.be.calledWith("/*");
|
||||
});
|
||||
|
||||
it("GET /robots", function() {
|
||||
expect(server.get).to.be.calledWith("/robots");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/commands", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/commands");
|
||||
});
|
||||
|
||||
it("ALL /robots/:robot/commands/:command", function() {
|
||||
expect(server.all).to.be.calledWith("/robots/:robot/commands/:command");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/devices", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/devices");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/devices/:device", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/devices/:device");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/devices/:device/events/:event", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/devices/:device/events/:event");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/devices/:device/commands", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/devices/:device/commands");
|
||||
});
|
||||
|
||||
it("ALL /robots/:robot/devices/:device/commands/:command", function() {
|
||||
expect(server.all).to.be.calledWith("/robots/:robot/devices/:device/commands/:command");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/connections", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/connections");
|
||||
});
|
||||
|
||||
it("GET /robots/:robot/connections/:connection", function() {
|
||||
expect(server.get).to.be.calledWith("/robots/:robot/connections/:connection");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
'use strict'
|
||||
|
||||
var basic = source('api/auth/basic');
|
||||
|
||||
var MockRequest = require('../../../support/mock_request'),
|
||||
MockResponse = require('../../../support/mock_response');
|
||||
|
||||
describe("Basic Auth", function() {
|
||||
var opts = { user: 'user', pass: 'pass' },
|
||||
req,
|
||||
res,
|
||||
next;
|
||||
|
||||
basic = basic(opts);
|
||||
|
||||
beforeEach(function() {
|
||||
req = new MockRequest();
|
||||
res = new MockResponse();
|
||||
next = spy();
|
||||
|
||||
var auth = new Buffer("user:pass", "utf8").toString('base64');
|
||||
req.headers = { authorization: "Basic " + auth };
|
||||
});
|
||||
|
||||
var checkUnauthorized = function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
result = basic(req, res, next);
|
||||
});
|
||||
|
||||
it("returns false", function() {
|
||||
expect(result).to.be.falsy;
|
||||
});
|
||||
|
||||
it("sends a 401 error", function() {
|
||||
expect(res.statusCode).to.be.eql(401);
|
||||
expect(res.end).to.be.calledWith("Unauthorized");
|
||||
});
|
||||
};
|
||||
|
||||
var checkError = function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
result = basic(req, res, next);
|
||||
});
|
||||
|
||||
it("triggers next with an error", function() {
|
||||
expect(next).to.be.called;
|
||||
});
|
||||
};
|
||||
|
||||
context("with a valid request", function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
result = basic(req, res, next);
|
||||
});
|
||||
|
||||
it("returns true", function() {
|
||||
expect(result).to.be.truthy;
|
||||
});
|
||||
|
||||
it("doesn't modify the response", function() {
|
||||
expect(res.end).to.not.be.called;
|
||||
})
|
||||
});
|
||||
|
||||
context("if the user/pass don't match", function() {
|
||||
beforeEach(function() {
|
||||
var auth = new Buffer("bad:wrong", "utf8").toString('base64');
|
||||
req.headers = { authorization: "Basic " + auth };
|
||||
});
|
||||
|
||||
checkUnauthorized();
|
||||
});
|
||||
|
||||
context("if there is already an authorized user", function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
req.user = 'user';
|
||||
result = basic(req, res, next);
|
||||
});
|
||||
|
||||
it("returns true", function() {
|
||||
expect(result).to.be.truthy;
|
||||
});
|
||||
|
||||
it("doesn't modify the response", function() {
|
||||
expect(res.end).to.not.be.called;
|
||||
})
|
||||
});
|
||||
|
||||
context("if there are no authorization headers", function() {
|
||||
beforeEach(function() {
|
||||
delete req.headers.authorization;
|
||||
});
|
||||
|
||||
checkUnauthorized();
|
||||
});
|
||||
|
||||
context("the authorization type isn't Basic", function() {
|
||||
beforeEach(function() {
|
||||
var auth = new Buffer("user:pass", "utf8").toString('base64');
|
||||
req.headers = { authorization: "Digest " + auth };
|
||||
});
|
||||
|
||||
checkError();
|
||||
});
|
||||
|
||||
context("the authorization header is missing content", function() {
|
||||
beforeEach(function() {
|
||||
req.headers = { authorization: "Basic" };
|
||||
});
|
||||
|
||||
checkError();
|
||||
});
|
||||
|
||||
context("if the authorization header isn't formatted correctly", function() {
|
||||
beforeEach(function() {
|
||||
var auth = new Buffer("user-pass", "utf8").toString('base64');
|
||||
req.headers = { authorization: "Basic " + auth };
|
||||
});
|
||||
|
||||
checkUnauthorized();
|
||||
});
|
||||
});
|
|
@ -1,17 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
var Basestar = source('basestar');
|
||||
var Basestar = source('basestar'),
|
||||
Utils = source('utils');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
describe('Basestar', function() {
|
||||
describe('constructor', function() {
|
||||
it('assigns @self to the instance of the Basestar class', function() {
|
||||
var instance = new Basestar();
|
||||
expect(instance.self).to.be.eql(instance);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#proxyMethods', function() {
|
||||
var methods = ['asString', 'toString', 'returnString'];
|
||||
|
||||
|
@ -34,7 +28,7 @@ describe('Basestar', function() {
|
|||
this.proxyMethods(methods, this.testInstance, this, true);
|
||||
}
|
||||
|
||||
subclass(TestClass, Basestar);
|
||||
Utils.subclass(TestClass, Basestar);
|
||||
|
||||
it('can alias methods', function() {
|
||||
var testclass = new TestClass;
|
||||
|
@ -70,8 +64,8 @@ describe('Basestar', function() {
|
|||
});
|
||||
}
|
||||
|
||||
subclass(ProxyClass, Basestar);
|
||||
subclass(EmitterClass, Basestar);
|
||||
Utils.subclass(ProxyClass, Basestar);
|
||||
Utils.subclass(EmitterClass, Basestar);
|
||||
|
||||
it("proxies events from one class to another", function() {
|
||||
var eventSpy = spy(),
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
var Robot = source("robot"),
|
||||
Logger = source('logger');
|
||||
Logger = source('logger'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("Cylon.Connection", function() {
|
||||
describe("Connection", function() {
|
||||
var robot = new Robot({
|
||||
name: "Robby",
|
||||
connection: { name: 'loopback', adaptor: 'loopback', port: "/dev/null" }
|
||||
|
@ -29,28 +30,28 @@ describe("Cylon.Connection", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#data", function() {
|
||||
var data = connection.data();
|
||||
describe("#toJSON", function() {
|
||||
var json = connection.toJSON();
|
||||
|
||||
it("returns an object", function() {
|
||||
expect(data).to.be.an('object');
|
||||
expect(json).to.be.an('object');
|
||||
});
|
||||
|
||||
it("contains the connection's name", function() {
|
||||
expect(data.name).to.be.eql("loopback");
|
||||
expect(json.name).to.be.eql("loopback");
|
||||
});
|
||||
|
||||
it("contains the connection's port", function() {
|
||||
expect(data.port).to.be.eql("/dev/null");
|
||||
expect(json.port).to.be.eql("/dev/null");
|
||||
});
|
||||
|
||||
it("contains the connection's adaptor name", function() {
|
||||
expect(data.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(data.connection_id).to.be.eql(id);
|
||||
expect(json.connection_id).to.be.eql(id);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -70,7 +71,7 @@ describe("Cylon.Connection", function() {
|
|||
});
|
||||
|
||||
it("logs that it's connecting the device", function() {
|
||||
var message = "Connecting to loopback on port /dev/null";
|
||||
var message = "Connecting to 'loopback' on port /dev/null.";
|
||||
expect(Logger.info).to.be.calledWith(message);
|
||||
});
|
||||
|
||||
|
@ -93,7 +94,7 @@ describe("Cylon.Connection", function() {
|
|||
});
|
||||
|
||||
it("logs that it's disconnecting from the device", function() {
|
||||
var message = "Disconnecting from loopback on port /dev/null";
|
||||
var message = "Disconnecting from 'loopback' on port /dev/null.";
|
||||
expect(Logger.info).to.be.calledWith(message);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
var Cylon = source("cylon");
|
||||
var Cylon = source("cylon"),
|
||||
Utils = source('utils');
|
||||
|
||||
var Logger = source('logger'),
|
||||
var API = source('api'),
|
||||
Logger = source('logger'),
|
||||
Adaptor = source('adaptor'),
|
||||
Driver = source('driver');
|
||||
|
||||
|
@ -25,21 +27,13 @@ describe("Cylon", function() {
|
|||
expect(Cylon.api_instance).to.be.eql(null);
|
||||
});
|
||||
|
||||
it("sets @api_config to an object containing host/port info", function() {
|
||||
var config = Cylon.api_config;
|
||||
|
||||
expect(config).to.be.an('object');
|
||||
expect(config.host).to.be.eql('127.0.0.1');
|
||||
expect(config.port).to.be.eql('3000');
|
||||
});
|
||||
|
||||
it("sets @robots to an empty array by default", function() {
|
||||
expect(Cylon.robots).to.be.eql([]);
|
||||
it("sets @robots to an empty object by default", function() {
|
||||
expect(Cylon.robots).to.be.eql({});
|
||||
});
|
||||
|
||||
describe("#robot", function() {
|
||||
after(function() {
|
||||
Cylon.robots = [];
|
||||
Cylon.robots = {};
|
||||
});
|
||||
|
||||
it("uses passed options to create a new Robot", function() {
|
||||
|
@ -47,284 +41,39 @@ describe("Cylon", function() {
|
|||
var robot = Cylon.robot(opts);
|
||||
|
||||
expect(robot.toString()).to.be.eql("[Robot name='Ultron']")
|
||||
expect(Cylon.robots.pop()).to.be.eql(robot);
|
||||
expect(Cylon.robots['Ultron']).to.be.eql(robot);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#api", function() {
|
||||
var expectedConfig;
|
||||
|
||||
beforeEach(function() {
|
||||
expectedConfig = {
|
||||
host: '127.0.0.1',
|
||||
port: '3000',
|
||||
auth: {},
|
||||
CORS: null,
|
||||
ssl: {}
|
||||
};
|
||||
stub(API.prototype, 'listen');
|
||||
});
|
||||
|
||||
// this is the shortest, cheapest way to dup an object in JS.
|
||||
// I don't like it either.
|
||||
Cylon.api_config = JSON.parse(JSON.stringify(expectedConfig));
|
||||
afterEach(function() {
|
||||
API.prototype.listen.restore();
|
||||
});
|
||||
|
||||
it('creates a new API instance', function() {
|
||||
Cylon.api();
|
||||
expect(Cylon.api_instance).to.be.an.instanceOf(API);
|
||||
});
|
||||
|
||||
it('passes arguments to the API constructor', function() {
|
||||
Cylon.api({ port: '1234' });
|
||||
expect(Cylon.api_instance.port).to.be.eql('1234');
|
||||
})
|
||||
|
||||
context("without arguments", function() {
|
||||
it("returns the current API configuration", function() {
|
||||
Cylon.api();
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
context("only specifying port", function() {
|
||||
it("changes the port, but not the host", function() {
|
||||
expectedConfig.port = "4000";
|
||||
|
||||
Cylon.api({ port: "4000" });
|
||||
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
context("only specifying host", function() {
|
||||
it("changes the host, but not the port", function() {
|
||||
expectedConfig.host = "0.0.0.0";
|
||||
Cylon.api({ host: "0.0.0.0" });
|
||||
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
context("specifying new host and port", function() {
|
||||
it("changes both the host and port", function() {
|
||||
expectedConfig.host = "0.0.0.0";
|
||||
expectedConfig.port = "4000";
|
||||
|
||||
Cylon.api({ host: "0.0.0.0", port: "4000" });
|
||||
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
context("specifiying SSL key and cert", function() {
|
||||
it("changes the SSL key and cert", function() {
|
||||
expectedConfig.ssl.cert = "/path/to/cert/file";
|
||||
expectedConfig.ssl.key = "/path/to/key/file";
|
||||
|
||||
Cylon.api({
|
||||
ssl: {
|
||||
cert: "/path/to/cert/file",
|
||||
key: "/path/to/key/file"
|
||||
}
|
||||
});
|
||||
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
context("specifying an auth strategy", function() {
|
||||
it("changes the auth strategy", function() {
|
||||
var auth = { type: 'basic', user: 'user', pass: 'pass'}
|
||||
expectedConfig.auth = auth;
|
||||
Cylon.api({ auth: auth })
|
||||
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
context("specifying CORS restrictions", function() {
|
||||
it("changes the CORS restrictions", function() {
|
||||
var CORS = "https://localhost:4000";
|
||||
expectedConfig.CORS = CORS;
|
||||
Cylon.api({ CORS: CORS })
|
||||
|
||||
expect(Cylon.api_config).to.be.eql(expectedConfig);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#findRobot", function() {
|
||||
var bot;
|
||||
|
||||
before(function() {
|
||||
bot = Cylon.robot({ name: "Robby" })
|
||||
});
|
||||
|
||||
describe("async", function() {
|
||||
context("looking for a robot that exists", function() {
|
||||
it("calls the callback with the robot", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobot("Robby", callback);
|
||||
expect(callback).to.be.calledWith(undefined, bot);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for a robot that does not exist", function(){
|
||||
it("calls the callback with no robot and an error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobot("Ultron", callback);
|
||||
var error = { error: "No Robot found with the name Ultron" };
|
||||
expect(callback).to.be.calledWith(error, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("sync", function() {
|
||||
context("looking for a robot that exists", function() {
|
||||
it("returns the robot", function() {
|
||||
expect(Cylon.findRobot("Robby")).to.be.eql(bot);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for a robot that does not exist", function(){
|
||||
it("returns null", function() {
|
||||
expect(Cylon.findRobot("Ultron")).to.be.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#findRobotDevice", function() {
|
||||
var bot, device;
|
||||
|
||||
before(function() {
|
||||
bot = Cylon.robot({
|
||||
name: "Ultron",
|
||||
device: { name: "ping", driver: "ping" }
|
||||
});
|
||||
|
||||
device = bot.devices.ping;
|
||||
});
|
||||
|
||||
describe("async", function() {
|
||||
context("looking for a valid robot/device", function() {
|
||||
it("calls the callback with the device and no error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobotDevice("Ultron", "ping", callback);
|
||||
expect(callback).to.be.calledWith(undefined, device);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for a valid robot and invalid device", function() {
|
||||
it("calls the callback with no device and an error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobotDevice("Ultron", "nope", callback);
|
||||
var error = { error: "No device found with the name nope." };
|
||||
expect(callback).to.be.calledWith(error, null);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for an invalid robot", function() {
|
||||
it("calls the callback with no device and an error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobotDevice("Rob", "ping", callback);
|
||||
var error = { error: "No Robot found with the name Rob" };
|
||||
expect(callback).to.be.calledWith(error, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("synchronous", function() {
|
||||
context("looking for a valid robot/device", function() {
|
||||
it("returns the device", function() {
|
||||
expect(Cylon.findRobotDevice("Ultron", "ping")).to.be.eql(device);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for a valid robot and invalid device", function() {
|
||||
it("returns null", function() {
|
||||
expect(Cylon.findRobotDevice("Ultron", "nope")).to.be.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for an invalid robot", function() {
|
||||
it("returns null", function() {
|
||||
expect(Cylon.findRobotDevice("Rob", "ping")).to.be.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#findRobotConnection", function() {
|
||||
var bot, conn;
|
||||
|
||||
before(function() {
|
||||
bot = Cylon.robot({
|
||||
name: "JARVIS",
|
||||
connection: { name: "loopback", adaptor: "loopback" }
|
||||
});
|
||||
|
||||
conn = bot.connections.loopback;
|
||||
});
|
||||
|
||||
describe("async", function() {
|
||||
context("looking for a valid robot/connection", function() {
|
||||
it("calls the callback with the connection and no error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobotConnection("JARVIS", "loopback", callback);
|
||||
expect(callback).to.be.calledWith(undefined, conn);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for a valid robot and invalid connection", function() {
|
||||
it("calls the callback with no connection and an error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobotConnection("JARVIS", "nope", callback);
|
||||
var error = { error: "No connection found with the name nope." };
|
||||
expect(callback).to.be.calledWith(error, null);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for an invalid robot", function() {
|
||||
it("calls the callback with no connection and an error message", function() {
|
||||
var callback = spy();
|
||||
Cylon.findRobotConnection("Rob", "loopback", callback);
|
||||
var error = { error: "No Robot found with the name Rob" };
|
||||
expect(callback).to.be.calledWith(error, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("synchronous", function() {
|
||||
context("looking for a valid robot/connection", function() {
|
||||
it("returns the connection", function() {
|
||||
expect(Cylon.findRobotConnection("JARVIS", "loopback")).to.be.eql(conn);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for a valid robot and invalid connection", function() {
|
||||
it("returns null", function() {
|
||||
expect(Cylon.findRobotConnection("JARVIS", "nope")).to.be.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
context("looking for an invalid robot", function() {
|
||||
it("returns null", function() {
|
||||
expect(Cylon.findRobotConnection("Rob", "loopback")).to.be.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#start", function() {
|
||||
before(function() {
|
||||
Cylon.robots = [];
|
||||
stub(Cylon, 'startAPI').returns(true);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
Cylon.startAPI.restore();
|
||||
});
|
||||
|
||||
it("starts the API", function() {
|
||||
Cylon.start();
|
||||
expect(Cylon.startAPI).to.be.called;
|
||||
});
|
||||
|
||||
it("calls #start() on all robots", function() {
|
||||
var bot1 = { start: spy() },
|
||||
bot2 = { start: spy() };
|
||||
|
||||
Cylon.robots = [bot1, bot2];
|
||||
Cylon.robots = {
|
||||
'bot1': bot1,
|
||||
'bot2': bot2
|
||||
};
|
||||
|
||||
Cylon.start();
|
||||
|
||||
|
@ -334,15 +83,14 @@ describe("Cylon", function() {
|
|||
});
|
||||
|
||||
describe("#halt", function() {
|
||||
before(function() {
|
||||
Cylon.robots = [];
|
||||
});
|
||||
|
||||
it("calls #halt() on all robots", function() {
|
||||
var bot1 = { halt: spy() },
|
||||
bot2 = { halt: spy() };
|
||||
|
||||
Cylon.robots = [bot1, bot2];
|
||||
Cylon.robots = {
|
||||
'bot1': bot1,
|
||||
'bot2': bot2
|
||||
};
|
||||
|
||||
Cylon.halt();
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
var Ping = source('test/ping'),
|
||||
Device = source("device"),
|
||||
Robot = source("robot"),
|
||||
Logger = source('logger');
|
||||
Logger = source('logger'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("Cylon.Device", function() {
|
||||
describe("Device", function() {
|
||||
var robot = new Robot({
|
||||
name: "TestingBot",
|
||||
connection: { name: 'loopback', adaptor: 'loopback' }
|
||||
|
@ -28,10 +29,6 @@ describe("Cylon.Device", function() {
|
|||
});
|
||||
|
||||
describe("constructor", function() {
|
||||
it("sets @self as a circular reference", function() {
|
||||
expect(device.self).to.be.eql(device);
|
||||
});
|
||||
|
||||
it("sets @robot to the passed robot", function() {
|
||||
expect(device.robot).to.be.eql(robot);
|
||||
});
|
||||
|
@ -74,7 +71,7 @@ describe("Cylon.Device", function() {
|
|||
|
||||
it("logs that it's starting the device", function() {
|
||||
stub(Logger, 'info');
|
||||
var message = "Starting device ping on pin 13";
|
||||
var message = "Starting device 'ping' on pin 13.";
|
||||
|
||||
device.start()
|
||||
|
||||
|
@ -99,7 +96,7 @@ describe("Cylon.Device", function() {
|
|||
});
|
||||
|
||||
it("logs that it's halt the device", function() {
|
||||
var message = "Halting device ping";
|
||||
var message = "Halting device 'ping'.";
|
||||
stub(Logger, 'info');
|
||||
|
||||
device.halt();
|
||||
|
@ -109,31 +106,31 @@ describe("Cylon.Device", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#data", function() {
|
||||
var data = device.data();
|
||||
describe("#toJSON", function() {
|
||||
var json = device.toJSON();
|
||||
|
||||
it("returns an object", function() {
|
||||
expect(data).to.be.a('object');
|
||||
expect(json).to.be.a('object');
|
||||
});
|
||||
|
||||
it("contains the device's name", function() {
|
||||
expect(data.name).to.be.eql(device.name);
|
||||
expect(json.name).to.be.eql(device.name);
|
||||
});
|
||||
|
||||
it("contains the device's pin", function() {
|
||||
expect(data.pin).to.be.eql(device.pin);
|
||||
expect(json.pin).to.be.eql(device.pin);
|
||||
});
|
||||
|
||||
it("contains the device's driver name", function() {
|
||||
expect(data.driver).to.be.eql('Ping');
|
||||
expect(json.driver).to.be.eql('Ping');
|
||||
});
|
||||
|
||||
it("contains the device's connection data", function() {
|
||||
expect(data.connection).to.be.eql(device.connection.data());
|
||||
it("contains the device's connection json", function() {
|
||||
expect(json.connection).to.be.eql(device.connection.toJSON());
|
||||
});
|
||||
|
||||
it("contains the device's driver commands", function() {
|
||||
expect(data.commands).to.be.eql(driver.commands());
|
||||
expect(json.commands).to.be.eql(driver.commands);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
var fs = require('fs');
|
||||
|
||||
var DigitalPin = source('io/digital-pin');
|
||||
var DigitalPin = source('io/digital-pin'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("Cylon.IO.DigitalPin", function() {
|
||||
var pin = new DigitalPin({ pin: '4', mode: 'w' })
|
||||
|
@ -195,12 +196,12 @@ describe("Cylon.IO.DigitalPin", function() {
|
|||
|
||||
context("if the mode isn't 'r'", function() {
|
||||
before(function() {
|
||||
stub(global, 'every');
|
||||
stub(Utils, 'every');
|
||||
stub(pin, '_setMode');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
global.every.restore();
|
||||
Utils.every.restore();
|
||||
pin._setMode.restore();
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Driver = source("driver"),
|
||||
Logger = source('logger');
|
||||
Logger = source('logger'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("Driver", function() {
|
||||
var device = {
|
||||
|
@ -33,23 +34,8 @@ describe("Driver", function() {
|
|||
expect(driver.connection).to.be.eql(device.connection);
|
||||
});
|
||||
|
||||
it("sets @commandList to an empty array by default", function() {
|
||||
expect(driver.commandList).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#commands", function() {
|
||||
var commands = ['list', 'of', 'commands']
|
||||
before(function() {
|
||||
driver.commandList = commands;
|
||||
});
|
||||
|
||||
after(function() {
|
||||
driver.commandList = [];
|
||||
});
|
||||
|
||||
it("returns the driver's @commandList", function() {
|
||||
expect(driver.commands()).to.be.eql(commands);
|
||||
it("sets @commands to an empty array by default", function() {
|
||||
expect(driver.commands).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -66,23 +52,22 @@ describe("Driver", function() {
|
|||
});
|
||||
|
||||
it("logs that it's starting the driver", function() {
|
||||
var string = "Driver driver started";
|
||||
var string = "Driver driver started.";
|
||||
expect(Logger.info).to.be.calledWith(string);
|
||||
});
|
||||
|
||||
it("triggers the provided callback", function() {
|
||||
expect(callback).to.be.called;
|
||||
});
|
||||
|
||||
it("tells the device to emit the 'start' event", function() {
|
||||
expect(device.emit).to.be.calledWith('start');
|
||||
});
|
||||
});
|
||||
|
||||
describe("#halt", function() {
|
||||
var callback;
|
||||
|
||||
before(function() {
|
||||
stub(Logger, 'info');
|
||||
driver.halt();
|
||||
callback = spy();
|
||||
driver.halt(callback);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
|
@ -90,7 +75,11 @@ describe("Driver", function() {
|
|||
});
|
||||
|
||||
it("logs that it's halting the driver", function() {
|
||||
expect(Logger.info).to.be.calledWith("Driver driver halted")
|
||||
expect(Logger.info).to.be.calledWith("Driver driver halted.")
|
||||
});
|
||||
|
||||
it("triggers the callback", function() {
|
||||
expect(callback).to.be.called;
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var Logger = source('logger');
|
||||
var Logger = source('logger'),
|
||||
Utils = source('utils');
|
||||
|
||||
describe('Logger', function() {
|
||||
after(function() {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
'use strict';
|
||||
|
||||
var BasicLogger = source('logger/basic_logger');
|
||||
|
||||
var date = new Date(0).toISOString();
|
||||
|
||||
describe('BasicLogger', function() {
|
||||
var logger = new BasicLogger(),
|
||||
clock;
|
||||
|
||||
beforeEach(function() {
|
||||
stub(console, 'log');
|
||||
clock = sinon.useFakeTimers(0);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
console.log.restore();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
describe("#toString", function() {
|
||||
it("returns 'BasicLogger'", function() {
|
||||
expect(logger.toString()).to.be.eql('BasicLogger');
|
||||
});
|
||||
});
|
||||
|
||||
describe("#debug", function() {
|
||||
it("logs to the console with a debug string", function() {
|
||||
var logstring = "D, [" + date + "] DEBUG -- :";
|
||||
|
||||
logger.debug("Hello, World");
|
||||
expect(console.log).to.be.calledWith(logstring, "Hello, World");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#info", function() {
|
||||
it("logs to the console with a info string", function() {
|
||||
var logstring = "I, [" + date + "] INFO -- :";
|
||||
|
||||
logger.info("Hello, World");
|
||||
expect(console.log).to.be.calledWith(logstring, "Hello, World");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#warn", function() {
|
||||
it("logs to the console with a warn string", function() {
|
||||
var logstring = "W, [" + date + "] WARN -- :";
|
||||
|
||||
logger.warn("Hello, World");
|
||||
expect(console.log).to.be.calledWith(logstring, "Hello, World");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#error", function() {
|
||||
it("logs to the console with a error string", function() {
|
||||
var logstring = "E, [" + date + "] ERROR -- :";
|
||||
|
||||
logger.error("Hello, World");
|
||||
expect(console.log).to.be.calledWith(logstring, "Hello, World");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#fatal", function() {
|
||||
it("logs to the console with a fatal string", function() {
|
||||
var logstring = "F, [" + date + "] FATAL -- :";
|
||||
|
||||
logger.fatal("Hello, World");
|
||||
expect(console.log).to.be.calledWith(logstring, "Hello, World");
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
var Device = source('device'),
|
||||
Connection = source('connection'),
|
||||
Robot = source("robot");
|
||||
Robot = source("robot"),
|
||||
Utils = source('utils');
|
||||
|
||||
describe("Robot", function() {
|
||||
var work = spy();
|
||||
|
@ -19,10 +20,6 @@ describe("Robot", function() {
|
|||
});
|
||||
|
||||
describe("constructor", function() {
|
||||
it("sets a @robot variable as a circular reference to the robot", function() {
|
||||
expect(robot.robot).to.be.eql(robot);
|
||||
});
|
||||
|
||||
describe("name", function() {
|
||||
context("if provided", function() {
|
||||
it("is set to the passed value", function() {
|
||||
|
@ -76,34 +73,46 @@ describe("Robot", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#data", function() {
|
||||
describe("all work and no play", function() {
|
||||
var play = spy();
|
||||
|
||||
var playBot = new Robot({
|
||||
play: play
|
||||
});
|
||||
|
||||
it('makes Jack a dull boy', function() {
|
||||
expect(playBot.work).to.be.eql(play);
|
||||
})
|
||||
})
|
||||
|
||||
describe("#toJSON", function() {
|
||||
var bot = new Robot({
|
||||
connection: { name: 'loopback', adaptor: 'loopback' },
|
||||
device: { name: 'ping', driver: 'ping' }
|
||||
});
|
||||
|
||||
var data = bot.data();
|
||||
var json = bot.toJSON();
|
||||
|
||||
it("returns an object", function() {
|
||||
expect(data).to.be.a('object');
|
||||
expect(json).to.be.a('object');
|
||||
});
|
||||
|
||||
it("contains the robot's name", function() {
|
||||
expect(data.name).to.eql(bot.name);
|
||||
expect(json.name).to.eql(bot.name);
|
||||
});
|
||||
|
||||
it("contains the robot's commands", function() {
|
||||
expect(data.commands).to.eql(bot.commands);
|
||||
expect(json.commands).to.eql(bot.commands);
|
||||
});
|
||||
|
||||
it("contains the robot's devices", function() {
|
||||
var deviceData = bot.devices.ping.data();
|
||||
expect(data.devices).to.eql([deviceData]);
|
||||
var deviceJSON = bot.devices.ping.toJSON();
|
||||
expect(json.devices).to.eql([deviceJSON]);
|
||||
});
|
||||
|
||||
it("contains the robot's connections", function() {
|
||||
var connectionData = bot.connections.loopback.data();
|
||||
expect(data.connections).to.eql([connectionData]);
|
||||
var connectionJSON = bot.connections.loopback.toJSON();
|
||||
expect(json.connections).to.eql([connectionJSON]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -25,6 +25,14 @@ describe("Utils", function() {
|
|||
it("converts floats", function() {
|
||||
expect(2.5.fromScale(0, 10)).to.be.eql(0.25);
|
||||
});
|
||||
|
||||
it("should return 1 if the number goes above the top of the scale", function() {
|
||||
expect((15).fromScale(0, 10)).to.be.eql(1);
|
||||
});
|
||||
|
||||
it("should return 0 if the number goes below the bottom of the scale", function() {
|
||||
expect((5).fromScale(10, 20)).to.be.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#toScale", function() {
|
||||
|
@ -32,6 +40,14 @@ describe("Utils", function() {
|
|||
expect((0.5).toScale(0, 10)).to.be.eql(5);
|
||||
});
|
||||
|
||||
it("bottom of scale should be returned when value goes below it", function() {
|
||||
expect((-5).toScale(0, 10)).to.be.eql(0);
|
||||
});
|
||||
|
||||
it("top of scale should be returned when value goes above it", function() {
|
||||
expect((15).toScale(0, 10)).to.be.eql(10);
|
||||
});
|
||||
|
||||
it("converts to floats", function() {
|
||||
expect(0.25.toScale(0, 10)).to.be.eql(2.5);
|
||||
});
|
||||
|
@ -95,20 +111,6 @@ describe("Utils", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#slice", function() {
|
||||
it("performs array slices", function() {
|
||||
var arr = [1, 2, 3, 4, 5];
|
||||
expect(slice.call(arr, 1)).to.be.eql([2, 3, 4, 5]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasProp", function() {
|
||||
it("checks objects have properties", function() {
|
||||
var obj = { test: 'test' };
|
||||
expect(hasProp.call(obj, 'test')).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("#subclass", function() {
|
||||
var BaseClass = (function() {
|
||||
function BaseClass(opts) {
|
||||
|
@ -123,7 +125,7 @@ describe("Utils", function() {
|
|||
})();
|
||||
|
||||
var SubClass = (function(klass) {
|
||||
subclass(SubClass, klass);
|
||||
utils.subclass(SubClass, klass);
|
||||
|
||||
function SubClass(opts) {
|
||||
SubClass.__super__.constructor.apply(this, arguments);
|
||||
|
@ -164,7 +166,7 @@ describe("Utils", function() {
|
|||
function TestClass() {
|
||||
this.self = this;
|
||||
this.testInstance = new ProxyClass;
|
||||
proxyFunctionsToObject(methods, this.testInstance, this.self, true);
|
||||
utils.proxyFunctionsToObject(methods, this.testInstance, this.self, true);
|
||||
}
|
||||
|
||||
return TestClass;
|
||||
|
@ -186,19 +188,19 @@ describe("Utils", function() {
|
|||
});
|
||||
|
||||
describe("#proxyTestStubs", function() {
|
||||
it("proxies methods to an object's commandList", function() {
|
||||
it("proxies methods to an object's commands", function() {
|
||||
var methods = ["hello", "goodbye"],
|
||||
base = { commandList: [] };
|
||||
base = { commands: [] };
|
||||
|
||||
proxyTestStubs(methods, base);
|
||||
expect(base.commandList).to.be.eql(methods);
|
||||
utils.proxyTestStubs(methods, base);
|
||||
expect(base.commands).to.be.eql(methods);
|
||||
});
|
||||
|
||||
it("returns the object methods have been proxied to", function() {
|
||||
var methods = ["hello", "goodbye"],
|
||||
base = { commandList: [] };
|
||||
base = { commands: [] };
|
||||
|
||||
expect(proxyTestStubs(methods, base)).to.be.eql(base);
|
||||
expect(utils.proxyTestStubs(methods, base)).to.be.eql(base);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -208,14 +210,14 @@ describe("Utils", function() {
|
|||
|
||||
it("binds the 'this' scope for the method", function() {
|
||||
proxy.boundMethod = function() { return this.hello; };
|
||||
proxy.boundMethod = bind(proxy.boundMethod, me);
|
||||
proxy.boundMethod = utils.bind(proxy.boundMethod, me);
|
||||
|
||||
expect(proxy.boundMethod()).to.eql("Hello World");
|
||||
});
|
||||
|
||||
it("passes arguments along to bound functions", function() {
|
||||
proxy.boundMethod = function(hello, world) { return [hello, world]; };
|
||||
proxy.boundMethod = bind(proxy.boundMethod, me);
|
||||
proxy.boundMethod = utils.bind(proxy.boundMethod, me);
|
||||
|
||||
expect(proxy.boundMethod("Hello", "World")).to.eql(["Hello", "World"]);
|
||||
})
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
var sinon = require('sinon'),
|
||||
spy = sinon.spy,
|
||||
stub = sinon.stub;
|
||||
|
||||
// A mock version of the http.ClientRequest class
|
||||
var MockRequest = module.exports = function MockRequest(opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
this.url = "/";
|
||||
|
||||
this.headers = {};
|
||||
|
||||
for (var opt in opts) {
|
||||
this[opt] = opts[opt];
|
||||
}
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
var sinon = require('sinon'),
|
||||
spy = sinon.spy,
|
||||
stub = sinon.stub;
|
||||
|
||||
// A mock version of http.ServerResponse to be used in tests
|
||||
var MockResponse = module.exports = function MockResponse() {
|
||||
this.end = spy();
|
||||
|
||||
this.headers = {};
|
||||
};
|
||||
|
||||
MockResponse.prototype.setHeader = function setHeader(name, value) {
|
||||
this.headers[name] = value;
|
||||
};
|
Loading…
Reference in New Issue