Working With Robots
This guide should help you interact with Cylon's Robot class.
Here's the example Robot we'll be using:
Cylon.robot({ connections: { arduino: { adaptor: 'firmata', port: '/dev/ttyACM0' } }, devices: { led: { driver: 'led', pin: 13 }, button: { driver: 'button', pin: 2 } }, work: function(my) { my.button.on('push', function() { my.led.toggle(); }); } });
my
vs. this
A robot's work block is run in the context of the robot. To make code with closures and other nesting work better, we also pass it as an argument to the work block. So these two works blocks are equivalent in behaviour:
work: function() { this.button.on("push", function() { this.led.toggle(); }.bind(this)); } // is equal to work: function(my) { my.button.on("push", function() { my.led.toggle(); }); }
Programmers preferring more idiomatic JavaScript might prefer to use this
, but to simplify things in our examples, we will use the my
-argument style.
Connections
When initializing a robot, you pass a connections
object:
Cylon.robot({ connections: { loopback: { adaptor: "loopback" }, arduino: { adaptor: "firmata", port: "/dev/ttyACM0" } } });
Inside a work block, the Robot's connections can be accessed through the connections
attribute, which contains a mapping of names to connections.
Additionally, if they're not overridden by a Device's name, Connections are directly aliased as properties on the Robot for convenient access.
Cylon.robot({ connections: { loopback: { adaptor: "loopback" }, arduino: { adaptor: "firmata", port: "/dev/ttyACM0" } }, devices: { loopback: { driver: 'ping', connection: 'loopback' } } work: function(my) { my.connections; // { // loopback: [Connection], // arduino: [Connection] // } my.arduino; // [Connection] my.loopback; // [Device] } });
Devices
Devices, for the most part, behave similarly to Connections.
When initializing a robot, you pass a devices
object:
Cylon.robot({ devices: { led: { driver: "led", pin: "13" } } }); // vs Cylon.robot({ devices: { led: { driver: 'led', pin: 13 }, button: { driver: 'button', pin: 2 } } });
Inside a work block, the Robot's devices can be accessed through the devices
attribute, which contains a mapping of names to devices.
Devices are also aliased directly as properties on the robot. This aliasing overrides connection aliases if they happen to have the same name.
Cylon.robot({ connections: { loopback: { adaptor: 'loopback' }, arduino: { adaptor: 'firmata', port: '/dev/ttyACM0' } }, devices: { led: { driver: 'led', pin: 13, connection: 'arduino' }, button: { driver: 'button', pin: 2, connection: 'arduino' }, loopback: { driver: 'ping', connection: 'loopback' } }, work: function(my) { my.devices; // { // led: [Device], // button: [Device], // loopback: [Device], // } my.led; // [Device] my.button; // [Device] my.loopback; // [Device] my.arduino; // [Connection] } });
Commands
Robots in Cylon can have commands that can be called from the API.
There's three main ways to define commands:
Automatically - by default, any non-standard property on a Robot that is a function will be made accessible to the API. For example:
Cylon.robot({ name: "testbot", connections: { "loopback": { adaptor: "loopback" } }, devices: { "ping": { driver: "ping" } }, work: function() {}, doAThing: function() { console.log("I did a thing!"); } }); // GET /api/robots/testbot/commands // => ['doAThing']
As an Object - providing a commands
object in the Robot options:
Cylon.robot({ name: "testbot", connections: { "loopback": { adaptor: "loopback" } }, devices: { "ping": { driver: "ping" } }, work: function() {}, commands: { do_a_thing: function() { this.doAThing.call(this); } } doAThing: function() { console.log("I did a thing!"); } }); // GET /api/robots/testbot/commands // => ['do_a_thing']
As a Function - this option allows for easier scoping, as you can directly reference Robot properties. The function must return an Object or an error will be thrown.
Cylon.robot({ name: "testbot", connections: { "loopback": { adaptor: "loopback" } }, devices: { "ping": { driver: "ping" } }, work: function() {}, commands: function() { var commands = {}; commands.do_a_thing = this.doAThing return commands; } doAThing: function() { console.log("I did a thing!"); } }); // GET /api/robots/testbot/commands // => ['do_a_thing']
Utilities
Cylon provides a few helpful utility functions that can be used in your Robot's work block.
Number#seconds is a new prototype method on Number
, allowing for easier setting of intervals/timeouts.
For example:
(10).seconds(); // 10000 (60).seconds(); // 60000 setInterval(randomFunction, (5).seconds()); // will repeat every 5s
The Number being used needs a wrapper set of parentheses, due to how JS the JS parser works.
Alternatively, 10..seconds()
will have the same effect.
Number#second functions the same, but is meant to make calling it on 1
look more natural.
e.g. (1).seconds();
vs. (1).second();
Number#fromScale converts a number from a provided scale to a (0..1) scale. If the number exceeds the provided scale, either 0 or 1 will be returned. For example:
(100).fromScale(0, 200); // 0.5 (100).fromScale(0, 50); // 1 (-100).fromScale(0, 50); // 0
Number#toScale converts a number from a (0..1) scale to the provided scale. If the number exceeds the provided scale, either the min or max end of the scale will be returned. For example:
(0.5).toScale(0, 200); // 100 (10).toScale(0, 50); // 50 (-1).toScale(0, 50); // 0
every is a wrapper around setInterval
, with the aim of making the semantics easier for new developers.
It's intended to be used with the Number#seconds
helper method.
Examples:
every((5).seconds(), function() { console.log("Hello world!"); }); // is equivalent to: setInterval(function() { console.log("Hello world!"); }, 5000)
after is a wrapper around setTimeout
, with similar semantics to every
.
Examples:
after((5).seconds(), function() { console.log("Hello world!"); }); // is equivalent to: setTimeout(function() { console.log("Hello world!"); }, 5000)
constantly is a wrapper around setInterval
, with a specified interval
of 0 ms.
Examples:
constantly(function() { console.log("Hello world!"); }); // is equivalent to: setInterval(function() { console.log("Hello world!"); , 0)