├── .travis.yml
├── templates
├── generic-class-description.liquid
├── autogen-header.liquid
├── connect-super-call.liquid
├── export-string-literal-types.liquid
├── def-string-literal-types.liquid
├── led-platform-class.liquid
├── sensor-helper-classes.liquid
├── property-value-constants.liquid
└── generic-get-set.liquid
├── autogen-config.json
├── tsconfig.json
├── .gitmodules
├── examples
├── README.md
├── run-specific-motor.js
├── touch-sensor-motor-control.js
├── fade-leds.js
├── battery-metadata.js
├── raw-device-events.js
├── led-from-motor-speed.js
└── test-motor-sensor.js
├── .gitignore
├── package.json
├── gruntfile.js
├── test
├── sensor-tests.js
├── device-tests.js
├── test-utils.js
└── motor-tests.js
├── src
├── index.ts
├── io.ts
├── sensors.ts
├── extras.ts
└── motors.ts
├── README.md
└── lib
└── bluebird.d.ts
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4.1"
4 | - "0.10"
--------------------------------------------------------------------------------
/templates/generic-class-description.liquid:
--------------------------------------------------------------------------------
1 | /** {% for line in currentClass.description %}
2 | * {{ line }}{%
3 | endfor %}
4 | */
--------------------------------------------------------------------------------
/templates/autogen-header.liquid:
--------------------------------------------------------------------------------
1 | // Sections of the following code were auto-generated based on spec v{{ meta.version }}{% if meta.specRevision %}, rev {{meta.specRevision}}{% endif %}.
2 |
--------------------------------------------------------------------------------
/autogen-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "src/extras.ts",
4 | "src/motors.ts",
5 | "src/sensors.ts",
6 | "src/index.ts"
7 | ],
8 | "templateDir": "templates/"
9 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "src/index.ts"
4 | ],
5 | "compilerOptions": {
6 | "sourceMap": true,
7 | "declaration": true,
8 | "target": "es5",
9 | "module": "commonjs",
10 | "outDir": "bin/"
11 | }
12 | }
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "docs"]
2 | path = docs
3 | url = https://github.com/wasabifan/ev3dev-lang-js
4 | [submodule "ev3dev-lang"]
5 | path = ev3dev-lang
6 | url = https://github.com/ev3dev/ev3dev-lang
7 | [submodule "test/fake-sys"]
8 | path = test/fake-sys
9 | url = https://github.com/ddemidov/ev3dev-lang-fake-sys.git
10 |
--------------------------------------------------------------------------------
/templates/connect-super-call.liquid:
--------------------------------------------------------------------------------
1 | {% assign conventionRegex = currentClass.systemDeviceNameConvention | replace: '\{\d\}', '(\\d*)'
2 | %}{% assign paramLength = extraParams | size
3 | %} super('{{currentClass.systemClassName}}', {% if omitNameConvention %}null{% else %}'{{conventionRegex}}'{% endif %}{% if paramLength > 0 %}, {{extraParams}}{% endif %});
--------------------------------------------------------------------------------
/templates/export-string-literal-types.liquid:
--------------------------------------------------------------------------------
1 | export module {{ currentClass.friendlyName | camel_case | capitalize }} {
2 | {% for propertyVals in currentClass.propertyValues %}
3 | export type {{ propertyVals.propertyName | camel_case | capitalize }}Value = {{ module }}.{{ currentClass.friendlyName | camel_case | capitalize }}.{{ propertyVals.propertyName | camel_case | capitalize }}Value;{%
4 | endfor %}
5 | }
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | ## Node.js ev3dev API examples
2 |
3 | The scripts in this folder should help you test out the functionality of this library as well as understand the access patterns for sensors, motors, and LEDs. The scripts prefixed with `test-` are meant to simply exercise the functionality of the EV3 without performing any useful functions; if you're looking for cool things to try out, look at the other samples.
4 |
--------------------------------------------------------------------------------
/templates/def-string-literal-types.liquid:
--------------------------------------------------------------------------------
1 | export module {{ currentClass.friendlyName | camel_case | capitalize }} {
2 | {% for propertyVals in currentClass.propertyValues %}{%
3 | assign type = '' %}{%
4 | for propVal in propertyVals.values %}{%
5 | assign type = type | append: "'" %}{%
6 | assign type = type | append: propVal.name %}{%
7 | assign type = type | append: "' " %}{%
8 | endfor %}{%
9 | assign type = type | trim | replace: ' ', ' | ' %}
10 | export type {{ propertyVals.propertyName | camel_case | capitalize }}Value = {{ type }};{%
11 | endfor %}
12 | }
--------------------------------------------------------------------------------
/examples/run-specific-motor.js:
--------------------------------------------------------------------------------
1 | var ev3dev = require('../bin/index.js');
2 |
3 | var motor = new ev3dev.Motor(ev3dev.OUTPUT_A);
4 | if(!motor.connected) {
5 | console.error("No motor was found on port A. Please connect a tacho motor to port A and try again.");
6 | process.exit(1);
7 | }
8 |
9 | motor.runForDistance(360 * 10, 500, motor.stopActionValues.brake);
10 |
11 | console.log("Running the motor for 180 tacho counts...");
12 |
13 | // Prevent Node from exiting until motor is done
14 | var cancellationToken = setInterval(function() {
15 | if(!motor.isRunning)
16 | clearInterval(cancellationToken);
17 | }, 200);
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | # project-specific
30 | bin/
31 | project/
--------------------------------------------------------------------------------
/examples/touch-sensor-motor-control.js:
--------------------------------------------------------------------------------
1 | var ev3dev = require('../bin/index.js');
2 |
3 | var touchSensor = new ev3dev.TouchSensor();
4 | if(!touchSensor.connected) {
5 | console.error("No touch sensor could be found! Please verify that a touch sensor is plugged in and try again.");
6 | process.exit(1);
7 | }
8 |
9 | var motor = new ev3dev.Motor();
10 | if(!motor.connected) {
11 | console.error("No valid motor was found. Please connect a tacho motor and try again.");
12 | process.exit(1);
13 | }
14 |
15 | console.log("Connected to touch sensor at address " + touchSensor.address + " and tacho motor at address " + motor.address);
16 | console.log("Press the touch sensor to spin the motor.");
17 |
18 | setInterval(function() {
19 | motor.start(motor.maxSpeed * touchSensor.getValue(0), motor.stopActionValues.hold);
20 | }, 10);
21 |
--------------------------------------------------------------------------------
/examples/fade-leds.js:
--------------------------------------------------------------------------------
1 | var ev3dev = require('../bin/index.js');
2 |
3 | console.log('fading LEDs from green to red...');
4 |
5 | for (var pct = 0; pct < 100; pct += 1) {
6 | var brightnessVal = (pct / 100);
7 | var invertedBrightnessVal = 1 - brightnessVal;
8 |
9 | ev3dev.Ev3Leds.left.setColor([ brightnessVal, invertedBrightnessVal ]);
10 | ev3dev.Ev3Leds.right.setColor([ brightnessVal, invertedBrightnessVal ]);
11 |
12 | if(pct % 10 == 0)
13 | console.log(pct + '%');
14 |
15 | { //Hack to sleep for time
16 | // SHOULD NOT BE USED IN PRODUCTION CODE
17 | var start = new Date().getTime();
18 | while(new Date().getTime() < start + 100) {
19 | ;
20 | }
21 | }
22 | }
23 |
24 | console.log('done');
25 |
26 | ev3dev.Ev3Leds.left.allOff();
27 | ev3dev.Ev3Leds.right.allOff();
--------------------------------------------------------------------------------
/examples/battery-metadata.js:
--------------------------------------------------------------------------------
1 | var ev3dev = require('../bin/index.js');
2 |
3 | function printBatteryInfo(label, battery) {
4 | console.log(label + " --------------");
5 |
6 | if(battery.connected) {
7 | console.log(' Technology: ' + battery.technology);
8 | console.log(' Type: ' + battery.type);
9 |
10 | console.log(' Current (microamps): ' + battery.measuredCurrent);
11 | console.log(' Current (amps): ' + battery.currentAmps);
12 |
13 | console.log(' Voltage (microvolts): ' + battery.measuredVoltage);
14 | console.log(' Voltage (volts): ' + battery.voltageVolts);
15 |
16 | console.log(' Max voltage (microvolts): ' + battery.maxVoltage);
17 | console.log(' Min voltage (microvolts): ' + battery.minVoltage);
18 | }
19 | else
20 | console.log(" Battery not connected!");
21 | }
22 |
23 | var defaultBattery = new ev3dev.PowerSupply();
24 | printBatteryInfo("Default battery", defaultBattery);
--------------------------------------------------------------------------------
/templates/led-platform-class.liquid:
--------------------------------------------------------------------------------
1 | export class {{ currentPlatformClassName }} {
2 | {% for instanceInfo in currentPlatform.led.instances %}
3 | public static {{ instanceInfo.name | camel_case }} = new extras.LED("{{instanceInfo.systemName}}");{%
4 | endfor %}
5 | {%
6 | for groupInfo in currentPlatform.led.groups %}
7 | public static {{ groupInfo.name | camel_case }} = new extras.LEDGroup({{ groupInfo.entries | camel_case | prepend_all: '.' | prepend_all: currentPlatformClassName | join: ', ' }});{%
8 | endfor %}
9 | {%
10 | for colorInfo in currentPlatform.led.colors %}
11 | public static {{ colorInfo.name | camel_case }}Color = [{{ colorInfo.value | join: ', ' }}];{%
12 | endfor %}
13 |
14 | public static get isConnected(): boolean {
15 | return {{ currentPlatform.led.instances | select: 'name' | camel_case | prepend_all: '.' | prepend_all: currentPlatformClassName | append_all: '.connected' | join: ' && ' }};
16 | }
17 | }
--------------------------------------------------------------------------------
/examples/raw-device-events.js:
--------------------------------------------------------------------------------
1 | var ev3dev = require('../bin/index.js');
2 |
3 | var greenANSI = "\033[42m";
4 | var redANSI = "\033[41m";
5 | var resetANSI = "\033[0m";
6 |
7 | var touchSensor = new ev3dev.TouchSensor();
8 | if (!touchSensor.connected) {
9 | console.error("No touch sensor could be found! Please verify that a touch sensor is plugged in and try again.");
10 | process.exit(1);
11 | }
12 |
13 | touchSensor.registerEventCallback(function(error, touchInfo) {
14 | if(error) throw error;
15 | console.log("Sensor is " + (touchInfo.lastPressed ? greenANSI + "PRESSED" : redANSI + "RELEASED") + resetANSI);
16 | },
17 | function(userData) {
18 | var isPressed = touchSensor.isPressed;
19 | var changed = isPressed != userData.lastPressed;
20 |
21 | userData.lastPressed = isPressed;
22 | return changed;
23 | }, false, { lastPressed: false });
24 |
25 | console.log("Press the touch sensor to trigger the press event.");
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ev3dev-lang",
3 | "version": "1.0.0",
4 | "description": "An interface to control an EV3 or other supported platform running ev3dev from JavaScript.",
5 | "keywords": [
6 | "ev3dev",
7 | "ev3",
8 | "lego",
9 | "robotics"
10 | ],
11 | "homepage": "http://github.com/wasabifan/ev3dev-lang-js",
12 | "bugs": {
13 | "url": "http://github.com/wasabifan/ev3dev-lang-js/issues"
14 | },
15 | "author": {
16 | "name": "WasabiFan",
17 | "email": "WasabiFan@outlook.com"
18 | },
19 | "devDependencies": {
20 | "grunt": "^0.4.5",
21 | "grunt-cli": "^0.1.13",
22 | "grunt-shell": "^1.0.1",
23 | "grunt-typedoc": "0.2.3",
24 | "grunt-ts": "5.5.0-beta.2",
25 | "typescript": ">=1.8.0",
26 | "mocha": "2.3.4",
27 | "python-shell": "*"
28 | },
29 | "scripts": {
30 | "test": "grunt tsc && ./node_modules/mocha/bin/mocha"
31 | },
32 | "files": [
33 | "bin/"
34 | ],
35 | "main": "bin/index.js",
36 | "repository": {
37 | "type": "git",
38 | "url": "http://github.com/wasabifan/ev3dev-lang-js.git"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | grunt.loadNpmTasks('grunt-ts');
3 | grunt.loadNpmTasks('grunt-typedoc');
4 |
5 | grunt.initConfig({
6 | pkg: grunt.file.readJSON('package.json'),
7 | ts: {
8 | main: {
9 | src: "src/index.ts",
10 | dest: 'bin/',
11 | options: {
12 | target: 'es5',
13 | sourceMap: true,
14 | declaration: true,
15 | module: 'commonjs'
16 | }
17 | }
18 | },
19 | typedoc: {
20 | doc: {
21 | src: "src/index.ts",
22 | options: {
23 | out: './docs',
24 | name: 'ev3dev-lang for Node.js',
25 | target: 'es5',
26 | disableOutputCheck: '',
27 | module: 'commonjs'
28 | }
29 | }
30 | }
31 | });
32 |
33 | grunt.registerTask('default', ['ts', 'typedoc']);
34 | grunt.registerTask('tsc', ['ts']);
35 | grunt.registerTask('doc', ['ts', 'typedoc']);
36 | }
37 |
--------------------------------------------------------------------------------
/templates/sensor-helper-classes.liquid:
--------------------------------------------------------------------------------
1 | {% for currentClassMetadata in specialSensorTypes %}{%
2 | assign currentClass = currentClassMetadata[1]
3 | %}
4 | /** {% for line in currentClass.description %}
5 | * {{ line }}{%
6 | endfor %}
7 | */
8 | export class {{ currentClass.friendlyName | camel_case | capitalize }} extends Sensor {
9 | constructor(port?: string) {
10 | super(port, {{ currentClass.driverName | json_stringify }});
11 | }
12 |
13 | {% for valueMapping in currentClass.sensorValueMappings %}{%
14 | if valueMapping.type contains 'int' or valueMapping.type contains 'float' %}{%
15 | assign type = 'number' %}{%
16 | elsif valueMapping.type contains 'boolean' or valueMapping.type contains 'bool' %}{%
17 | assign type = 'boolean' %}{%
18 | else %}{%
19 | assign type = 'string' %}{%
20 | endif %}
21 | /**{% for line in valueMapping.description
22 | %}
23 | * {{ line }}{%
24 | endfor %}
25 | */
26 | get {{ valueMapping.name | camel_case }}(): {{ type }} {
27 | this.mode = '{{ valueMapping.requiredMode }}';
28 | return {{ type | capitalize }}(this.getFloatValue({{ valueMapping.sourceValue }}));
29 | }
30 | {% endfor %}
31 | }
32 | {% endfor %}
--------------------------------------------------------------------------------
/examples/led-from-motor-speed.js:
--------------------------------------------------------------------------------
1 | var ev3dev = require('../bin/index.js');
2 | var stoppedBlinkInterval = 200;
3 |
4 | if(!ev3dev.Ev3Leds.isConnected) {
5 | console.error("This sample can only run on the EV3 brick. Other platforms are not supported by this script.");
6 | process.exit(1);
7 | }
8 |
9 | var motor = new ev3dev.Motor();
10 |
11 | if(!motor.connected) {
12 | console.error("No valid motor was found. Please connect a tacho motor and try again.");
13 | process.exit(1);
14 | }
15 |
16 | console.log("Connected to motor " + motor.address);
17 | motor.stopAction = motor.stopActionValues.coast;
18 |
19 | console.log("Timer running... Rotate the motor and watch the on-board LEDs.");
20 |
21 | setInterval(function() {
22 |
23 | if(motor.speed > 1) {
24 | var rpsSpeed = Math.min(Math.abs(motor.speed) / motor.countPerRot, 1);
25 | var ledColor = [rpsSpeed, 1 - rpsSpeed];
26 | ev3dev.Ev3Leds.left.setColor(ledColor);
27 | ev3dev.Ev3Leds.right.setColor(ledColor);
28 | }
29 | else {
30 | var blinkOn = (new Date()).getTime() % stoppedBlinkInterval > (stoppedBlinkInterval / 2);
31 | ev3dev.Ev3Leds.left.setColor([0, blinkOn? 0 : 1]);
32 | ev3dev.Ev3Leds.right.setColor([0, 1]);
33 | }
34 |
35 | }, 80);
36 |
--------------------------------------------------------------------------------
/templates/property-value-constants.liquid:
--------------------------------------------------------------------------------
1 | {% for propertyVals in currentClass.propertyValues %}{%
2 | assign type = '' %}{%
3 | for propVal in propertyVals.values %}{%
4 | assign valName = propVal.name | camel_case %}{%
5 | assign valNameCaps = valName | capitalize %}{%
6 | assign className = currentClass.friendlyName | camel_case | capitalize %}{%
7 | assign propName = propertyVals.propertyName | camel_case | capitalize %}{%
8 |
9 | assign type = type | append: valName %}{%
10 | assign type = type | append: ": " %}{%
11 |
12 | assign type = type | append: className %}{%
13 | assign type = type | append: "." %}{%
14 | assign type = type | append: propName %}{%
15 | assign type = type | append: "Value" %}{%
16 |
17 | unless forloop.last %}{%
18 | assign type = type | append: ", " %}{%
19 | endunless %}{%
20 | endfor %}
21 | public get {{ propertyVals.propertyName | camel_case }}Values(): { {{ type }} } {
22 | return { {%
23 | for propVal in propertyVals.values %}
24 | {{ propVal.name | camel_case }}: "{{ propVal.name }}"{% unless forloop.last %},{% endunless %}{%
25 | endfor %}
26 | }
27 | }
28 | {%
29 | endfor %}
--------------------------------------------------------------------------------
/examples/test-motor-sensor.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/node
2 |
3 | var ev3dev = require('../bin/index.js');
4 |
5 | // Run motor
6 | console.log('Motor --------------');
7 | // Pick the first connected motor
8 | var motor = new ev3dev.Motor();
9 |
10 | if (!motor.connected)
11 | console.log("No motor could be found. Are you sure that one is connected?");
12 |
13 | console.log(' Port: ' + motor.address);
14 | console.log(' Driver: ' + motor.driverName);
15 | console.log(' Available commands: ' + motor.commands);
16 |
17 | console.log('Sending motor command...');
18 |
19 | motor.rampUpSp = 100;
20 | motor.rampDownSp = 100;
21 | motor.runForTime(1000, motor.maxSpeed / 2, motor.stopActionValues.brake);
22 |
23 | do {
24 | console.log("Motor speed: " + motor.speed);
25 |
26 | { //Hack to sleep for time
27 | // SHOULD NOT BE USED IN PRODUCTION CODE
28 | var start = new Date().getTime();
29 | while (new Date().getTime() < start + 80) {
30 | ;
31 | }
32 | }
33 | } while(motor.speed > 10);
34 |
35 | console.log('--------------------');
36 |
37 | //Read sensor
38 | console.log('Sensor -------------');
39 | // Pick the first connected sensor
40 | var sensor = new ev3dev.Sensor();
41 |
42 | if (!sensor.connected)
43 | console.log("No sensor could be found. Are you sure that one is connected?");
44 |
45 | console.log(' Port: ' + sensor.address);
46 | console.log(' Driver: ' + sensor.driverName);
47 |
48 | console.log('Reading all sensor values...');
49 | for(var i = 0; i < sensor.numValues; i++) {
50 | console.log(' Value ' + i + ': ' + sensor.getValue(i) + ', ' + sensor.getFloatValue(i));
51 | }
52 | console.log('--------------------')
53 | console.log("Core motor and sensor test complete");
54 |
--------------------------------------------------------------------------------
/test/sensor-tests.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var ev3dev = require('../bin/index.js');
3 | var utils = require('./test-utils.js');
4 | var path = require('path');
5 |
6 | var touchSensorData = {
7 | name: "touch_sensor",
8 | index: 0
9 | }
10 |
11 | describe('TouchSensor', function () {
12 | this.timeout(4000);
13 |
14 | before(function () {
15 | ev3dev.Device.overrideSysClassDir = path.join(__dirname, "fake-sys", "arena");
16 | });
17 |
18 | it('should connect to a specified port', function (done) {
19 | utils.populateArena({
20 | "in1": touchSensorData
21 | }, function (pathMapping) {
22 |
23 | var currentSensor = new ev3dev.TouchSensor("in1");
24 | utils.assertDeviceConnected(currentSensor, pathMapping["in1"].path);
25 |
26 | done();
27 | });
28 | });
29 |
30 | it('should properly read touch state', function (done) {
31 | utils.populateArena({
32 | "in1": touchSensorData
33 | }, function (pathMapping) {
34 |
35 | var currentSensor = new ev3dev.TouchSensor("in1");
36 |
37 | utils.assertDeviceConnected(currentSensor, pathMapping["in1"].path);
38 |
39 | utils.setDeviceProperty(currentSensor, "value0", 0);
40 | assert.equal(currentSensor.isPressed, false, "sensor shouldn't be pressed");
41 |
42 | utils.setDeviceProperty(currentSensor, "value0", 1);
43 | assert.equal(currentSensor.isPressed, true, "sensor should be pressed");
44 |
45 | utils.setDeviceProperty(currentSensor, "value0", 0);
46 | assert.equal(currentSensor.isPressed, false, "sensor shouldn't be pressed");
47 |
48 | utils.setDeviceProperty(currentSensor, "value0", 1);
49 | assert.equal(currentSensor.isPressed, true, "sensor should be pressed");
50 |
51 |
52 | done();
53 | });
54 | });
55 | });
--------------------------------------------------------------------------------
/templates/generic-get-set.liquid:
--------------------------------------------------------------------------------
1 | {% for prop in currentClass.systemProperties %}{%
2 | assign specialReadCall = '' %}{%
3 | if prop.type contains 'int' or prop.type contains 'float' %}{%
4 | assign type = 'number' %}{%
5 | assign typeCaps = 'Number' %}{%
6 | else %}{%
7 | assign type = 'string' %}{%
8 | for propertyVals in currentClass.propertyValues %}{%
9 | if propertyVals.propertyName == prop.name %}{%
10 | assign typeQualifier = currentClass.friendlyName | camel_case | capitalize | append: '.' %}{%
11 | assign type = prop.name | camel_case | capitalize | append: 'Value' | prepend: typeQualifier %}{%
12 | assign specialReadCall = 'AsType<' | append: type | append: '>' %}{%
13 | endif %}{%
14 | endfor %}{%
15 | assign typeCaps = 'String' %}{%
16 | endif %}{%
17 | if prop.type contains 'array' %}{%
18 | assign array = '[]' %}{%
19 | else %}{%
20 | assign array = blank %}{%
21 | endif %}{%
22 |
23 | if prop.readAccess == true
24 | %} /**{% for line in prop.description
25 | %}
26 | * {{ line }}{%
27 | endfor %}
28 | */
29 | get {{ prop.name | camel_case }}(): {{ type }}{{ array }} {
30 | {% if array
31 | %} return this.read{{ typeCaps }}Array{{ specialReadCall }}("{{ prop.systemName }}");{%
32 | elsif prop.type contains 'selector'
33 | %} return this.read{{ typeCaps }}Selector("{{ prop.systemName }}");{%
34 | else
35 | %} return this.read{{ typeCaps }}{{ specialReadCall }}("{{ prop.systemName }}");{%
36 | endif %}
37 | }
38 | {%
39 | endif %}{%
40 | if prop.writeAccess == true
41 | %} /**{% for line in prop.description
42 | %}
43 | * {{ line }}{%
44 | endfor %}
45 | */
46 | set {{ prop.name | camel_case }}(value: {{type}}) {
47 | this.set{{ typeCaps }}("{{ prop.systemName }}", value);
48 | }
49 | {% endif %}
50 | {% endfor %}
--------------------------------------------------------------------------------
/test/device-tests.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var ev3dev = require('../bin/index.js');
3 | var utils = require('./test-utils.js');
4 | var path = require('path');
5 |
6 | var touchSensorData = {
7 | name: "touch_sensor",
8 | index: 0
9 | }
10 |
11 | describe('Device', function () {
12 | this.timeout(4000);
13 |
14 | before(function () {
15 | ev3dev.Device.overrideSysClassDir = path.join(__dirname, "fake-sys", "arena");
16 | });
17 |
18 | it('should connect to any device if no criteria are provided', function (done) {
19 | utils.populateArena({
20 | "in1": touchSensorData
21 | }, function (pathMapping) {
22 |
23 | var currentDevice = new ev3dev.Device();
24 | currentDevice.connect("lego-sensor", "sensor(\\d*)");
25 |
26 | utils.assertDeviceConnected(currentDevice, pathMapping["in1"].path);
27 |
28 | done();
29 | });
30 | });
31 |
32 | it('should connect to a specific device if a single criterion is specified', function (done) {
33 | utils.populateArena({
34 | "in3": touchSensorData
35 | }, function (pathMapping) {
36 |
37 | var currentDevice = new ev3dev.Device();
38 | currentDevice.connect("lego-sensor", "sensor(\\d*)", {
39 | address: "in3"
40 | });
41 |
42 | utils.assertDeviceConnected(currentDevice, pathMapping["in3"].path);
43 |
44 | done();
45 | });
46 | });
47 |
48 | it('should not connect to any device if no devices matching criteria are found', function (done) {
49 | utils.populateArena({
50 | "in3": touchSensorData
51 | }, function (pathMapping) {
52 |
53 | var currentDevice = new ev3dev.Device();
54 | currentDevice.connect("lego-sensor", "sensor(\\d*)", {
55 | address: "thisDoesntExist"
56 | });
57 |
58 | assert.equal(currentDevice.connected, false);
59 |
60 | done();
61 | });
62 | });
63 | });
--------------------------------------------------------------------------------
/test/test-utils.js:
--------------------------------------------------------------------------------
1 | var PythonShell = require('python-shell');
2 | var assert = require('assert');
3 | var path = require('path');
4 | var fs = require('fs');
5 |
6 | var fakeSysRootDir = path.relative(".", path.join(__dirname, "fake-sys/"));
7 |
8 | module.exports.cleanArena = function (callback) {
9 | PythonShell.run(path.join(fakeSysRootDir, "/clean_arena.py"), function (err) {
10 | if (err) throw err;
11 |
12 | callback();
13 | });
14 | }
15 |
16 | module.exports.populateArena = function (deviceConfiguration, callback) {
17 | module.exports.cleanArena(function () {
18 | var deviceParams = [];
19 | for (var addressKey in deviceConfiguration) {
20 | deviceParams.push(
21 | deviceConfiguration[addressKey].name
22 | + ":" + deviceConfiguration[addressKey].index
23 | + "@" + addressKey);
24 | }
25 |
26 | var shellOptions = {
27 | args: deviceParams
28 | };
29 | var populateShell = new PythonShell(path.join(fakeSysRootDir, "/populate_arena.py"), shellOptions);
30 |
31 | var pathMapping = {};
32 | populateShell.on("message", function (message) {
33 | if (message.indexOf("\t") === 0) {
34 | var splitMessage = message.trim().split("\t");
35 |
36 | assert.equal(splitMessage.length, 3);
37 |
38 | pathMapping[splitMessage[1]] = {
39 | index: Number(splitMessage[0]),
40 | path: splitMessage[2]
41 | }
42 | }
43 | else {
44 | throw new Error("Populate script returned unexpected data (probably an error message): " + message);
45 | }
46 | });
47 |
48 | populateShell.end(function (err) {
49 | if (err) throw err;
50 |
51 | callback(pathMapping);
52 | })
53 | })
54 | }
55 |
56 | module.exports.assertDeviceConnected = function (device, targetPath, targetIndex, targetAddress) {
57 | assert.equal(device.connected, true);
58 |
59 | if (targetIndex != undefined && targetIndex != null) {
60 | assert.equal(path.normalize(device.deviceRoot), path.normalize(targetPath));
61 | }
62 |
63 | if (targetIndex != undefined && targetIndex != null) {
64 | if (device.hasOwnProperty("deviceIndex")) {
65 | assert.equal(device.deviceIndex, targetIndex);
66 | }
67 | else {
68 | assert.fail(undefined, undefined, "Cannot assert that device is connected at a given index if this device doesn't have an index property.");
69 | }
70 | }
71 |
72 | if (targetAddress != undefined && targetAddress != null) {
73 | if (device.hasOwnProperty("address")) {
74 | assert.equal(device.address, targetAddress);
75 | }
76 | else {
77 | assert.fail(undefined, undefined, "Cannot assert that device is connected at a given address if this device doesn't have an address property.");
78 | }
79 | }
80 | }
81 |
82 | module.exports.setDeviceProperty = function(device, key, value) {
83 | assert.doesNotThrow(function() {
84 | var propFilePath = path.normalize(path.join(device.deviceRoot, key));
85 | if(!fs.existsSync(propFilePath))
86 | throw new Error("The requested property does not exist: " + key);
87 |
88 | fs.writeFileSync(propFilePath, value);
89 | });
90 | }
91 |
92 |
93 | module.exports.getDeviceProperty = function(device, key) {
94 | var readResult = null;
95 | assert.doesNotThrow(function() {
96 | var propFilePath = path.normalize(path.join(device.deviceRoot, key));
97 | if(!fs.existsSync(propFilePath))
98 | throw new Error("The requested property does not exist: " + key);
99 |
100 | readResult = fs.readFileSync(propFilePath).toString();
101 | });
102 |
103 | return readResult;
104 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a language binding for the ev3dev device APIs. More info at: https://github.com/ev3dev/ev3dev-lang
3 | * This library complies with spec v0.9.3.
4 | */
5 |
6 | import io = require("./io");
7 | import motors = require("./motors");
8 | import sensors = require("./sensors");
9 | import extras = require("./extras");
10 |
11 | // Constants
12 | export var INPUT_AUTO = undefined;
13 | export var OUTPUT_AUTO = undefined;
14 |
15 | export var INPUT_1 = "in1";
16 | export var INPUT_2 = "in2";
17 | export var INPUT_3 = "in3";
18 | export var INPUT_4 = "in4";
19 |
20 | export var OUTPUT_A = "outA";
21 | export var OUTPUT_B = "outB";
22 | export var OUTPUT_C = "outC";
23 | export var OUTPUT_D = "outD";
24 |
25 | // IO
26 | export var Device = io.Device;
27 | export var IndexedDevice = io.IndexedDevice;
28 |
29 | // Motors
30 | export var Motor = motors.Motor;
31 | export var DcMotor = motors.DcMotor;
32 | export var LargeMotor = motors.LargeMotor;
33 | export var MediumMotor = motors.MediumMotor;
34 | export var ServoMotor = motors.ServoMotor;
35 |
36 | //~autogen export-string-literal-types classes.motor>currentClass "motors">module
37 | export module Motor {
38 |
39 | export type CommandValue = motors.Motor.CommandValue;
40 | export type EncoderPolarityValue = motors.Motor.EncoderPolarityValue;
41 | export type PolarityValue = motors.Motor.PolarityValue;
42 | export type StateValue = motors.Motor.StateValue;
43 | export type StopActionValue = motors.Motor.StopActionValue;
44 | }
45 | //~autogen
46 |
47 | //~autogen export-string-literal-types classes.servoMotor>currentClass "motors">module
48 | export module ServoMotor {
49 |
50 | export type CommandValue = motors.ServoMotor.CommandValue;
51 | export type PolarityValue = motors.ServoMotor.PolarityValue;
52 | }
53 | //~autogen
54 |
55 | //~autogen export-string-literal-types classes.dcMotor>currentClass "motors">module
56 | export module DcMotor {
57 |
58 | export type CommandValue = motors.DcMotor.CommandValue;
59 | export type PolarityValue = motors.DcMotor.PolarityValue;
60 | export type StopActionValue = motors.DcMotor.StopActionValue;
61 | }
62 | //~autogen
63 |
64 | // Sensors
65 | export var Sensor = sensors.Sensor;
66 | export var I2CSensor = sensors.I2CSensor;
67 | export var TouchSensor = sensors.TouchSensor;
68 | export var ColorSensor = sensors.ColorSensor;
69 | export var UltrasonicSensor = sensors.UltrasonicSensor;
70 | export var GyroSensor = sensors.GyroSensor;
71 | export var InfraredSensor = sensors.InfraredSensor;
72 | export var SoundSensor = sensors.SoundSensor;
73 | export var LightSensor = sensors.LightSensor;
74 |
75 | // Extras
76 | export var PowerSupply = extras.PowerSupply;
77 | export var LED = extras.LED;
78 | export var LEDGroup = extras.LEDGroup;
79 | export var LegoPort = extras.LegoPort;
80 |
81 | //~autogen led-platform-class platforms.ev3>currentPlatform "Ev3Leds">currentPlatformClassName
82 | export class Ev3Leds {
83 |
84 | public static redLeft = new extras.LED("ev3:left:red:ev3dev");
85 | public static redRight = new extras.LED("ev3:right:red:ev3dev");
86 | public static greenLeft = new extras.LED("ev3:left:green:ev3dev");
87 | public static greenRight = new extras.LED("ev3:right:green:ev3dev");
88 |
89 | public static left = new extras.LEDGroup(Ev3Leds.redLeft, Ev3Leds.greenLeft);
90 | public static right = new extras.LEDGroup(Ev3Leds.redRight, Ev3Leds.greenRight);
91 |
92 | public static blackColor = [0, 0];
93 | public static redColor = [1, 0];
94 | public static greenColor = [0, 1];
95 | public static amberColor = [1, 1];
96 | public static orangeColor = [1, 0.5];
97 | public static yellowColor = [0.1, 1];
98 |
99 | public static get isConnected(): boolean {
100 | return Ev3Leds.redLeft.connected && Ev3Leds.redRight.connected && Ev3Leds.greenLeft.connected && Ev3Leds.greenRight.connected;
101 | }
102 | }
103 | //~autogen
104 |
105 | //~autogen led-platform-class platforms.brickpi>currentPlatform "BrickpiLeds">currentPlatformClassName
106 | export class BrickpiLeds {
107 |
108 | public static blueLed1 = new extras.LED("brickpi:led1:blue:ev3dev");
109 | public static blueLed2 = new extras.LED("brickpi:led2:blue:ev3dev");
110 |
111 | public static led1 = new extras.LEDGroup(BrickpiLeds.blueLed1);
112 | public static led2 = new extras.LEDGroup(BrickpiLeds.blueLed2);
113 |
114 | public static blackColor = [0];
115 | public static blueColor = [1];
116 |
117 | public static get isConnected(): boolean {
118 | return BrickpiLeds.blueLed1.connected && BrickpiLeds.blueLed2.connected;
119 | }
120 | }
121 | //~autogen
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Node.js Language Binding for ev3dev
2 | =============
3 |
4 | This is a Node.js module that exposes the features of the [ev3dev](http://github.com/ev3dev/ev3dev) API in an easy-to-use structure. It is part of the "unified" bindings project for ev3dev, which means it implements our abstract API specification. This specification is implemented in multiple languages so that one can easily carry the same code concepts from one language to another, and all the core ev3dev APIs are supported universally.
5 |
6 | ## WARNING
7 |
8 | Due to the fact that Node.js has dropped support for the processor in the EV3, this library will no longer be supported nor operable starting with the upcoming ev3dev-stretch release. I suggest [looking into Python](https://github.com/ev3dev/ev3dev-lang-python) as an alternative language choice.
9 |
10 | **Current supported kernel version:** `*-11-ev3dev-*`
11 |
12 | ## Quickstart
13 |
14 | Install the module from `npm`:
15 |
16 | ```
17 | $ npm install ev3dev-lang
18 | ```
19 |
20 | Now add a `require` statement to your `.js` file:
21 |
22 | ```
23 | var ev3dev = require('ev3dev-lang');
24 | ```
25 |
26 | Now check out the **[online documentation](http://wasabifan.github.io/ev3dev-lang-js/)** to see what you can do. Note that all the classes listed in the docs are available in the `ev3dev` object that you imported above.
27 |
28 | ## Getting the Module
29 |
30 | ### Installing the latest release from npm
31 |
32 | The easiest way to get the module is to install it through `npm`:
33 |
34 | ```
35 | $ npm install ev3dev-lang
36 | ```
37 |
38 | And then `require()` it for use in your code.
39 |
40 | ### Downloading the source code and compiling yourself
41 | You can also download the source from GitHub directly, either from the releases page or via git. If you do it this way, you will need to follow the building instructions below to make it usable from Node.
42 |
43 | This module is written in TypeScript, which means it cannot be directly used from JavaScript or Node.js. If you would like to make changes to the module or use a copy of the module from GitHub, you will need to follow these steps to build the module from source. The below steps should work on any modern OS, including Linux, OSX and Windows.
44 |
45 | First, you will need to install some tools. Both building and running the module will require Node.js and `npm`, so make sure that you have both installed. Then install grunt, the task runner that we use to build the library:
46 | ```
47 | $ npm install -g grunt-cli
48 | ```
49 |
50 | Once you have done this, run `grunt --version` to make sure that everything was installed correctly (you may have to restart your terminal window first). Next you'll need to get the source code. You can `git clone` it to get the most recent changes, or download a release from the releases page. The following commands will need to be executed from the root directory of the source tree so `cd` in to that directory before continuing.
51 |
52 | Now we will install the last few tools that we need. The list of dependencies for the module is pre-defined in the `package.json` file, so all we need to do is to tell `npm` to install them for us:
53 | ```
54 | $ npm install
55 | ```
56 |
57 | The final step is to run the build job. We can invoke the task runner that we installed earlier to do this:
58 | ```
59 | $ grunt tsc
60 | ```
61 |
62 | The build job should have put the generated JavaScript in the `bin` folder.
63 |
64 | ## Getting started with the API
65 | We recommend that you start by running the files in the `examples/` subdirectory of the repo so that you can make sure that your system is set up correctly. Assuming you don't get any errors, you can create your own `js` file and `require` the `ev3dev-lang` module to start writing your own code. For reference, you can take a look at the example scripts or check out the [online documentation](http://wasabifan.github.io/ev3dev-lang-js/).
66 |
67 | ## Executing your Node.js scripts
68 | The simplest way is to run your code from the command line with the `node` command. This can be done over an SSH session or directly on the brick. To run a `.js` file, execute:
69 | ```bash
70 | $ node path/to/file.js
71 | ```
72 |
73 | If you want to be able to execute your scripts from brickman's file browser, you can add a [shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) and make it executable. You first must add the following code to the top of your `.js` file:
74 | ```
75 | #!/usr/bin/env node
76 | ```
77 |
78 | You can then make it executable from the command line:
79 | ```bash
80 | $ chmod +x path/to/file.js
81 | ```
82 |
83 | You should now be able to execute it directly from brickman.
84 |
85 | ## Use cases for JavaScript on the EV3
86 | JavaScript is asynchronous by nature. There is practically no way to "sleep" your code for a certain amount of time, or wait for the operation to finish. This is by design, and both restricts the use cases for Node and JS as well as opens up new scenarios to explore.
87 |
88 | Situations to use JavaScript:
89 | - Servers
90 | - Programming a web interface, where you need to serve files
91 | - Responding to commands sent by an external controller (maybe a PC and browser)
92 | - Continuously taking input
93 | - Running a job on a timer
94 | - Running any code that only occasionally "wakes up"
95 |
96 |
97 | Situations in which you should use other languages:
98 | - Sequential actions that must run in a specific order
99 | - Precise timing and delay
100 | - Coordinating multiple motors, sensors, or other hardware devices
101 |
--------------------------------------------------------------------------------
/test/motor-tests.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var ev3dev = require('../bin/index.js');
3 | var utils = require('./test-utils.js');
4 | var path = require('path');
5 |
6 | var motorData = {
7 | name: "medium_motor",
8 | index: 0
9 | }
10 |
11 | describe('Motor', function () {
12 | this.timeout(4000);
13 |
14 | before(function () {
15 | ev3dev.Device.overrideSysClassDir = path.join(__dirname, "fake-sys", "arena");
16 | });
17 |
18 | it('should connect to a specified port', function (done) {
19 | utils.populateArena({
20 | "outA": motorData
21 | }, function (pathMapping) {
22 |
23 | var currentMotor = new ev3dev.Motor("outA");
24 | utils.assertDeviceConnected(currentMotor, pathMapping["outA"].path);
25 |
26 | done();
27 | });
28 | });
29 |
30 | it('should properly set properties', function (done) {
31 | utils.populateArena({
32 | "outA": motorData
33 | }, function (pathMapping) {
34 |
35 | var currentMotor = new ev3dev.Motor("outA");
36 | utils.assertDeviceConnected(currentMotor, pathMapping["outA"].path);
37 |
38 | currentMotor.speedSp = 400;
39 | assert.equal(utils.getDeviceProperty(currentMotor, "speed_sp"), 400);
40 |
41 | currentMotor.command = "run-forever";
42 | assert.equal(utils.getDeviceProperty(currentMotor, "command"), "run-forever");
43 |
44 | currentMotor.rampDownSp = 150;
45 | assert.equal(utils.getDeviceProperty(currentMotor, "ramp_down_sp"), 150);
46 |
47 | done();
48 | });
49 | });
50 |
51 | it('should properly read properties', function (done) {
52 | utils.populateArena({
53 | "outA": motorData
54 | }, function (pathMapping) {
55 |
56 | var currentMotor = new ev3dev.Motor("outA");
57 | utils.assertDeviceConnected(currentMotor, pathMapping["outA"].path);
58 |
59 | utils.setDeviceProperty(currentMotor, "speed", 300);
60 | assert.equal(currentMotor.speed, 300);
61 |
62 | utils.setDeviceProperty(currentMotor, "polarity", "inversed");
63 | assert.equal(currentMotor.polarity, "inversed");
64 |
65 | utils.setDeviceProperty(currentMotor, "commands", "run-forever run-timed run-direct");
66 | assert.deepEqual(currentMotor.commands, ["run-forever", "run-timed", "run-direct"]);
67 |
68 | done();
69 | });
70 | });
71 |
72 | it('should properly start motor via helper functions', function (done) {
73 | utils.populateArena({
74 | "outA": motorData
75 | }, function (pathMapping) {
76 |
77 | var currentMotor = new ev3dev.Motor("outA");
78 | utils.assertDeviceConnected(currentMotor, pathMapping["outA"].path);
79 |
80 | currentMotor.start(700, currentMotor.stopActionValues.hold);
81 | assert.equal(utils.getDeviceProperty(currentMotor, "speed_sp"), 700);
82 | assert.equal(utils.getDeviceProperty(currentMotor, "command"), "run-forever");
83 | assert.equal(utils.getDeviceProperty(currentMotor, "stop_action"), "hold");
84 |
85 | currentMotor.runForDistance(720, 800, currentMotor.stopActionValues.brake);
86 | assert.equal(utils.getDeviceProperty(currentMotor, "position_sp"), 720);
87 | assert.equal(utils.getDeviceProperty(currentMotor, "speed_sp"), 800);
88 | assert.equal(utils.getDeviceProperty(currentMotor, "stop_action"), "brake");
89 | assert.equal(utils.getDeviceProperty(currentMotor, "command"), "run-to-rel-pos");
90 |
91 | currentMotor.runToPosition(150, 900, currentMotor.stopActionValues.coast);
92 | assert.equal(utils.getDeviceProperty(currentMotor, "position_sp"), 150);
93 | assert.equal(utils.getDeviceProperty(currentMotor, "speed_sp"), 900);
94 | assert.equal(utils.getDeviceProperty(currentMotor, "stop_action"), "coast");
95 | assert.equal(utils.getDeviceProperty(currentMotor, "command"), "run-to-abs-pos");
96 |
97 | currentMotor.runForTime(2000, 1000, currentMotor.stopActionValues.hold);
98 | assert.equal(utils.getDeviceProperty(currentMotor, "time_sp"), 2000);
99 | assert.equal(utils.getDeviceProperty(currentMotor, "speed_sp"), 1000);
100 | assert.equal(utils.getDeviceProperty(currentMotor, "stop_action"), "hold");
101 | assert.equal(utils.getDeviceProperty(currentMotor, "command"), "run-timed");
102 |
103 | done();
104 | });
105 | });
106 |
107 | it('should expose constants for string properties', function (done) {
108 | utils.populateArena({
109 | "outA": motorData
110 | }, function (pathMapping) {
111 |
112 | var currentMotor = new ev3dev.Motor("outA");
113 | utils.assertDeviceConnected(currentMotor, pathMapping["outA"].path);
114 |
115 | // Sampling of values; if one of them is broken, they all probably are.
116 | assert.equal(currentMotor.commandValues.runForever, "run-forever");
117 | assert.equal(currentMotor.encoderPolarityValues.inversed, "inversed");
118 |
119 | done();
120 | });
121 | });
122 | });
--------------------------------------------------------------------------------
/src/io.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import fs = require('fs');
5 | import path = require('path');
6 | var Promise: PromiseConstructor = null;
7 | try {
8 | Promise = require('bluebird');
9 | }
10 | catch( e ) {
11 | // Promises are not available
12 | }
13 |
14 |
15 | class XError {
16 | public message: string;
17 |
18 | constructor(...tsargs) {
19 | Error.apply(this, arguments);
20 | return new Error();
21 | }
22 | }
23 |
24 | XError['prototype'] = new Error();
25 |
26 | export class TraceError {
27 | public innerError: any;
28 | public message: string;
29 |
30 | constructor(message?: string, innerError?: any) {
31 | this.message = message;
32 | this.innerError = innerError;
33 | }
34 |
35 | public toString() {
36 | var str = this.message.trim() + '\r\nInner error:\r\n';
37 |
38 | var innerLines = this.innerError.toString().split('\r\n');
39 | for (var i in innerLines) {
40 | innerLines[i] = ' ' + innerLines[i];
41 | }
42 | return str + innerLines.join('\r\n');
43 | }
44 | }
45 |
46 | class EventNotificationRequest {
47 | private callbackFunction: (err?: Error) => void;
48 | private eventPredicate: (userData?: any) => boolean;
49 | private userData: any;
50 | private firstTriggerOnly: boolean;
51 |
52 | constructor(callbackFunction: (err?: Error) => void, eventPredicate: (userData?: any) => boolean, firstTriggerOnly: boolean = true, userData?: any) {
53 | this.callbackFunction = callbackFunction;
54 | this.eventPredicate = eventPredicate;
55 | this.userData = userData;
56 |
57 | this.firstTriggerOnly = firstTriggerOnly;
58 | }
59 |
60 | /**
61 | * Calls this event's predicate and invokes its callback if the
62 | * predicate signals for the event to fire. Returns a boolean
63 | * indicating whether the event should continue to be updated.
64 | */
65 | public handleUpdate(): boolean {
66 |
67 | var predicateResult: boolean;
68 |
69 | try {
70 | predicateResult = this.eventPredicate(this.userData)
71 | }
72 | catch (e) {
73 | this.callbackFunction(e);
74 | return false;
75 | }
76 |
77 | if (predicateResult) {
78 | this.callbackFunction();
79 |
80 | if (this.firstTriggerOnly)
81 | return false;
82 | }
83 |
84 | return true;
85 | }
86 | }
87 |
88 | export class Device {
89 | public static overrideSysClassDir: string = null;
90 |
91 | private static eventTimerInterval = 50;
92 |
93 | public deviceRoot: string;
94 | public deviceDirName: string;
95 | public connected: boolean = false;
96 |
97 | private sysClassDir: string = '/sys/class';
98 |
99 | private pendingEventRequests: EventNotificationRequest[] = [];
100 | private eventTimerCancellationToken: NodeJS.Timer = null;
101 |
102 | public connect(driverName: string, nameConvention?: string, propertyConstraints?: { [propertyName: string]: any }) {
103 | var nameRegex = nameConvention? new RegExp(nameConvention) : undefined;
104 |
105 | var deviceSearchDir = path.join(Device.overrideSysClassDir || this.sysClassDir, driverName);
106 |
107 | var availableDevices: string[];
108 | try {
109 | availableDevices = fs.readdirSync(deviceSearchDir);
110 | }
111 | catch (error) {
112 | return;
113 | }
114 |
115 | for (var deviceDirIndex in availableDevices) {
116 | var currentDeviceDirName = availableDevices[deviceDirIndex];
117 |
118 | if (nameRegex != undefined && !nameRegex.test(currentDeviceDirName)) {
119 | continue;
120 | }
121 |
122 | var currentDeviceDir = path.join(deviceSearchDir, currentDeviceDirName);
123 |
124 | var satisfiesConstraints: boolean = true;
125 | if (propertyConstraints != undefined) {
126 | for (var propName in propertyConstraints) {
127 | var propertyValue = this.readProperty(propName, currentDeviceDir);
128 | var constraintValue = propertyConstraints[propName];
129 |
130 | if (constraintValue instanceof Array) {
131 | if (constraintValue.indexOf(propertyValue) === -1) {
132 | satisfiesConstraints = false;
133 | }
134 | }
135 | else if (propertyValue != constraintValue) {
136 | satisfiesConstraints = false;
137 | }
138 | }
139 | }
140 |
141 | if (!satisfiesConstraints)
142 | continue;
143 |
144 | this.deviceRoot = currentDeviceDir;
145 | this.deviceDirName = currentDeviceDirName;
146 | this.connected = true;
147 | }
148 | }
149 |
150 | protected constructPropertyPath(property: string, deviceRoot?: string) {
151 | return path.join(deviceRoot || this.deviceRoot, property);
152 | }
153 |
154 | public readNumber(property: string, deviceRoot?: string): number {
155 | var value = this.readProperty(property, deviceRoot);
156 |
157 | if (typeof value !== 'number')
158 | return NaN;
159 |
160 | return value;
161 | }
162 |
163 | public readString(property: string, deviceRoot?: string): string {
164 | var value = this.readProperty(property, deviceRoot);
165 | return String(value);
166 | }
167 |
168 | public readStringAsType(property: string, deviceRoot?: string): T {
169 | return this.readString(property, deviceRoot) as T;
170 | }
171 |
172 | public readStringArray(property: string, deviceRoot?: string): string[] {
173 | return this.readString(property, deviceRoot)
174 | .split(' ')
175 | .map((value: string) => value.replace(/^\[|\]$/g, ''));
176 | }
177 |
178 | public readStringArrayAsType(property: string, deviceRoot?: string): T[] {
179 | return this.readStringArray(property, deviceRoot) as T[];
180 | }
181 |
182 | public readStringSelector(property: string, deviceRoot?: string): string {
183 | var bracketedParts = this.readString(property, deviceRoot)
184 | .split(' ')
185 | .filter((value: string) => value.match(/^\[|\]$/g) != null);
186 |
187 | if (bracketedParts.length <= 0)
188 | return null;
189 |
190 | return bracketedParts[0].replace(/^\[|\]$/g, '');
191 | }
192 |
193 | public readProperty(property: string, deviceRoot?: string): any {
194 | if (!deviceRoot && !this.connected)
195 | throw new Error('You must be connected to a device before you can read from it. This error probably means that the target device was not found.');
196 |
197 | var rawValue: string;
198 | var propertyPath = this.constructPropertyPath(property, deviceRoot);
199 |
200 | try {
201 | rawValue = fs.readFileSync(propertyPath).toString();
202 | }
203 | catch (e) {
204 | throw new TraceError('There was an error while reading from the property file "' + propertyPath + '".', e);
205 | }
206 |
207 | rawValue = rawValue.trim();
208 | var numValue = Number(rawValue);
209 |
210 | if (isNaN(numValue))
211 | return rawValue;
212 | else
213 | return numValue;
214 | }
215 |
216 | public setProperty(property: string, value: any): any {
217 | if (!this.connected)
218 | throw new Error('You must be connected to a device before you can write to it. This error probably means that the target device was not found.');
219 |
220 | var propertyPath = this.constructPropertyPath(property);
221 |
222 | try {
223 | fs.writeFileSync(propertyPath, value.toString());
224 | }
225 | catch (e) {
226 | throw new TraceError('There was an error while writing to the property file "' + propertyPath + '".', e);
227 | }
228 | }
229 |
230 | public setNumber(property: string, value: number) {
231 | this.setProperty(property, value);
232 | }
233 |
234 | public setString(property: string, value: string) {
235 | this.setProperty(property, value);
236 | }
237 |
238 | public set(propertyDefs: any) {
239 | for (var key in propertyDefs) {
240 | this.setProperty(key, propertyDefs[key]);
241 | }
242 | }
243 |
244 | private updatePendingEventRequests() {
245 | this.pendingEventRequests = this.pendingEventRequests.filter(
246 | (eventRequest, index, arr) =>
247 | eventRequest.handleUpdate());
248 |
249 | this.updateEventTimerState();
250 | }
251 |
252 | private updateEventTimerState() {
253 | if (this.pendingEventRequests.length > 0 && this.eventTimerCancellationToken == null) {
254 | this.eventTimerCancellationToken = setInterval(() => this.updatePendingEventRequests(), Device.eventTimerInterval);
255 | }
256 | else if (this.pendingEventRequests.length <= 0 && this.eventTimerCancellationToken != null) {
257 | clearInterval(this.eventTimerCancellationToken);
258 | this.eventTimerCancellationToken = null;
259 | }
260 | }
261 |
262 | public registerEventCallback(
263 | callbackFunction: (err?: Error, userData?: any) => void,
264 | eventPredicate: (userData?: any) => boolean,
265 | firstTriggerOnly: boolean = false,
266 | userCallbackData?: any) {
267 |
268 | var newEventRequest: EventNotificationRequest = new EventNotificationRequest(
269 | (err?) => {
270 | callbackFunction(err, userCallbackData);
271 | }, eventPredicate, firstTriggerOnly, userCallbackData);
272 |
273 | this.pendingEventRequests.push(newEventRequest);
274 | this.updateEventTimerState();
275 | }
276 |
277 | public registerEventPromise(eventPredicate: (userData?: any) => boolean, userCallbackData?: any): Promise {
278 | if(Promise == null) {
279 | throw new Error("Promises are currently unavailable. Install the 'bluebird' package or use 'registerEventCallback(...)' instead.");
280 | }
281 |
282 | return new Promise((resolve, reject) => {
283 | this.registerEventCallback((err?) => {
284 | if (err)
285 | reject(err);
286 | else
287 | resolve(userCallbackData);
288 |
289 | }, eventPredicate, true, userCallbackData);
290 | });
291 | }
292 | }
293 |
294 | export class IndexedDevice extends Device {
295 | protected deviceIndexRegex = new RegExp("(\\d+)", "g");
296 |
297 | protected _deviceIndex: number = -1;
298 | get deviceIndex(): number {
299 | return this._deviceIndex;
300 | }
301 |
302 | constructor(driverTypeDirName: string, nameConvention?: string, targetAddress?: string, targetDriverName?: string | string[]) {
303 | super();
304 |
305 | var propertyConstraints: {[propertyName: string]: any} = {};
306 |
307 | if (targetAddress != undefined)
308 | propertyConstraints['address'] = targetAddress;
309 |
310 | if (targetDriverName != undefined)
311 | propertyConstraints['driver_name'] = [].concat(targetDriverName);
312 |
313 | this.connect(driverTypeDirName, nameConvention, propertyConstraints);
314 |
315 | if (this.connected) {
316 | var matches = this.deviceIndexRegex.exec(this.deviceDirName);
317 |
318 | if (matches != null && matches[0] != undefined) {
319 | this._deviceIndex = Number(matches[1]);
320 | }
321 | }
322 | }
323 | }
--------------------------------------------------------------------------------
/src/sensors.ts:
--------------------------------------------------------------------------------
1 | import IO = require('./io');
2 | import Device = IO.Device;
3 | import IndexedDevice = IO.IndexedDevice;
4 |
5 | export class SensorBase extends IndexedDevice {
6 | constructor(driverTypeDirName: string, nameConvention?: string, targetAddress?: string, targetDriverName?: string | string[]) {
7 | super(driverTypeDirName, nameConvention, targetAddress, targetDriverName);
8 | }
9 | }
10 |
11 | //~autogen generic-class-description classes.sensor>currentClass
12 | /**
13 | * The sensor class provides a uniform interface for using most of the
14 | * sensors available for the EV3. The various underlying device drivers will
15 | * create a `lego-sensor` device for interacting with the sensors.
16 | *
17 | * Sensors are primarily controlled by setting the `mode` and monitored by
18 | * reading the `value` attributes. Values can be converted to floating point
19 | * if needed by `value` / 10.0 ^ `decimals`.
20 | *
21 | * Since the name of the `sensor` device node does not correspond to the port
22 | * that a sensor is plugged in to, you must look at the `address` attribute if
23 | * you need to know which port a sensor is plugged in to. However, if you don't
24 | * have more than one sensor of each type, you can just look for a matching
25 | * `driver_name`. Then it will not matter which port a sensor is plugged in to - your
26 | * program will still work.
27 | */
28 | //~autogen
29 | export class Sensor extends SensorBase {
30 |
31 | constructor(port?: string, driverNames?: string[]| string) {
32 | //~autogen connect-super-call classes.sensor>currentClass "port,driverNames">extraParams
33 | super('lego-sensor', 'sensor(\\d*)', port,driverNames);
34 | //~autogen
35 |
36 | }
37 |
38 | public getValue(valueIndex: number): number {
39 | return this.readNumber("value" + valueIndex);
40 | }
41 |
42 | public getFloatValue(valueIndex: number): number {
43 | return this.getValue(valueIndex) / Math.pow(10, this.decimals);
44 | }
45 |
46 | //PROPERTIES
47 | //~autogen generic-get-set classes.sensor>currentClass
48 | /**
49 | * Returns the name of the port that the sensor is connected to, e.g. `ev3:in1`.
50 | * I2C sensors also include the I2C address (decimal), e.g. `ev3:in1:i2c8`.
51 | */
52 | get address(): string {
53 | return this.readString("address");
54 | }
55 |
56 | /**
57 | * Sends a command to the sensor.
58 | */
59 | set command(value: string) {
60 | this.setString("command", value);
61 | }
62 |
63 | /**
64 | * Returns a list of the valid commands for the sensor.
65 | * Returns -EOPNOTSUPP if no commands are supported.
66 | */
67 | get commands(): string[] {
68 | return this.readStringArray("commands");
69 | }
70 |
71 | /**
72 | * Returns the number of decimal places for the values in the `value`
73 | * attributes of the current mode.
74 | */
75 | get decimals(): number {
76 | return this.readNumber("decimals");
77 | }
78 |
79 | /**
80 | * Returns the name of the sensor device/driver. See the list of [supported
81 | * sensors] for a complete list of drivers.
82 | */
83 | get driverName(): string {
84 | return this.readString("driver_name");
85 | }
86 |
87 | /**
88 | * Returns the current mode. Writing one of the values returned by `modes`
89 | * sets the sensor to that mode.
90 | */
91 | get mode(): string {
92 | return this.readString("mode");
93 | }
94 | /**
95 | * Returns the current mode. Writing one of the values returned by `modes`
96 | * sets the sensor to that mode.
97 | */
98 | set mode(value: string) {
99 | this.setString("mode", value);
100 | }
101 |
102 | /**
103 | * Returns a list of the valid modes for the sensor.
104 | */
105 | get modes(): string[] {
106 | return this.readStringArray("modes");
107 | }
108 |
109 | /**
110 | * Returns the number of `value` attributes that will return a valid value
111 | * for the current mode.
112 | */
113 | get numValues(): number {
114 | return this.readNumber("num_values");
115 | }
116 |
117 | /**
118 | * Returns the units of the measured value for the current mode. May return
119 | * empty string
120 | */
121 | get units(): string {
122 | return this.readString("units");
123 | }
124 |
125 |
126 | //~autogen
127 | }
128 |
129 | //~autogen sensor-helper-classes
130 |
131 | /**
132 | * Touch Sensor
133 | */
134 | export class TouchSensor extends Sensor {
135 | constructor(port?: string) {
136 | super(port, ["lego-ev3-touch","lego-nxt-touch"]);
137 | }
138 |
139 |
140 | /**
141 | * A boolean indicating whether the current touch sensor is being
142 | * pressed.
143 | */
144 | get isPressed(): boolean {
145 | this.mode = 'TOUCH';
146 | return Boolean(this.getFloatValue(0));
147 | }
148 |
149 | }
150 |
151 | /**
152 | * LEGO EV3 color sensor.
153 | */
154 | export class ColorSensor extends Sensor {
155 | constructor(port?: string) {
156 | super(port, ["lego-ev3-color"]);
157 | }
158 |
159 |
160 | /**
161 | * Reflected light intensity as a percentage. Light on sensor is red.
162 | */
163 | get reflectedLightIntensity(): number {
164 | this.mode = 'COL-REFLECT';
165 | return Number(this.getFloatValue(0));
166 | }
167 |
168 | /**
169 | * Ambient light intensity. Light on sensor is dimly lit blue.
170 | */
171 | get ambientLightIntensity(): number {
172 | this.mode = 'COL-AMBIENT';
173 | return Number(this.getFloatValue(0));
174 | }
175 |
176 | /**
177 | * Color detected by the sensor, categorized by overall value.
178 | * - 0: No color
179 | * - 1: Black
180 | * - 2: Blue
181 | * - 3: Green
182 | * - 4: Yellow
183 | * - 5: Red
184 | * - 6: White
185 | * - 7: Brown
186 | */
187 | get color(): number {
188 | this.mode = 'COL-COLOR';
189 | return Number(this.getFloatValue(0));
190 | }
191 |
192 | /**
193 | * Red component of the detected color, in the range 0-1020.
194 | */
195 | get red(): number {
196 | this.mode = 'RGB-RAW';
197 | return Number(this.getFloatValue(0));
198 | }
199 |
200 | /**
201 | * Green component of the detected color, in the range 0-1020.
202 | */
203 | get green(): number {
204 | this.mode = 'RGB-RAW';
205 | return Number(this.getFloatValue(1));
206 | }
207 |
208 | /**
209 | * Blue component of the detected color, in the range 0-1020.
210 | */
211 | get blue(): number {
212 | this.mode = 'RGB-RAW';
213 | return Number(this.getFloatValue(2));
214 | }
215 |
216 | }
217 |
218 | /**
219 | * LEGO EV3 ultrasonic sensor.
220 | */
221 | export class UltrasonicSensor extends Sensor {
222 | constructor(port?: string) {
223 | super(port, ["lego-ev3-us","lego-nxt-us"]);
224 | }
225 |
226 |
227 | /**
228 | * Measurement of the distance detected by the sensor,
229 | * in centimeters.
230 | */
231 | get distanceCentimeters(): number {
232 | this.mode = 'US-DIST-CM';
233 | return Number(this.getFloatValue(0));
234 | }
235 |
236 | /**
237 | * Measurement of the distance detected by the sensor,
238 | * in inches.
239 | */
240 | get distanceInches(): number {
241 | this.mode = 'US-DIST-IN';
242 | return Number(this.getFloatValue(0));
243 | }
244 |
245 | /**
246 | * Value indicating whether another ultrasonic sensor could
247 | * be heard nearby.
248 | */
249 | get otherSensorPresent(): boolean {
250 | this.mode = 'US-LISTEN';
251 | return Boolean(this.getFloatValue(0));
252 | }
253 |
254 | }
255 |
256 | /**
257 | * LEGO EV3 gyro sensor.
258 | */
259 | export class GyroSensor extends Sensor {
260 | constructor(port?: string) {
261 | super(port, ["lego-ev3-gyro"]);
262 | }
263 |
264 |
265 | /**
266 | * The number of degrees that the sensor has been rotated
267 | * since it was put into this mode.
268 | */
269 | get angle(): number {
270 | this.mode = 'GYRO-ANG';
271 | return Number(this.getFloatValue(0));
272 | }
273 |
274 | /**
275 | * The rate at which the sensor is rotating, in degrees/second.
276 | */
277 | get rate(): number {
278 | this.mode = 'GYRO-RATE';
279 | return Number(this.getFloatValue(0));
280 | }
281 |
282 | }
283 |
284 | /**
285 | * LEGO EV3 infrared sensor.
286 | */
287 | export class InfraredSensor extends Sensor {
288 | constructor(port?: string) {
289 | super(port, ["lego-ev3-ir"]);
290 | }
291 |
292 |
293 | /**
294 | * A measurement of the distance between the sensor and the remote,
295 | * as a percentage. 100% is approximately 70cm/27in.
296 | */
297 | get proximity(): number {
298 | this.mode = 'IR-PROX';
299 | return Number(this.getFloatValue(0));
300 | }
301 |
302 | }
303 |
304 | /**
305 | * LEGO NXT Sound Sensor
306 | */
307 | export class SoundSensor extends Sensor {
308 | constructor(port?: string) {
309 | super(port, ["lego-nxt-sound"]);
310 | }
311 |
312 |
313 | /**
314 | * A measurement of the measured sound pressure level, as a
315 | * percent. Uses a flat weighting.
316 | */
317 | get soundPressure(): number {
318 | this.mode = 'DB';
319 | return Number(this.getFloatValue(0));
320 | }
321 |
322 | /**
323 | * A measurement of the measured sound pressure level, as a
324 | * percent. Uses A-weighting, which focuses on levels up to 55 dB.
325 | */
326 | get soundPressureLow(): number {
327 | this.mode = 'DBA';
328 | return Number(this.getFloatValue(0));
329 | }
330 |
331 | }
332 |
333 | /**
334 | * LEGO NXT Light Sensor
335 | */
336 | export class LightSensor extends Sensor {
337 | constructor(port?: string) {
338 | super(port, ["lego-nxt-light"]);
339 | }
340 |
341 |
342 | /**
343 | * A measurement of the reflected light intensity, as a percentage.
344 | */
345 | get reflectedLightIntensity(): number {
346 | this.mode = 'REFLECT';
347 | return Number(this.getFloatValue(0));
348 | }
349 |
350 | /**
351 | * A measurement of the ambient light intensity, as a percentage.
352 | */
353 | get ambientLightIntensity(): number {
354 | this.mode = 'AMBIENT';
355 | return Number(this.getFloatValue(0));
356 | }
357 |
358 | }
359 |
360 | //~autogen
361 |
362 | //~autogen generic-class-description classes.i2cSensor>currentClass
363 | /**
364 | * A generic interface to control I2C-type EV3 sensors.
365 | */
366 | //~autogen
367 | export class I2CSensor extends Sensor {
368 | constructor(port?: string, driverNames?: string[]) {
369 | super(port, driverNames);
370 | }
371 |
372 | //~autogen generic-get-set classes.i2cSensor>currentClass
373 | /**
374 | * Returns the firmware version of the sensor if available. Currently only
375 | * I2C/NXT sensors support this.
376 | */
377 | get fwVersion(): string {
378 | return this.readString("fw_version");
379 | }
380 |
381 | /**
382 | * Returns the polling period of the sensor in milliseconds. Writing sets the
383 | * polling period. Setting to 0 disables polling. Minimum value is hard
384 | * coded as 50 msec. Returns -EOPNOTSUPP if changing polling is not supported.
385 | * Currently only I2C/NXT sensors support changing the polling period.
386 | */
387 | get pollMs(): number {
388 | return this.readNumber("poll_ms");
389 | }
390 | /**
391 | * Returns the polling period of the sensor in milliseconds. Writing sets the
392 | * polling period. Setting to 0 disables polling. Minimum value is hard
393 | * coded as 50 msec. Returns -EOPNOTSUPP if changing polling is not supported.
394 | * Currently only I2C/NXT sensors support changing the polling period.
395 | */
396 | set pollMs(value: number) {
397 | this.setNumber("poll_ms", value);
398 | }
399 |
400 |
401 | //~autogen
402 | }
--------------------------------------------------------------------------------
/src/extras.ts:
--------------------------------------------------------------------------------
1 | //~autogen autogen-header
2 | // Sections of the following code were auto-generated based on spec v1.2.0.
3 |
4 | //~autogen
5 |
6 | import IO = require('./io');
7 | import Device = IO.Device;
8 |
9 | //~autogen generic-class-description classes.powerSupply>currentClass
10 | /**
11 | * A generic interface to read data from the system's power_supply class.
12 | * Uses the built-in legoev3-battery if none is specified.
13 | */
14 | //~autogen
15 | export class PowerSupply extends Device {
16 | public deviceName;
17 |
18 | constructor(deviceName: string) {
19 | super();
20 |
21 | var deviceConstraints = { };
22 | if (deviceName == undefined)
23 | deviceConstraints["scope"] = "System";
24 | else
25 | this.deviceName = deviceName;
26 |
27 | this.connect('power_supply', deviceName, deviceConstraints);
28 | }
29 |
30 | //PROPERTIES
31 | //~autogen generic-get-set classes.powerSupply>currentClass
32 | /**
33 | * The measured current that the battery is supplying (in microamps)
34 | */
35 | get measuredCurrent(): number {
36 | return this.readNumber("current_now");
37 | }
38 |
39 | /**
40 | * The measured voltage that the battery is supplying (in microvolts)
41 | */
42 | get measuredVoltage(): number {
43 | return this.readNumber("voltage_now");
44 | }
45 |
46 | /**
47 | */
48 | get maxVoltage(): number {
49 | return this.readNumber("voltage_max_design");
50 | }
51 |
52 | /**
53 | */
54 | get minVoltage(): number {
55 | return this.readNumber("voltage_min_design");
56 | }
57 |
58 | /**
59 | */
60 | get technology(): string {
61 | return this.readString("technology");
62 | }
63 |
64 | /**
65 | */
66 | get type(): string {
67 | return this.readString("type");
68 | }
69 |
70 |
71 | //~autogen
72 |
73 | get voltageVolts(): number {
74 | return this.measuredVoltage / 1000000;
75 | }
76 |
77 | get currentAmps(): number {
78 | return this.measuredCurrent / 1000000;
79 | }
80 |
81 | }
82 |
83 | //~autogen generic-class-description classes.led>currentClass
84 | /**
85 | * Any device controlled by the generic LED driver.
86 | * See https://www.kernel.org/doc/Documentation/leds/leds-class.txt
87 | * for more details.
88 | */
89 | //~autogen
90 | export class LED extends Device {
91 | public deviceName: string;
92 |
93 | constructor(deviceName: string) {
94 | super();
95 |
96 | this.deviceName = deviceName;
97 |
98 | this.connect('leds', deviceName);
99 | }
100 |
101 | //PROPERTIES
102 |
103 | //~autogen generic-get-set classes.led>currentClass
104 | /**
105 | * Returns the maximum allowable brightness value.
106 | */
107 | get maxBrightness(): number {
108 | return this.readNumber("max_brightness");
109 | }
110 |
111 | /**
112 | * Sets the brightness level. Possible values are from 0 to `max_brightness`.
113 | */
114 | get brightness(): number {
115 | return this.readNumber("brightness");
116 | }
117 | /**
118 | * Sets the brightness level. Possible values are from 0 to `max_brightness`.
119 | */
120 | set brightness(value: number) {
121 | this.setNumber("brightness", value);
122 | }
123 |
124 | /**
125 | * Returns a list of available triggers.
126 | */
127 | get triggers(): string[] {
128 | return this.readStringArray("trigger");
129 | }
130 |
131 | /**
132 | * Sets the led trigger. A trigger
133 | * is a kernel based source of led events. Triggers can either be simple or
134 | * complex. A simple trigger isn't configurable and is designed to slot into
135 | * existing subsystems with minimal additional code. Examples are the `ide-disk` and
136 | * `nand-disk` triggers.
137 | *
138 | * Complex triggers whilst available to all LEDs have LED specific
139 | * parameters and work on a per LED basis. The `timer` trigger is an example.
140 | * The `timer` trigger will periodically change the LED brightness between
141 | * 0 and the current brightness setting. The `on` and `off` time can
142 | * be specified via `delay_{on,off}` attributes in milliseconds.
143 | * You can change the brightness value of a LED independently of the timer
144 | * trigger. However, if you set the brightness value to 0 it will
145 | * also disable the `timer` trigger.
146 | */
147 | get trigger(): string {
148 | return this.readStringSelector("trigger");
149 | }
150 | /**
151 | * Sets the led trigger. A trigger
152 | * is a kernel based source of led events. Triggers can either be simple or
153 | * complex. A simple trigger isn't configurable and is designed to slot into
154 | * existing subsystems with minimal additional code. Examples are the `ide-disk` and
155 | * `nand-disk` triggers.
156 | *
157 | * Complex triggers whilst available to all LEDs have LED specific
158 | * parameters and work on a per LED basis. The `timer` trigger is an example.
159 | * The `timer` trigger will periodically change the LED brightness between
160 | * 0 and the current brightness setting. The `on` and `off` time can
161 | * be specified via `delay_{on,off}` attributes in milliseconds.
162 | * You can change the brightness value of a LED independently of the timer
163 | * trigger. However, if you set the brightness value to 0 it will
164 | * also disable the `timer` trigger.
165 | */
166 | set trigger(value: string) {
167 | this.setString("trigger", value);
168 | }
169 |
170 | /**
171 | * The `timer` trigger will periodically change the LED brightness between
172 | * 0 and the current brightness setting. The `on` time can
173 | * be specified via `delay_on` attribute in milliseconds.
174 | */
175 | get delayOn(): number {
176 | return this.readNumber("delay_on");
177 | }
178 | /**
179 | * The `timer` trigger will periodically change the LED brightness between
180 | * 0 and the current brightness setting. The `on` time can
181 | * be specified via `delay_on` attribute in milliseconds.
182 | */
183 | set delayOn(value: number) {
184 | this.setNumber("delay_on", value);
185 | }
186 |
187 | /**
188 | * The `timer` trigger will periodically change the LED brightness between
189 | * 0 and the current brightness setting. The `off` time can
190 | * be specified via `delay_off` attribute in milliseconds.
191 | */
192 | get delayOff(): number {
193 | return this.readNumber("delay_off");
194 | }
195 | /**
196 | * The `timer` trigger will periodically change the LED brightness between
197 | * 0 and the current brightness setting. The `off` time can
198 | * be specified via `delay_off` attribute in milliseconds.
199 | */
200 | set delayOff(value: number) {
201 | this.setNumber("delay_off", value);
202 | }
203 |
204 |
205 | //~autogen
206 |
207 | /**
208 | * Sets the LED's brightness to the given percent (0-1) of the max value.
209 | */
210 | public get brightnessPct(): number{
211 | return this.brightness / this.maxBrightness;
212 | }
213 |
214 | public set brightnessPct(brightnessPct: number) {
215 | this.brightness = Math.round(this.maxBrightness * brightnessPct);
216 | }
217 |
218 | /**
219 | * Sets brightness to maximum value, turning the LED on
220 | */
221 | public on() {
222 | this.brightness = this.maxBrightness;
223 | }
224 |
225 | /**
226 | * Sets brightness to 0, turning the LED off
227 | */
228 | public off() {
229 | this.brightness = 0;
230 | }
231 |
232 | /**
233 | * Flashes the LED on a timer using the given intervals.
234 | */
235 | public flash(onInterval: number, offInterval: number) {
236 | this.delayOn = onInterval;
237 | this.delayOff = offInterval;
238 | this.trigger = "timer";
239 | }
240 | }
241 |
242 | export class LEDGroup {
243 | private leds: LED[];
244 |
245 | public constructor(...leds: (string | LED)[]) {
246 | this.leds = [];
247 | for(var ledObj of leds) {
248 | if(typeof ledObj == "string") {
249 | var newLed = new LED(ledObj);
250 | this.leds.push(newLed);
251 | }
252 | else {
253 | this.leds.push(ledObj);
254 | }
255 | }
256 | }
257 |
258 | public get isConnected(): boolean {
259 | return this.leds.every(function(led: LED, index: number, wholeArray: LED[]) {
260 | return led.connected;
261 | });
262 | }
263 |
264 | /**
265 | * Sets the brightness percentages for each LED in the group to the given percentages,
266 | * scaling each according to the given percent power scale if provided.
267 | *
268 | * @param colorCombination The percent powers to use for each LED, applied to the corresponding index in the LED group.
269 | * @param pctPower The scale factor to multiply each value by. Leave undefined or null to default to `1`.
270 | */
271 | public setColor(colorCombination: number[], pctPower: number) {
272 | if(colorCombination.length !== this.leds.length) {
273 | throw new Error("The given color values had either too few or too many numbers for this LED group."
274 | + " Expected length: " + this.leds.length + "; Given length: " + colorCombination.length);
275 | }
276 |
277 | if(pctPower == undefined || pctPower == null) {
278 | pctPower = 1;
279 | }
280 |
281 | for(var ledIndex = 0; ledIndex < this.leds.length; ledIndex++) {
282 | this.leds[ledIndex].brightnessPct = pctPower * colorCombination[ledIndex];
283 | }
284 | }
285 |
286 | /**
287 | * Sets the given property names to the corresponding values on each LED in the group.
288 | *
289 | * If the requested property does not exist on the LED object, the property is skipped.
290 | *
291 | * @param props A hash containing the key-value pairs of properties to set.
292 | */
293 | public setProps(props: { [propName: string]: any}) {
294 | for(var led of this.leds) {
295 | for(var prop in Object.keys(props)) {
296 | if(Object.keys(led).indexOf(prop) != -1) {
297 | led[prop] = props[prop];
298 | }
299 | }
300 | }
301 | }
302 |
303 | public allOn() {
304 | for(var led of this.leds) {
305 | led.on();
306 | }
307 | }
308 |
309 | public allOff() {
310 | for(var led of this.leds) {
311 | led.off();
312 | }
313 | }
314 | }
315 |
316 | //~autogen generic-class-description classes.legoPort>currentClass
317 | /**
318 | * The `lego-port` class provides an interface for working with input and
319 | * output ports that are compatible with LEGO MINDSTORMS RCX/NXT/EV3, LEGO
320 | * WeDo and LEGO Power Functions sensors and motors. Supported devices include
321 | * the LEGO MINDSTORMS EV3 Intelligent Brick, the LEGO WeDo USB hub and
322 | * various sensor multiplexers from 3rd party manufacturers.
323 | *
324 | * Some types of ports may have multiple modes of operation. For example, the
325 | * input ports on the EV3 brick can communicate with sensors using UART, I2C
326 | * or analog validate signals - but not all at the same time. Therefore there
327 | * are multiple modes available to connect to the different types of sensors.
328 | *
329 | * In most cases, ports are able to automatically detect what type of sensor
330 | * or motor is connected. In some cases though, this must be manually specified
331 | * using the `mode` and `set_device` attributes. The `mode` attribute affects
332 | * how the port communicates with the connected device. For example the input
333 | * ports on the EV3 brick can communicate using UART, I2C or analog voltages,
334 | * but not all at the same time, so the mode must be set to the one that is
335 | * appropriate for the connected sensor. The `set_device` attribute is used to
336 | * specify the exact type of sensor that is connected. Note: the mode must be
337 | * correctly set before setting the sensor type.
338 | *
339 | * Ports can be found at `/sys/class/lego-port/port` where `` is
340 | * incremented each time a new port is registered. Note: The number is not
341 | * related to the actual port at all - use the `address` attribute to find
342 | * a specific port.
343 | */
344 | //~autogen
345 | export class LegoPort extends Device {
346 | protected _deviceIndex: number = -1;
347 | get deviceIndex(): number {
348 | return this._deviceIndex;
349 | }
350 |
351 | constructor(port: string) {
352 | super();
353 |
354 | this.connect('lego-port', 'port(\d*)', {
355 | port_name: port
356 | });
357 | }
358 |
359 | //PROPERTIES
360 | //~autogen generic-get-set classes.legoPort>currentClass
361 | /**
362 | * Returns the name of the port. See individual driver documentation for
363 | * the name that will be returned.
364 | */
365 | get address(): string {
366 | return this.readString("address");
367 | }
368 |
369 | /**
370 | * Returns the name of the driver that loaded this device. You can find the
371 | * complete list of drivers in the [list of port drivers].
372 | */
373 | get driverName(): string {
374 | return this.readString("driver_name");
375 | }
376 |
377 | /**
378 | * Returns a list of the available modes of the port.
379 | */
380 | get modes(): string[] {
381 | return this.readStringArray("modes");
382 | }
383 |
384 | /**
385 | * Reading returns the currently selected mode. Writing sets the mode.
386 | * Generally speaking when the mode changes any sensor or motor devices
387 | * associated with the port will be removed new ones loaded, however this
388 | * this will depend on the individual driver implementing this class.
389 | */
390 | get mode(): string {
391 | return this.readString("mode");
392 | }
393 | /**
394 | * Reading returns the currently selected mode. Writing sets the mode.
395 | * Generally speaking when the mode changes any sensor or motor devices
396 | * associated with the port will be removed new ones loaded, however this
397 | * this will depend on the individual driver implementing this class.
398 | */
399 | set mode(value: string) {
400 | this.setString("mode", value);
401 | }
402 |
403 | /**
404 | * For modes that support it, writing the name of a driver will cause a new
405 | * device to be registered for that driver and attached to this port. For
406 | * example, since NXT/Analog sensors cannot be auto-detected, you must use
407 | * this attribute to load the correct driver. Returns -EOPNOTSUPP if setting a
408 | * device is not supported.
409 | */
410 | set setDevice(value: string) {
411 | this.setString("set_device", value);
412 | }
413 |
414 | /**
415 | * In most cases, reading status will return the same value as `mode`. In
416 | * cases where there is an `auto` mode additional values may be returned,
417 | * such as `no-device` or `error`. See individual port driver documentation
418 | * for the full list of possible values.
419 | */
420 | get status(): string {
421 | return this.readString("status");
422 | }
423 |
424 |
425 | //~autogen
426 | }
--------------------------------------------------------------------------------
/src/motors.ts:
--------------------------------------------------------------------------------
1 | //~autogen autogen-header
2 | // Sections of the following code were auto-generated based on spec v1.2.0.
3 |
4 | //~autogen
5 |
6 | import IO = require('./io');
7 | import Device = IO.Device;
8 | import IndexedDevice = IO.IndexedDevice;
9 |
10 | //~autogen def-string-literal-types classes.motor>currentClass
11 | export module Motor {
12 |
13 | export type CommandValue = 'run-forever' | 'run-to-abs-pos' | 'run-to-rel-pos' | 'run-timed' | 'run-direct' | 'stop' | 'reset';
14 | export type EncoderPolarityValue = 'normal' | 'inversed';
15 | export type PolarityValue = 'normal' | 'inversed';
16 | export type StateValue = 'running' | 'ramping' | 'holding' | 'overloaded' | 'stalled';
17 | export type StopActionValue = 'coast' | 'brake' | 'hold';
18 | }
19 | //~autogen
20 |
21 | //~autogen def-string-literal-types classes.dcMotor>currentClass
22 | export module DcMotor {
23 |
24 | export type CommandValue = 'run-forever' | 'run-timed' | 'run-direct' | 'stop';
25 | export type PolarityValue = 'normal' | 'inversed';
26 | export type StopActionValue = 'coast' | 'brake';
27 | }
28 | //~autogen
29 |
30 | //~autogen def-string-literal-types classes.servoMotor>currentClass
31 | export module ServoMotor {
32 |
33 | export type CommandValue = 'run' | 'float';
34 | export type PolarityValue = 'normal' | 'inversed';
35 | }
36 | //~autogen
37 |
38 | export class MotorBase extends IndexedDevice {
39 | constructor(driverTypeDirName: string, nameConvention?: string, targetAddress?: string, targetDriverName?: string | string[]) {
40 | super(driverTypeDirName, nameConvention, targetAddress, targetDriverName);
41 | }
42 | }
43 |
44 | //~autogen generic-class-description classes.motor>currentClass
45 | /**
46 | * The motor class provides a uniform interface for using motors with
47 | * positional and directional feedback such as the EV3 and NXT motors.
48 | * This feedback allows for precise control of the motors. This is the
49 | * most common type of motor, so we just call it `motor`.
50 | *
51 | * The way to configure a motor is to set the '_sp' attributes when
52 | * calling a command or before. Only in 'run_direct' mode attribute
53 | * changes are processed immediately, in the other modes they only
54 | * take place when a new command is issued.
55 | */
56 | //~autogen
57 | export class Motor extends MotorBase {
58 |
59 | public constructor(port?: string, targetDriverName?: string[] | string) {
60 | //~autogen connect-super-call classes.motor>currentClass "port,targetDriverName">extraParams "true">omitNameConvention
61 | super('tacho-motor', null, port,targetDriverName);
62 | //~autogen
63 | }
64 |
65 | //~autogen property-value-constants classes.motor>currentClass
66 |
67 | public get commandValues(): { runForever: Motor.CommandValue, runToAbsPos: Motor.CommandValue, runToRelPos: Motor.CommandValue, runTimed: Motor.CommandValue, runDirect: Motor.CommandValue, stop: Motor.CommandValue, reset: Motor.CommandValue } {
68 | return {
69 | runForever: "run-forever",
70 | runToAbsPos: "run-to-abs-pos",
71 | runToRelPos: "run-to-rel-pos",
72 | runTimed: "run-timed",
73 | runDirect: "run-direct",
74 | stop: "stop",
75 | reset: "reset"
76 | }
77 | }
78 |
79 | public get encoderPolarityValues(): { normal: Motor.EncoderPolarityValue, inversed: Motor.EncoderPolarityValue } {
80 | return {
81 | normal: "normal",
82 | inversed: "inversed"
83 | }
84 | }
85 |
86 | public get polarityValues(): { normal: Motor.PolarityValue, inversed: Motor.PolarityValue } {
87 | return {
88 | normal: "normal",
89 | inversed: "inversed"
90 | }
91 | }
92 |
93 | public get stateValues(): { running: Motor.StateValue, ramping: Motor.StateValue, holding: Motor.StateValue, overloaded: Motor.StateValue, stalled: Motor.StateValue } {
94 | return {
95 | running: "running",
96 | ramping: "ramping",
97 | holding: "holding",
98 | overloaded: "overloaded",
99 | stalled: "stalled"
100 | }
101 | }
102 |
103 | public get stopActionValues(): { coast: Motor.StopActionValue, brake: Motor.StopActionValue, hold: Motor.StopActionValue } {
104 | return {
105 | coast: "coast",
106 | brake: "brake",
107 | hold: "hold"
108 | }
109 | }
110 |
111 | //~autogen
112 |
113 | public reset() {
114 | this.command = this.commandValues.reset;
115 | }
116 |
117 | public stop() {
118 | this.command = this.commandValues.stop;
119 | }
120 |
121 | //PROPERTIES
122 | //~autogen generic-get-set classes.motor>currentClass
123 | /**
124 | * Returns the name of the port that this motor is connected to.
125 | */
126 | get address(): string {
127 | return this.readString("address");
128 | }
129 |
130 | /**
131 | * Sends a command to the motor controller. See `commands` for a list of
132 | * possible values.
133 | */
134 | set command(value: Motor.CommandValue) {
135 | this.setString("command", value);
136 | }
137 |
138 | /**
139 | * Returns a list of commands that are supported by the motor
140 | * controller. Possible values are `run-forever`, `run-to-abs-pos`, `run-to-rel-pos`,
141 | * `run-timed`, `run-direct`, `stop` and `reset`. Not all commands may be supported.
142 | *
143 | * - `run-forever` will cause the motor to run until another command is sent.
144 | * - `run-to-abs-pos` will run to an absolute position specified by `position_sp`
145 | * and then stop using the action specified in `stop_action`.
146 | * - `run-to-rel-pos` will run to a position relative to the current `position` value.
147 | * The new position will be current `position` + `position_sp`. When the new
148 | * position is reached, the motor will stop using the action specified by `stop_action`.
149 | * - `run-timed` will run the motor for the amount of time specified in `time_sp`
150 | * and then stop the motor using the action specified by `stop_action`.
151 | * - `run-direct` will run the motor at the duty cycle specified by `duty_cycle_sp`.
152 | * Unlike other run commands, changing `duty_cycle_sp` while running *will*
153 | * take effect immediately.
154 | * - `stop` will stop any of the run commands before they are complete using the
155 | * action specified by `stop_action`.
156 | * - `reset` will reset all of the motor parameter attributes to their default value.
157 | * This will also have the effect of stopping the motor.
158 | */
159 | get commands(): string[] {
160 | return this.readStringArray("commands");
161 | }
162 |
163 | /**
164 | * Returns the number of tacho counts in one rotation of the motor. Tacho counts
165 | * are used by the position and speed attributes, so you can use this value
166 | * to convert rotations or degrees to tacho counts. (rotation motors only)
167 | */
168 | get countPerRot(): number {
169 | return this.readNumber("count_per_rot");
170 | }
171 |
172 | /**
173 | * Returns the number of tacho counts in one meter of travel of the motor. Tacho
174 | * counts are used by the position and speed attributes, so you can use this
175 | * value to convert from distance to tacho counts. (linear motors only)
176 | */
177 | get countPerM(): number {
178 | return this.readNumber("count_per_m");
179 | }
180 |
181 | /**
182 | * Returns the name of the driver that provides this tacho motor device.
183 | */
184 | get driverName(): string {
185 | return this.readString("driver_name");
186 | }
187 |
188 | /**
189 | * Returns the current duty cycle of the motor. Units are percent. Values
190 | * are -100 to 100.
191 | */
192 | get dutyCycle(): number {
193 | return this.readNumber("duty_cycle");
194 | }
195 |
196 | /**
197 | * Writing sets the duty cycle setpoint. Reading returns the current value.
198 | * Units are in percent. Valid values are -100 to 100. A negative value causes
199 | * the motor to rotate in reverse.
200 | */
201 | get dutyCycleSp(): number {
202 | return this.readNumber("duty_cycle_sp");
203 | }
204 | /**
205 | * Writing sets the duty cycle setpoint. Reading returns the current value.
206 | * Units are in percent. Valid values are -100 to 100. A negative value causes
207 | * the motor to rotate in reverse.
208 | */
209 | set dutyCycleSp(value: number) {
210 | this.setNumber("duty_cycle_sp", value);
211 | }
212 |
213 | /**
214 | * Returns the number of tacho counts in the full travel of the motor. When
215 | * combined with the `count_per_m` atribute, you can use this value to
216 | * calculate the maximum travel distance of the motor. (linear motors only)
217 | */
218 | get fullTravelCount(): number {
219 | return this.readNumber("full_travel_count");
220 | }
221 |
222 | /**
223 | * Sets the polarity of the motor. With `normal` polarity, a positive duty
224 | * cycle will cause the motor to rotate clockwise. With `inversed` polarity,
225 | * a positive duty cycle will cause the motor to rotate counter-clockwise.
226 | * Valid values are `normal` and `inversed`.
227 | */
228 | get polarity(): Motor.PolarityValue {
229 | return this.readStringAsType("polarity");
230 | }
231 | /**
232 | * Sets the polarity of the motor. With `normal` polarity, a positive duty
233 | * cycle will cause the motor to rotate clockwise. With `inversed` polarity,
234 | * a positive duty cycle will cause the motor to rotate counter-clockwise.
235 | * Valid values are `normal` and `inversed`.
236 | */
237 | set polarity(value: Motor.PolarityValue) {
238 | this.setString("polarity", value);
239 | }
240 |
241 | /**
242 | * Returns the current position of the motor in pulses of the rotary
243 | * encoder. When the motor rotates clockwise, the position will increase.
244 | * Likewise, rotating counter-clockwise causes the position to decrease.
245 | * Writing will set the position to that value.
246 | */
247 | get position(): number {
248 | return this.readNumber("position");
249 | }
250 | /**
251 | * Returns the current position of the motor in pulses of the rotary
252 | * encoder. When the motor rotates clockwise, the position will increase.
253 | * Likewise, rotating counter-clockwise causes the position to decrease.
254 | * Writing will set the position to that value.
255 | */
256 | set position(value: number) {
257 | this.setNumber("position", value);
258 | }
259 |
260 | /**
261 | * The proportional constant for the position PID.
262 | */
263 | get positionP(): number {
264 | return this.readNumber("hold_pid/Kp");
265 | }
266 | /**
267 | * The proportional constant for the position PID.
268 | */
269 | set positionP(value: number) {
270 | this.setNumber("hold_pid/Kp", value);
271 | }
272 |
273 | /**
274 | * The integral constant for the position PID.
275 | */
276 | get positionI(): number {
277 | return this.readNumber("hold_pid/Ki");
278 | }
279 | /**
280 | * The integral constant for the position PID.
281 | */
282 | set positionI(value: number) {
283 | this.setNumber("hold_pid/Ki", value);
284 | }
285 |
286 | /**
287 | * The derivative constant for the position PID.
288 | */
289 | get positionD(): number {
290 | return this.readNumber("hold_pid/Kd");
291 | }
292 | /**
293 | * The derivative constant for the position PID.
294 | */
295 | set positionD(value: number) {
296 | this.setNumber("hold_pid/Kd", value);
297 | }
298 |
299 | /**
300 | * Writing specifies the target position for the `run-to-abs-pos` and `run-to-rel-pos`
301 | * commands. Reading returns the current value. Units are in tacho counts. You
302 | * can use the value returned by `counts_per_rot` to convert tacho counts to/from
303 | * rotations or degrees.
304 | */
305 | get positionSp(): number {
306 | return this.readNumber("position_sp");
307 | }
308 | /**
309 | * Writing specifies the target position for the `run-to-abs-pos` and `run-to-rel-pos`
310 | * commands. Reading returns the current value. Units are in tacho counts. You
311 | * can use the value returned by `counts_per_rot` to convert tacho counts to/from
312 | * rotations or degrees.
313 | */
314 | set positionSp(value: number) {
315 | this.setNumber("position_sp", value);
316 | }
317 |
318 | /**
319 | * Returns the maximum value that is accepted by the `speed_sp` attribute. This
320 | * may be slightly different than the maximum speed that a particular motor can
321 | * reach - it's the maximum theoretical speed.
322 | */
323 | get maxSpeed(): number {
324 | return this.readNumber("max_speed");
325 | }
326 |
327 | /**
328 | * Returns the current motor speed in tacho counts per second. Note, this is
329 | * not necessarily degrees (although it is for LEGO motors). Use the `count_per_rot`
330 | * attribute to convert this value to RPM or deg/sec.
331 | */
332 | get speed(): number {
333 | return this.readNumber("speed");
334 | }
335 |
336 | /**
337 | * Writing sets the target speed in tacho counts per second used for all `run-*`
338 | * commands except `run-direct`. Reading returns the current value. A negative
339 | * value causes the motor to rotate in reverse with the exception of `run-to-*-pos`
340 | * commands where the sign is ignored. Use the `count_per_rot` attribute to convert
341 | * RPM or deg/sec to tacho counts per second. Use the `count_per_m` attribute to
342 | * convert m/s to tacho counts per second.
343 | */
344 | get speedSp(): number {
345 | return this.readNumber("speed_sp");
346 | }
347 | /**
348 | * Writing sets the target speed in tacho counts per second used for all `run-*`
349 | * commands except `run-direct`. Reading returns the current value. A negative
350 | * value causes the motor to rotate in reverse with the exception of `run-to-*-pos`
351 | * commands where the sign is ignored. Use the `count_per_rot` attribute to convert
352 | * RPM or deg/sec to tacho counts per second. Use the `count_per_m` attribute to
353 | * convert m/s to tacho counts per second.
354 | */
355 | set speedSp(value: number) {
356 | this.setNumber("speed_sp", value);
357 | }
358 |
359 | /**
360 | * Writing sets the ramp up setpoint. Reading returns the current value. Units
361 | * are in milliseconds and must be positive. When set to a non-zero value, the
362 | * motor speed will increase from 0 to 100% of `max_speed` over the span of this
363 | * setpoint. The actual ramp time is the ratio of the difference between the
364 | * `speed_sp` and the current `speed` and max_speed multiplied by `ramp_up_sp`.
365 | */
366 | get rampUpSp(): number {
367 | return this.readNumber("ramp_up_sp");
368 | }
369 | /**
370 | * Writing sets the ramp up setpoint. Reading returns the current value. Units
371 | * are in milliseconds and must be positive. When set to a non-zero value, the
372 | * motor speed will increase from 0 to 100% of `max_speed` over the span of this
373 | * setpoint. The actual ramp time is the ratio of the difference between the
374 | * `speed_sp` and the current `speed` and max_speed multiplied by `ramp_up_sp`.
375 | */
376 | set rampUpSp(value: number) {
377 | this.setNumber("ramp_up_sp", value);
378 | }
379 |
380 | /**
381 | * Writing sets the ramp down setpoint. Reading returns the current value. Units
382 | * are in milliseconds and must be positive. When set to a non-zero value, the
383 | * motor speed will decrease from 0 to 100% of `max_speed` over the span of this
384 | * setpoint. The actual ramp time is the ratio of the difference between the
385 | * `speed_sp` and the current `speed` and max_speed multiplied by `ramp_down_sp`.
386 | */
387 | get rampDownSp(): number {
388 | return this.readNumber("ramp_down_sp");
389 | }
390 | /**
391 | * Writing sets the ramp down setpoint. Reading returns the current value. Units
392 | * are in milliseconds and must be positive. When set to a non-zero value, the
393 | * motor speed will decrease from 0 to 100% of `max_speed` over the span of this
394 | * setpoint. The actual ramp time is the ratio of the difference between the
395 | * `speed_sp` and the current `speed` and max_speed multiplied by `ramp_down_sp`.
396 | */
397 | set rampDownSp(value: number) {
398 | this.setNumber("ramp_down_sp", value);
399 | }
400 |
401 | /**
402 | * The proportional constant for the speed regulation PID.
403 | */
404 | get speedP(): number {
405 | return this.readNumber("speed_pid/Kp");
406 | }
407 | /**
408 | * The proportional constant for the speed regulation PID.
409 | */
410 | set speedP(value: number) {
411 | this.setNumber("speed_pid/Kp", value);
412 | }
413 |
414 | /**
415 | * The integral constant for the speed regulation PID.
416 | */
417 | get speedI(): number {
418 | return this.readNumber("speed_pid/Ki");
419 | }
420 | /**
421 | * The integral constant for the speed regulation PID.
422 | */
423 | set speedI(value: number) {
424 | this.setNumber("speed_pid/Ki", value);
425 | }
426 |
427 | /**
428 | * The derivative constant for the speed regulation PID.
429 | */
430 | get speedD(): number {
431 | return this.readNumber("speed_pid/Kd");
432 | }
433 | /**
434 | * The derivative constant for the speed regulation PID.
435 | */
436 | set speedD(value: number) {
437 | this.setNumber("speed_pid/Kd", value);
438 | }
439 |
440 | /**
441 | * Reading returns a list of state flags. Possible flags are
442 | * `running`, `ramping`, `holding`, `overloaded` and `stalled`.
443 | */
444 | get state(): Motor.StateValue[] {
445 | return this.readStringArrayAsType("state");
446 | }
447 |
448 | /**
449 | * Reading returns the current stop action. Writing sets the stop action.
450 | * The value determines the motors behavior when `command` is set to `stop`.
451 | * Also, it determines the motors behavior when a run command completes. See
452 | * `stop_actions` for a list of possible values.
453 | */
454 | get stopAction(): Motor.StopActionValue {
455 | return this.readStringAsType("stop_action");
456 | }
457 | /**
458 | * Reading returns the current stop action. Writing sets the stop action.
459 | * The value determines the motors behavior when `command` is set to `stop`.
460 | * Also, it determines the motors behavior when a run command completes. See
461 | * `stop_actions` for a list of possible values.
462 | */
463 | set stopAction(value: Motor.StopActionValue) {
464 | this.setString("stop_action", value);
465 | }
466 |
467 | /**
468 | * Returns a list of stop actions supported by the motor controller.
469 | * Possible values are `coast`, `brake` and `hold`. `coast` means that power will
470 | * be removed from the motor and it will freely coast to a stop. `brake` means
471 | * that power will be removed from the motor and a passive electrical load will
472 | * be placed on the motor. This is usually done by shorting the motor terminals
473 | * together. This load will absorb the energy from the rotation of the motors and
474 | * cause the motor to stop more quickly than coasting. `hold` does not remove
475 | * power from the motor. Instead it actively tries to hold the motor at the current
476 | * position. If an external force tries to turn the motor, the motor will 'push
477 | * back' to maintain its position.
478 | */
479 | get stopActions(): string[] {
480 | return this.readStringArray("stop_actions");
481 | }
482 |
483 | /**
484 | * Writing specifies the amount of time the motor will run when using the
485 | * `run-timed` command. Reading returns the current value. Units are in
486 | * milliseconds.
487 | */
488 | get timeSp(): number {
489 | return this.readNumber("time_sp");
490 | }
491 | /**
492 | * Writing specifies the amount of time the motor will run when using the
493 | * `run-timed` command. Reading returns the current value. Units are in
494 | * milliseconds.
495 | */
496 | set timeSp(value: number) {
497 | this.setNumber("time_sp", value);
498 | }
499 |
500 |
501 | //~autogen
502 |
503 | public sendCommand(commandName: Motor.CommandValue) {
504 |
505 | if (this.commands.indexOf(commandName) < 0)
506 | throw new Error('The command ' + commandName + ' is not supported by the device.');
507 |
508 | this.command = commandName;
509 | }
510 |
511 | public setStopAction(stopAction: Motor.StopActionValue) {
512 |
513 | if (this.stopActions.indexOf(stopAction) < 0)
514 | throw new Error('The stop command ' + stopAction + ' is not supported by the device.');
515 |
516 | this.stopAction = stopAction;
517 | }
518 |
519 | public runForever(sp?: number, stopAction?: Motor.StopActionValue) {
520 | if (sp != undefined)
521 | this.speedSp = sp;
522 |
523 | if(stopAction != undefined)
524 | this.setStopAction(stopAction);
525 |
526 | this.sendCommand(this.commandValues.runForever);
527 | }
528 |
529 | public start(sp?: number, stopAction?: Motor.StopActionValue) {
530 | this.runForever(sp, stopAction);
531 | }
532 |
533 | public runToPosition(position?: number, speedSp?: number, stopAction?: Motor.StopActionValue) {
534 | this.runToAbsolutePosition(position, speedSp, stopAction);
535 | }
536 |
537 | public runToAbsolutePosition(position?: number, speedSp?: number, stopAction?: Motor.StopActionValue) {
538 | if (speedSp != undefined)
539 | this.speedSp = speedSp;
540 |
541 | if (position != undefined)
542 | this.positionSp = position;
543 |
544 | if(stopAction != undefined)
545 | this.setStopAction(stopAction);
546 |
547 | this.sendCommand(this.commandValues.runToAbsPos);
548 | }
549 |
550 | public runForDistance(distance?: number, speedSp?: number, stopAction?: Motor.StopActionValue) {
551 | this.runToRelativePosition(distance, speedSp, stopAction);
552 | }
553 |
554 | public runToRelativePosition(relPos?: number, speedSp?: number, stopAction?: Motor.StopActionValue) {
555 | if (speedSp != undefined)
556 | this.speedSp = speedSp;
557 |
558 | if (relPos != undefined)
559 | this.positionSp = relPos;
560 |
561 | if(stopAction != undefined)
562 | this.setStopAction(stopAction);
563 |
564 | this.sendCommand(this.commandValues.runToRelPos);
565 | }
566 |
567 | public runForTime(timeMs: number, speedSp?: number, stopAction?: Motor.StopActionValue) {
568 | if (speedSp != undefined)
569 | this.speedSp = speedSp;
570 |
571 | if (timeMs != undefined)
572 | this.timeSp = timeMs;
573 |
574 | if(stopAction != undefined)
575 | this.setStopAction(stopAction);
576 |
577 | this.sendCommand(this.commandValues.runTimed);
578 | }
579 |
580 | public hasState(stateValue: Motor.StateValue): boolean {
581 | return this.state.indexOf(stateValue) >= 0;
582 | }
583 |
584 | public get isRunning(): boolean {
585 | return this.hasState(this.stateValues.running);
586 | }
587 |
588 | public get isRamping(): boolean {
589 | return this.hasState(this.stateValues.ramping);
590 | }
591 |
592 | public get isHolding(): boolean {
593 | return this.hasState(this.stateValues.holding);
594 | }
595 |
596 | public get isOverloaded(): boolean {
597 | return this.hasState(this.stateValues.overloaded);
598 | }
599 |
600 | public get isStalled(): boolean {
601 | return this.hasState(this.stateValues.stalled);
602 | }
603 | }
604 |
605 | //~autogen generic-class-description classes.largeMotor>currentClass
606 | /**
607 | * EV3 large servo motor
608 | */
609 | //~autogen
610 | export class LargeMotor extends Motor {
611 | constructor(port?: string) {
612 | super(port, 'lego-ev3-l-motor');
613 | }
614 | }
615 |
616 | //~autogen generic-class-description classes.mediumMotor>currentClass
617 | /**
618 | * EV3 medium servo motor
619 | */
620 | //~autogen
621 | export class MediumMotor extends Motor {
622 | constructor(port?: string) {
623 | super(port, 'lego-ev3-m-motor');
624 | }
625 | }
626 |
627 | //~autogen generic-class-description classes.dcMotor>currentClass
628 | /**
629 | * The DC motor class provides a uniform interface for using regular DC motors
630 | * with no fancy controls or feedback. This includes LEGO MINDSTORMS RCX motors
631 | * and LEGO Power Functions motors.
632 | */
633 | //~autogen
634 | export class DcMotor extends MotorBase {
635 |
636 | constructor(port: string) {
637 | //~autogen connect-super-call classes.dcMotor>currentClass "port">extraParams "true">omitNameConvention
638 | super('dc-motor', null, port);
639 | //~autogen
640 | }
641 |
642 | //~autogen property-value-constants classes.dcMotor>currentClass
643 |
644 | public get commandValues(): { runForever: DcMotor.CommandValue, runTimed: DcMotor.CommandValue, runDirect: DcMotor.CommandValue, stop: DcMotor.CommandValue } {
645 | return {
646 | runForever: "run-forever",
647 | runTimed: "run-timed",
648 | runDirect: "run-direct",
649 | stop: "stop"
650 | }
651 | }
652 |
653 | public get polarityValues(): { normal: DcMotor.PolarityValue, inversed: DcMotor.PolarityValue } {
654 | return {
655 | normal: "normal",
656 | inversed: "inversed"
657 | }
658 | }
659 |
660 | public get stopActionValues(): { coast: DcMotor.StopActionValue, brake: DcMotor.StopActionValue } {
661 | return {
662 | coast: "coast",
663 | brake: "brake"
664 | }
665 | }
666 |
667 | //~autogen
668 |
669 | //PROPERTIES
670 |
671 | //~autogen generic-get-set classes.dcMotor>currentClass
672 | /**
673 | * Returns the name of the port that this motor is connected to.
674 | */
675 | get address(): string {
676 | return this.readString("address");
677 | }
678 |
679 | /**
680 | * Sets the command for the motor. Possible values are `run-forever`, `run-timed` and
681 | * `stop`. Not all commands may be supported, so be sure to check the contents
682 | * of the `commands` attribute.
683 | */
684 | set command(value: DcMotor.CommandValue) {
685 | this.setString("command", value);
686 | }
687 |
688 | /**
689 | * Returns a list of commands supported by the motor
690 | * controller.
691 | */
692 | get commands(): string[] {
693 | return this.readStringArray("commands");
694 | }
695 |
696 | /**
697 | * Returns the name of the motor driver that loaded this device. See the list
698 | * of [supported devices] for a list of drivers.
699 | */
700 | get driverName(): string {
701 | return this.readString("driver_name");
702 | }
703 |
704 | /**
705 | * Shows the current duty cycle of the PWM signal sent to the motor. Values
706 | * are -100 to 100 (-100% to 100%).
707 | */
708 | get dutyCycle(): number {
709 | return this.readNumber("duty_cycle");
710 | }
711 |
712 | /**
713 | * Writing sets the duty cycle setpoint of the PWM signal sent to the motor.
714 | * Valid values are -100 to 100 (-100% to 100%). Reading returns the current
715 | * setpoint.
716 | */
717 | get dutyCycleSp(): number {
718 | return this.readNumber("duty_cycle_sp");
719 | }
720 | /**
721 | * Writing sets the duty cycle setpoint of the PWM signal sent to the motor.
722 | * Valid values are -100 to 100 (-100% to 100%). Reading returns the current
723 | * setpoint.
724 | */
725 | set dutyCycleSp(value: number) {
726 | this.setNumber("duty_cycle_sp", value);
727 | }
728 |
729 | /**
730 | * Sets the polarity of the motor. Valid values are `normal` and `inversed`.
731 | */
732 | get polarity(): DcMotor.PolarityValue {
733 | return this.readStringAsType("polarity");
734 | }
735 | /**
736 | * Sets the polarity of the motor. Valid values are `normal` and `inversed`.
737 | */
738 | set polarity(value: DcMotor.PolarityValue) {
739 | this.setString("polarity", value);
740 | }
741 |
742 | /**
743 | * Sets the time in milliseconds that it take the motor to ramp down from 100%
744 | * to 0%. Valid values are 0 to 10000 (10 seconds). Default is 0.
745 | */
746 | get rampDownSp(): number {
747 | return this.readNumber("ramp_down_sp");
748 | }
749 | /**
750 | * Sets the time in milliseconds that it take the motor to ramp down from 100%
751 | * to 0%. Valid values are 0 to 10000 (10 seconds). Default is 0.
752 | */
753 | set rampDownSp(value: number) {
754 | this.setNumber("ramp_down_sp", value);
755 | }
756 |
757 | /**
758 | * Sets the time in milliseconds that it take the motor to up ramp from 0% to
759 | * 100%. Valid values are 0 to 10000 (10 seconds). Default is 0.
760 | */
761 | get rampUpSp(): number {
762 | return this.readNumber("ramp_up_sp");
763 | }
764 | /**
765 | * Sets the time in milliseconds that it take the motor to up ramp from 0% to
766 | * 100%. Valid values are 0 to 10000 (10 seconds). Default is 0.
767 | */
768 | set rampUpSp(value: number) {
769 | this.setNumber("ramp_up_sp", value);
770 | }
771 |
772 | /**
773 | * Gets a list of flags indicating the motor status. Possible
774 | * flags are `running` and `ramping`. `running` indicates that the motor is
775 | * powered. `ramping` indicates that the motor has not yet reached the
776 | * `duty_cycle_sp`.
777 | */
778 | get state(): string[] {
779 | return this.readStringArray("state");
780 | }
781 |
782 | /**
783 | * Sets the stop action that will be used when the motor stops. Read
784 | * `stop_actions` to get the list of valid values.
785 | */
786 | set stopAction(value: DcMotor.StopActionValue) {
787 | this.setString("stop_action", value);
788 | }
789 |
790 | /**
791 | * Gets a list of stop actions. Valid values are `coast`
792 | * and `brake`.
793 | */
794 | get stopActions(): string[] {
795 | return this.readStringArray("stop_actions");
796 | }
797 |
798 | /**
799 | * Writing specifies the amount of time the motor will run when using the
800 | * `run-timed` command. Reading returns the current value. Units are in
801 | * milliseconds.
802 | */
803 | get timeSp(): number {
804 | return this.readNumber("time_sp");
805 | }
806 | /**
807 | * Writing specifies the amount of time the motor will run when using the
808 | * `run-timed` command. Reading returns the current value. Units are in
809 | * milliseconds.
810 | */
811 | set timeSp(value: number) {
812 | this.setNumber("time_sp", value);
813 | }
814 |
815 |
816 | //~autogen
817 | }
818 |
819 | //~autogen generic-class-description classes.servoMotor>currentClass
820 | /**
821 | * The servo motor class provides a uniform interface for using hobby type
822 | * servo motors.
823 | */
824 | //~autogen
825 | export class ServoMotor extends MotorBase {
826 |
827 | constructor(port: string) {
828 | //~autogen connect-super-call classes.servoMotor>currentClass "port">extraParams "true">omitNameConvention
829 | super('servo-motor', null, port);
830 | //~autogen
831 | }
832 |
833 | //~autogen property-value-constants classes.servoMotor>currentClass
834 |
835 | public get commandValues(): { run: ServoMotor.CommandValue, float: ServoMotor.CommandValue } {
836 | return {
837 | run: "run",
838 | float: "float"
839 | }
840 | }
841 |
842 | public get polarityValues(): { normal: ServoMotor.PolarityValue, inversed: ServoMotor.PolarityValue } {
843 | return {
844 | normal: "normal",
845 | inversed: "inversed"
846 | }
847 | }
848 |
849 | //~autogen
850 |
851 | //PROPERTIES
852 |
853 | //~autogen generic-get-set classes.servoMotor>currentClass
854 | /**
855 | * Returns the name of the port that this motor is connected to.
856 | */
857 | get address(): string {
858 | return this.readString("address");
859 | }
860 |
861 | /**
862 | * Sets the command for the servo. Valid values are `run` and `float`. Setting
863 | * to `run` will cause the servo to be driven to the position_sp set in the
864 | * `position_sp` attribute. Setting to `float` will remove power from the motor.
865 | */
866 | set command(value: ServoMotor.CommandValue) {
867 | this.setString("command", value);
868 | }
869 |
870 | /**
871 | * Returns the name of the motor driver that loaded this device. See the list
872 | * of [supported devices] for a list of drivers.
873 | */
874 | get driverName(): string {
875 | return this.readString("driver_name");
876 | }
877 |
878 | /**
879 | * Used to set the pulse size in milliseconds for the signal that tells the
880 | * servo to drive to the maximum (clockwise) position_sp. Default value is 2400.
881 | * Valid values are 2300 to 2700. You must write to the position_sp attribute for
882 | * changes to this attribute to take effect.
883 | */
884 | get maxPulseSp(): number {
885 | return this.readNumber("max_pulse_sp");
886 | }
887 | /**
888 | * Used to set the pulse size in milliseconds for the signal that tells the
889 | * servo to drive to the maximum (clockwise) position_sp. Default value is 2400.
890 | * Valid values are 2300 to 2700. You must write to the position_sp attribute for
891 | * changes to this attribute to take effect.
892 | */
893 | set maxPulseSp(value: number) {
894 | this.setNumber("max_pulse_sp", value);
895 | }
896 |
897 | /**
898 | * Used to set the pulse size in milliseconds for the signal that tells the
899 | * servo to drive to the mid position_sp. Default value is 1500. Valid
900 | * values are 1300 to 1700. For example, on a 180 degree servo, this would be
901 | * 90 degrees. On continuous rotation servo, this is the 'neutral' position_sp
902 | * where the motor does not turn. You must write to the position_sp attribute for
903 | * changes to this attribute to take effect.
904 | */
905 | get midPulseSp(): number {
906 | return this.readNumber("mid_pulse_sp");
907 | }
908 | /**
909 | * Used to set the pulse size in milliseconds for the signal that tells the
910 | * servo to drive to the mid position_sp. Default value is 1500. Valid
911 | * values are 1300 to 1700. For example, on a 180 degree servo, this would be
912 | * 90 degrees. On continuous rotation servo, this is the 'neutral' position_sp
913 | * where the motor does not turn. You must write to the position_sp attribute for
914 | * changes to this attribute to take effect.
915 | */
916 | set midPulseSp(value: number) {
917 | this.setNumber("mid_pulse_sp", value);
918 | }
919 |
920 | /**
921 | * Used to set the pulse size in milliseconds for the signal that tells the
922 | * servo to drive to the miniumum (counter-clockwise) position_sp. Default value
923 | * is 600. Valid values are 300 to 700. You must write to the position_sp
924 | * attribute for changes to this attribute to take effect.
925 | */
926 | get minPulseSp(): number {
927 | return this.readNumber("min_pulse_sp");
928 | }
929 | /**
930 | * Used to set the pulse size in milliseconds for the signal that tells the
931 | * servo to drive to the miniumum (counter-clockwise) position_sp. Default value
932 | * is 600. Valid values are 300 to 700. You must write to the position_sp
933 | * attribute for changes to this attribute to take effect.
934 | */
935 | set minPulseSp(value: number) {
936 | this.setNumber("min_pulse_sp", value);
937 | }
938 |
939 | /**
940 | * Sets the polarity of the servo. Valid values are `normal` and `inversed`.
941 | * Setting the value to `inversed` will cause the position_sp value to be
942 | * inversed. i.e `-100` will correspond to `max_pulse_sp`, and `100` will
943 | * correspond to `min_pulse_sp`.
944 | */
945 | get polarity(): ServoMotor.PolarityValue {
946 | return this.readStringAsType("polarity");
947 | }
948 | /**
949 | * Sets the polarity of the servo. Valid values are `normal` and `inversed`.
950 | * Setting the value to `inversed` will cause the position_sp value to be
951 | * inversed. i.e `-100` will correspond to `max_pulse_sp`, and `100` will
952 | * correspond to `min_pulse_sp`.
953 | */
954 | set polarity(value: ServoMotor.PolarityValue) {
955 | this.setString("polarity", value);
956 | }
957 |
958 | /**
959 | * Reading returns the current position_sp of the servo. Writing instructs the
960 | * servo to move to the specified position_sp. Units are percent. Valid values
961 | * are -100 to 100 (-100% to 100%) where `-100` corresponds to `min_pulse_sp`,
962 | * `0` corresponds to `mid_pulse_sp` and `100` corresponds to `max_pulse_sp`.
963 | */
964 | get positionSp(): number {
965 | return this.readNumber("position_sp");
966 | }
967 | /**
968 | * Reading returns the current position_sp of the servo. Writing instructs the
969 | * servo to move to the specified position_sp. Units are percent. Valid values
970 | * are -100 to 100 (-100% to 100%) where `-100` corresponds to `min_pulse_sp`,
971 | * `0` corresponds to `mid_pulse_sp` and `100` corresponds to `max_pulse_sp`.
972 | */
973 | set positionSp(value: number) {
974 | this.setNumber("position_sp", value);
975 | }
976 |
977 | /**
978 | * Sets the rate_sp at which the servo travels from 0 to 100.0% (half of the full
979 | * range of the servo). Units are in milliseconds. Example: Setting the rate_sp
980 | * to 1000 means that it will take a 180 degree servo 2 second to move from 0
981 | * to 180 degrees. Note: Some servo controllers may not support this in which
982 | * case reading and writing will fail with `-EOPNOTSUPP`. In continuous rotation
983 | * servos, this value will affect the rate_sp at which the speed ramps up or down.
984 | */
985 | get rateSp(): number {
986 | return this.readNumber("rate_sp");
987 | }
988 | /**
989 | * Sets the rate_sp at which the servo travels from 0 to 100.0% (half of the full
990 | * range of the servo). Units are in milliseconds. Example: Setting the rate_sp
991 | * to 1000 means that it will take a 180 degree servo 2 second to move from 0
992 | * to 180 degrees. Note: Some servo controllers may not support this in which
993 | * case reading and writing will fail with `-EOPNOTSUPP`. In continuous rotation
994 | * servos, this value will affect the rate_sp at which the speed ramps up or down.
995 | */
996 | set rateSp(value: number) {
997 | this.setNumber("rate_sp", value);
998 | }
999 |
1000 | /**
1001 | * Returns a list of flags indicating the state of the servo.
1002 | * Possible values are:
1003 | * * `running`: Indicates that the motor is powered.
1004 | */
1005 | get state(): string[] {
1006 | return this.readStringArray("state");
1007 | }
1008 |
1009 |
1010 | //~autogen
1011 | }
--------------------------------------------------------------------------------
/lib/bluebird.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for bluebird 2.0.0
2 | // Project: https://github.com/petkaantonov/bluebird
3 | // Definitions by: Bart van der Schoor , falsandtru
4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5 |
6 | // ES6 model with generics overload was sourced and trans-multiplied from es6-promises.d.ts
7 | // By: Campredon
8 |
9 | // Warning: recommended to use `tsc > v0.9.7` (critical bugs in earlier generic code):
10 | // - https://github.com/DefinitelyTyped/DefinitelyTyped/issues/1563
11 |
12 | // Note: replicate changes to all overloads in both definition and test file
13 | // Note: keep both static and instance members inline (so similar)
14 |
15 | // TODO fix remaining TODO annotations in both definition and test
16 |
17 | // TODO verify support to have no return statement in handlers to get a Promise (more overloads?)
18 |
19 | declare var Promise: PromiseConstructor;
20 |
21 | interface PromiseConstructor {
22 | /**
23 | * Create a new promise. The passed in function will receive functions `resolve` and `reject` as its arguments which can be called to seal the fate of the created promise.
24 | */
25 | new (callback: (resolve: (thenableOrResult?: T | PromiseLike) => void, reject: (error: any) => void) => void): Promise;
26 |
27 | config(options: {
28 | warnings?: boolean | {wForgottenReturn?: boolean};
29 | longStackTraces?: boolean;
30 | cancellation?: boolean;
31 | monitoring?: boolean;
32 | }): void;
33 |
34 | // Ideally, we'd define e.g. "export class RangeError extends Error {}",
35 | // but as Error is defined as an interface (not a class), TypeScript doesn't
36 | // allow extending Error, only implementing it.
37 | // However, if we want to catch() only a specific error type, we need to pass
38 | // a constructor function to it. So, as a workaround, we define them here as such.
39 | RangeError(): RangeError;
40 | CancellationError(): Promise.CancellationError;
41 | TimeoutError(): Promise.TimeoutError;
42 | TypeError(): Promise.TypeError;
43 | RejectionError(): Promise.RejectionError;
44 | OperationalError(): Promise.OperationalError;
45 |
46 | /**
47 | * Changes how bluebird schedules calls a-synchronously.
48 | *
49 | * @param scheduler Should be a function that asynchronously schedules
50 | * the calling of the passed in function
51 | */
52 | setScheduler(scheduler: (callback: (...args: any[]) => void) => void): void;
53 |
54 | /**
55 | * Start the chain of promises with `Promise.try`. Any synchronous exceptions will be turned into rejections on the returned promise.
56 | *
57 | * Note about second argument: if it's specifically a true array, its values become respective arguments for the function call. Otherwise it is passed as is as the first argument for the function call.
58 | *
59 | * Alias for `attempt();` for compatibility with earlier ECMAScript version.
60 | */
61 | try(fn: () => T | PromiseLike, args?: any[], ctx?: any): Promise;
62 |
63 | attempt(fn: () => T | PromiseLike, args?: any[], ctx?: any): Promise;
64 |
65 | /**
66 | * Returns a new function that wraps the given function `fn`. The new function will always return a promise that is fulfilled with the original functions return values or rejected with thrown exceptions from the original function.
67 | * This method is convenient when a function can sometimes return synchronously or throw synchronously.
68 | */
69 | method(fn: Function): Function;
70 |
71 | /**
72 | * Create a promise that is resolved with the given `value`. If `value` is a thenable or promise, the returned promise will assume its state.
73 | */
74 | resolve(value: T | PromiseLike): Promise;
75 | resolve(): Promise;
76 |
77 | /**
78 | * Create a promise that is rejected with the given `reason`.
79 | */
80 | reject(reason: any): Promise;
81 | reject(reason: any): Promise;
82 |
83 | /**
84 | * Create a promise with undecided fate and return a `PromiseResolver` to control it. See resolution?: Promise(#promise-resolution).
85 | */
86 | defer(): Promise.Resolver;
87 |
88 | /**
89 | * Cast the given `value` to a trusted promise. If `value` is already a trusted `Promise`, it is returned as is. If `value` is not a thenable, a fulfilled is: Promise returned with `value` as its fulfillment value. If `value` is a thenable (Promise-like object, like those returned by jQuery's `$.ajax`), returns a trusted that: Promise assimilates the state of the thenable.
90 | */
91 | cast(value: T | PromiseLike): Promise;
92 |
93 | /**
94 | * Sugar for `Promise.resolve(undefined).bind(thisArg);`. See `.bind()`.
95 | */
96 | bind(thisArg: any): Promise;
97 |
98 | /**
99 | * See if `value` is a trusted Promise.
100 | */
101 | is(value: any): boolean;
102 |
103 | /**
104 | * Call this right after the library is loaded to enabled long stack traces. Long stack traces cannot be disabled after being enabled, and cannot be enabled after promises have alread been created. Long stack traces imply a substantial performance penalty, around 4-5x for throughput and 0.5x for latency.
105 | */
106 | longStackTraces(): void;
107 |
108 | /**
109 | * Returns a promise that will be fulfilled with `value` (or `undefined`) after given `ms` milliseconds. If `value` is a promise, the delay will start counting down when it is fulfilled and the returned promise will be fulfilled with the fulfillment value of the `value` promise.
110 | */
111 | // TODO enable more overloads
112 | delay(ms: number, value: T | PromiseLike): Promise;
113 | delay(ms: number): Promise;
114 |
115 | /**
116 | * Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.
117 | *
118 | * If the `nodeFunction` calls its callback with multiple success values, the fulfillment value will be an array of them.
119 | *
120 | * If you pass a `receiver`, the `nodeFunction` will be called as a method on the `receiver`.
121 | */
122 | promisify(func: (callback: (err: any, result: T) => void) => void, receiver?: any): () => Promise;
123 | promisify(func: (arg1: A1, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1) => Promise;
124 | promisify(func: (arg1: A1, arg2: A2, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2) => Promise;
125 | promisify(func: (arg1: A1, arg2: A2, arg3: A3, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3) => Promise;
126 | promisify(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Promise;
127 | promisify(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Promise;
128 | promisify(nodeFunction: Function, receiver?: any): Function;
129 |
130 | /**
131 | * Promisifies the entire object by going through the object's properties and creating an async equivalent of each function on the object and its prototype chain. The promisified method name will be the original method name postfixed with `Async`. Returns the input object.
132 | *
133 | * Note that the original methods on the object are not overwritten but new methods are created with the `Async`-postfix. For example, if you `promisifyAll()` the node.js `fs` object use `fs.statAsync()` to call the promisified `stat` method.
134 | */
135 | // TODO how to model promisifyAll?
136 | promisifyAll(target: Object, options?: Promise.PromisifyAllOptions): any;
137 |
138 |
139 | /**
140 | * Returns a promise that is resolved by a node style callback function.
141 | */
142 | fromNode(resolver: (callback: (err: any, result?: any) => void) => void, options? : {multiArgs? : boolean}): Promise;
143 | fromCallback(resolver: (callback: (err: any, result?: any) => void) => void, options? : {multiArgs? : boolean}): Promise;
144 |
145 | /**
146 | * Returns a function that can use `yield` to run asynchronous code synchronously. This feature requires the support of generators which are drafted in the next version of the language. Node version greater than `0.11.2` is required and needs to be executed with the `--harmony-generators` (or `--harmony`) command-line switch.
147 | */
148 | // TODO fix coroutine GeneratorFunction
149 | coroutine(generatorFunction: Function): Function;
150 |
151 | /**
152 | * Spawn a coroutine which may yield promises to run asynchronous code synchronously. This feature requires the support of generators which are drafted in the next version of the language. Node version greater than `0.11.2` is required and needs to be executed with the `--harmony-generators` (or `--harmony`) command-line switch.
153 | */
154 | // TODO fix spawn GeneratorFunction
155 | spawn(generatorFunction: Function): Promise;
156 |
157 | /**
158 | * This is relevant to browser environments with no module loader.
159 | *
160 | * Release control of the `Promise` namespace to whatever it was before this library was loaded. Returns a reference to the library namespace so you can attach it to something else.
161 | */
162 | noConflict(): typeof Promise;
163 |
164 | /**
165 | * Add `handler` as the handler to call when there is a possibly unhandled rejection. The default handler logs the error stack to stderr or `console.error` in browsers.
166 | *
167 | * Passing no value or a non-function will have the effect of removing any kind of handling for possibly unhandled rejections.
168 | */
169 | onPossiblyUnhandledRejection(handler: (reason: any) => any): void;
170 |
171 | /**
172 | * Given an array, or a promise of an array, which contains promises (or a mix of promises and values) return a promise that is fulfilled when all the items in the array are fulfilled. The promise's fulfillment value is an array with fulfillment values at respective positions to the original array. If any promise in the array rejects, the returned promise is rejected with the rejection reason.
173 | */
174 | // TODO enable more overloads
175 | // promise of array with promises of value
176 | all(values: PromiseLike[]>): Promise;
177 | // promise of array with values
178 | all(values: PromiseLike): Promise;
179 | // array with promises of value
180 | all(values: PromiseLike[]): Promise;
181 | // array with promises of different types
182 | all(values: [PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike]): Promise<[T1, T2, T3, T4, T5]>;
183 | all(values: [PromiseLike, PromiseLike, PromiseLike, PromiseLike]): Promise<[T1, T2, T3, T4]>;
184 | all(values: [PromiseLike, PromiseLike, PromiseLike]): Promise<[T1, T2, T3]>;
185 | all(values: [PromiseLike, PromiseLike]): Promise<[T1, T2]>;
186 | // array with values
187 | all(values: T[]): Promise;
188 |
189 | /**
190 | * Like ``Promise.all`` but for object properties instead of array items. Returns a promise that is fulfilled when all the properties of the object are fulfilled. The promise's fulfillment value is an object with fulfillment values at respective keys to the original object. If any promise in the object rejects, the returned promise is rejected with the rejection reason.
191 | *
192 | * If `object` is a trusted `Promise`, then it will be treated as a promise for object rather than for its properties. All other objects are treated for their properties as is returned by `Object.keys` - the object's own enumerable properties.
193 | *
194 | * *The original object is not modified.*
195 | */
196 | // TODO verify this is correct
197 | // trusted promise for object
198 | props(object: Promise