scratch-vm/src/io/bt.js
Evelyn Eastmond ec432e3b2f Refactor for hardware extensions (#1555)
* Beginning refactor: renaming 'device' to 'peripheral', shortening function names, reordering functions, etc.

* Continuing refactoring: renaming some functions to be more verbose in the runtime, adding JSDocs, etc.

* Changing 'device' to 'peripheral', etc.

* Changing 'session' to 'socket'.

* Fixing EV3 menus and menu arg validation, reordering functions, etc.

* Add _send, add some references to documentation, etc.

* Factored out _outputCommand and _inputCommand, renamed some enums, etc.

* Fixed _outputCommand, some other minor cleanup.

* Make _outputCommand and _inputCommand public.

* Added TODO.

* Renamed BLE UUID enums to be clearer.

* Change WeDo2 in comments to WeDo 2.0, etc.

* Changed some WeDo2Motor command names, cleaned up some JSDocs.

* Beginning a major EV3 refactor.

* WeDo2 formatting and comment changes.

* Motor refactoring in EV3: motorTurnClockwise and motorTurnCounterClockwise initial working state.

* Add reminders to possibly cast motor menu args in WeDo2.

* Continue to move motor commands in EV3 to EV3Motor class, don't create new EV3Motor on every poll cycle, etc.

* Factoring EV3 polling value commands, etc.

* Fixing EV3 motor power, position and button pressed, and some commenting, etc.

* Move EV3 motor position parsing to EV3Motor class, move directCommand and directCompoundCommand functions, some commenting, etc.

* Changed WeDo2 motor label enum name.

* Removed some EV3 motor functions that aren't needed, changed menu label enum names, moved some opcodes up to enums.

* Fixing comments and documentation.

* Some commenting.

* Adding further documentation and references to PDFs, changed reply check to be safer, etc.

* Some comment changes.

* Moving some functions around in EV3 and WeDo2 to match.

* Commenting, etc.

* Some renaming of session, etc.

* Fix stopAllMotors in EV3.

* Fixing clearing of motors in EV3.

* Some comment changes.

* Change runtime .extensions/registerExtension to .peripheralExtensions/registerPeripheralExtension.

* Renaming outputCommand/inputCommand to generateOutputCommand/generateInputCommand, etc.

* Moved motorCommandIDs to EV3Motor class, renamed directCommand to generateCommand, etc.

* Adding a reminder to rename something.

* JSDoc fix in EV3Motor class.

* Fixing microbit function name.

* Adding a todo item.

* Changing Ev3 menu formats to be backwards compatible, moving a BLE function up.

* Fixing EV3 ports again, and button pressed returning a boolean.

* Fixing menu value to be a string in EV3.
2018-09-07 17:01:23 -04:00

126 lines
4.3 KiB
JavaScript

const JSONRPCWebSocket = require('../util/jsonrpc-web-socket');
const ScratchLinkWebSocket = 'wss://device-manager.scratch.mit.edu:20110/scratch/bt';
// const log = require('../util/log');
class BT extends JSONRPCWebSocket {
/**
* A BT peripheral socket object. It handles connecting, over web sockets, to
* BT peripherals, and reading and writing data to them.
* @param {Runtime} runtime - the Runtime for sending/receiving GUI update events.
* @param {object} peripheralOptions - the list of options for peripheral discovery.
* @param {object} connectCallback - a callback for connection.
* @param {object} messageCallback - a callback for message sending.
*/
constructor (runtime, peripheralOptions, connectCallback, messageCallback) {
const ws = new WebSocket(ScratchLinkWebSocket);
super(ws);
this._ws = ws;
this._ws.onopen = this.requestPeripheral.bind(this); // only call request peripheral after socket opens
this._ws.onerror = this._sendError.bind(this, 'ws onerror');
this._ws.onclose = this._sendError.bind(this, 'ws onclose');
this._availablePeripherals = {};
this._connectCallback = connectCallback;
this._connected = false;
this._characteristicDidChangeCallback = null;
this._peripheralOptions = peripheralOptions;
this._discoverTimeoutID = null;
this._messageCallback = messageCallback;
this._runtime = runtime;
}
/**
* Request connection to the peripheral.
* If the web socket is not yet open, request when the socket promise resolves.
*/
requestPeripheral () {
if (this._ws.readyState === 1) { // is this needed since it's only called on ws.onopen?
this._availablePeripherals = {};
this._discoverTimeoutID = window.setTimeout(this._sendDiscoverTimeout.bind(this), 15000);
this.sendRemoteRequest('discover', this._peripheralOptions)
.catch(e => this._sendError(e)); // never reached?
}
// TODO: else?
}
/**
* Try connecting to the input peripheral id, and then call the connect
* callback if connection is successful.
* @param {number} id - the id of the peripheral to connect to
*/
connectPeripheral (id) {
this.sendRemoteRequest('connect', {peripheralId: id})
.then(() => {
this._connected = true;
this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);
this._connectCallback();
})
.catch(e => {
this._sendError(e);
});
}
/**
* Close the websocket.
*/
disconnect () {
this._ws.close();
this._connected = false;
}
/**
* @return {bool} whether the peripheral is connected.
*/
isConnected () {
return this._connected;
}
sendMessage (options) {
return this.sendRemoteRequest('send', options)
.catch(e => {
this._sendError(e);
});
}
/**
* Handle a received call from the socket.
* @param {string} method - a received method label.
* @param {object} params - a received list of parameters.
* @return {object} - optional return value.
*/
didReceiveCall (method, params) {
// TODO: Add peripheral 'undiscover' handling
switch (method) {
case 'didDiscoverPeripheral':
this._availablePeripherals[params.peripheralId] = params;
this._runtime.emit(
this._runtime.constructor.PERIPHERAL_LIST_UPDATE,
this._availablePeripherals
);
if (this._discoverTimeoutID) {
// TODO: window?
window.clearTimeout(this._discoverTimeoutID);
}
break;
case 'didReceiveMessage':
this._messageCallback(params); // TODO: refine?
break;
default:
return 'nah';
}
}
_sendError (/* e */) {
this.disconnect();
// log.error(`BT error: ${JSON.stringify(e)}`);
this._runtime.emit(this._runtime.constructor.PERIPHERAL_ERROR);
}
_sendDiscoverTimeout () {
this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);
}
}
module.exports = BT;