This commit is contained in:
Eric Rosenbaum 2018-06-19 17:59:03 -04:00 committed by Ray Schamp
parent 6c118cf8e0
commit d778ddf00e
5 changed files with 116 additions and 40 deletions

View file

@ -254,6 +254,8 @@ class Runtime extends EventEmitter {
video: new Video(this)
};
this.extensionDevices = {};
/**
* A runtime profiler that records timed events for later playback to
* diagnose Scratch performance.
@ -394,6 +396,22 @@ class Runtime extends EventEmitter {
return 'EXTENSION_ADDED';
}
/**
* Event name for updating the available set of peripheral devices.
* @const {string}
*/
static get PERIPHERAL_LIST_UPDATE () {
return 'PERIPHERAL_LIST_UPDATE';
}
/**
* Event name for reporting that a peripheral has connected.
* @const {string}
*/
static get PERIPHERAL_CONNECTED () {
return 'PERIPHERAL_CONNECTED';
}
/**
* Event name for reporting that blocksInfo was updated.
* @const {string}
@ -867,6 +885,22 @@ class Runtime extends EventEmitter {
(result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []);
}
registerExtensionDevice (extensionId, device) {
this.extensionDevices[extensionId] = device;
}
startDeviceScan (extensionId) {
if (this.extensionDevices[extensionId]) {
this.extensionDevices[extensionId].startScan();
}
}
connectToPeripheral (extensionId, peripheralId) {
if (this.extensionDevices[extensionId]) {
this.extensionDevices[extensionId].connectToPeripheral(peripheralId);
}
}
/**
* Retrieve the function associated with the given opcode.
* @param {!string} opcode The opcode to look up.

View file

@ -50,8 +50,9 @@ class MicroBit {
/**
* Construct a MicroBit communication object.
* @param {Runtime} runtime - the Scratch 3.0 runtime
* @param {string} extensionId - the id of the extension
*/
constructor (runtime) {
constructor (runtime, extensionId) {
/**
* The Scratch 3.0 runtime used to trigger the green flag button.
@ -65,7 +66,13 @@ class MicroBit {
* @type {ScratchBLE}
* @private
*/
this._ble = new ScratchBLE();
this._ble = new ScratchBLE(this._runtime, {
filters: [
{services: [BLEUUID.service]}
]
});
this._runtime.registerExtensionDevice(extensionId, this._ble);
/**
* The most recently received value for each sensor.
@ -102,15 +109,6 @@ class MicroBit {
timeout: false
}
};
// TODO: Temporary until the gui requests a device connection
this._ble.waitForSocket()
// TODO: remove pinging once no longer needed
.then(() => this._ble.sendRemoteRequest('pingMe'))
.then(() => this._onBLEReady());
// TODO: Add ScratchBLE 'disconnect' handling
}
/**
@ -184,13 +182,13 @@ class MicroBit {
/**
* Requests connection to a device when BLE session is ready.
*/
_onBLEReady () {
this._ble.requestDevice({
filters: [
{services: [BLEUUID.service]}
]
}, this._onBLEConnect.bind(this), this._onBLEError);
}
// _onBLEReady () {
// this._ble.requestDevice({
// filters: [
// {services: [BLEUUID.service]}
// ]
// }, this._onBLEConnect.bind(this), this._onBLEError);
// }
/**
* Starts reading data from device after BLE has connected to it.
@ -318,7 +316,7 @@ class Scratch3MicroBitBlocks {
this.runtime = runtime;
// Create a new MicroBit device instance
this._device = new MicroBit(this.runtime);
this._device = new MicroBit(this.runtime, Scratch3MicroBitBlocks.EXTENSION_ID);
}
/**

View file

@ -4,8 +4,9 @@ class PeripheralChooser {
return this._chosenPeripheralId;
}
constructor () {
this._availablePeripherals = []; // TODO for use in gui?
constructor (runtime) {
this._runtime = runtime;
this._availablePeripherals = {};
this._chosenPeripheralId = null;
}
@ -23,14 +24,23 @@ class PeripheralChooser {
/**
* Adds the peripheral ID to list of available peripherals.
* @param {number} peripheralId - the id to add.
* @param {object} info - the peripheral info object.
*/
addPeripheral (peripheralId) {
this._availablePeripherals.push(peripheralId);
addPeripheral (info) {
// Add a new peripheral, or if the id is already present, update it
this._availablePeripherals[info.peripheralId] = info;
const peripheralArray = Object.keys(this._availablePeripherals).map(id =>
this._availablePeripherals[id]
);
// @todo: sort peripherals by signal strength? or maybe not, so they don't jump around?
this._runtime.emit(this._runtime.constructor.PERIPHERAL_LIST_UPDATE, peripheralArray);
// TODO: Temporary: calls chosen callback on whatever peripherals are added.
this._chosenPeripheralId = this._availablePeripherals[0];
this._tempPeripheralChosenCallback(this._chosenPeripheralId);
// this._chosenPeripheralId = this._availablePeripherals[0];
// this._tempPeripheralChosenCallback(this._chosenPeripheralId);
}
}

View file

@ -4,25 +4,44 @@ const PeripheralChooser = require('./peripheralChooser');
const ScratchLinkWebSocket = 'ws://localhost:20110/scratch/ble';
class ScratchBLE extends JSONRPCWebSocket {
constructor () {
constructor (runtime, deviceOptions) {
const ws = new WebSocket(ScratchLinkWebSocket);
super(ws);
this._ws = ws;
this.peripheralChooser = new PeripheralChooser(); // TODO: finalize gui connection
this._characteristicDidChange = null;
}
/**
* Returns a promise for when the web socket opens.
* @return {Promise} - a promise when BLE socket is open.
*/
waitForSocket () {
return new Promise((resolve, reject) => {
this._socketPromise = new Promise((resolve, reject) => {
this._ws.onopen = resolve;
this._ws.onerror = reject;
});
this._runtime = runtime;
this._ws = ws;
this.peripheralChooser = new PeripheralChooser(this._runtime); // TODO: finalize gui connection
this._characteristicDidChange = null;
this._deviceOptions = deviceOptions;
}
// @todo handle websocket failed
startScan () {
console.log('BLE startScan', this._ws.readyState);
if (this._ws.readyState === 1) {
this.sendRemoteRequest('pingMe')
.then(() => this.requestDevice(this._deviceOptions));
} else {
// Try again to connect to the websocket
this._socketPromise(this.sendRemoteRequest('pingMe')
.then(() => this.requestDevice(this._deviceOptions)));
}
}
connectToPeripheral (id) {
this.sendRemoteRequest(
'connect',
{peripheralId: id}
).then(() =>
this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED)
);
}
/**
@ -54,7 +73,7 @@ class ScratchBLE extends JSONRPCWebSocket {
// TODO: Add peripheral 'undiscover' handling
switch (method) {
case 'didDiscoverPeripheral':
this.peripheralChooser.addPeripheral(params.peripheralId);
this.peripheralChooser.addPeripheral(params);
break;
case 'characteristicDidChange':
this._characteristicDidChange(params.message);

View file

@ -106,6 +106,13 @@ class VirtualMachine extends EventEmitter {
this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo);
});
this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => {
this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info);
});
this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () =>
this.emit(Runtime.PERIPHERAL_CONNECTED)
);
this.extensionManager = new ExtensionManager(this.runtime);
this.blockListener = this.blockListener.bind(this);
@ -195,6 +202,14 @@ class VirtualMachine extends EventEmitter {
this.runtime.ioDevices.video.setProvider(videoProvider);
}
startDeviceScan (extensionId) {
this.runtime.startDeviceScan(extensionId);
}
connectToPeripheral (extensionId, peripheralId) {
this.runtime.connectToPeripheral(extensionId, peripheralId);
}
/**
* Load a Scratch project from a .sb, .sb2, .sb3 or json string.
* @param {string | object} input A json string, object, or ArrayBuffer representing the project to load.