Skip to content

Commit bffce25

Browse files
Add device connect mode
1 parent 869c6a9 commit bffce25

File tree

2 files changed

+71
-12
lines changed

2 files changed

+71
-12
lines changed

lib/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
} from "./usb-radio-bridge.js";
4343
import {
4444
createWebUSBConnection,
45+
DeviceConnectMode,
4546
MicrobitWebUSBConnection,
4647
MicrobitWebUSBConnectionOptions,
4748
} from "./usb.js";
@@ -58,6 +59,7 @@ export {
5859
createWebBluetoothConnection,
5960
createWebUSBConnection,
6061
DeviceConnectionEventMap,
62+
DeviceConnectMode,
6163
DeviceError,
6264
FlashDataError,
6365
FlashEvent,

lib/usb.ts

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,26 @@ export const isChromeOS105 = (): boolean => {
3737
return /CrOS/.test(userAgent) && /Chrome\/105\b/.test(userAgent);
3838
};
3939

40+
export enum DeviceConnectMode {
41+
/**
42+
* First tries to connect to stored device id, if no device id stored
43+
* trigger device selection and pairing flow.
44+
*/
45+
TryInitialPair = "try initial pair",
46+
/**
47+
* First tries to connect to stored device id, if no device id stored attempt
48+
* connection with previously paired devices. If all fail, trigger
49+
* device selection and pairing flow.
50+
*/
51+
TryInitialAndPrevPair = "try initial and prev pair",
52+
}
53+
4054
export interface MicrobitWebUSBConnectionOptions {
4155
// We should copy this type when extracting a library, and make it optional.
4256
// Coupling for now to make it easy to evolve.
4357

44-
logging: Logging;
58+
logging?: Logging;
59+
deviceConnectMode?: DeviceConnectMode;
4560
}
4661

4762
export interface MicrobitWebUSBConnection
@@ -176,16 +191,17 @@ class MicrobitWebUSBConnectionImpl
176191
};
177192

178193
private logging: Logging;
194+
private deviceConnectMode: DeviceConnectMode;
179195

180196
private addedListeners: Record<string, number> = {
181197
serialdata: 0,
182198
};
183199

184-
constructor(
185-
options: MicrobitWebUSBConnectionOptions = { logging: new NullLogging() },
186-
) {
200+
constructor(options: MicrobitWebUSBConnectionOptions = {}) {
187201
super();
188-
this.logging = options.logging;
202+
this.logging = options.logging || new NullLogging();
203+
this.deviceConnectMode =
204+
options.deviceConnectMode || DeviceConnectMode.TryInitialPair;
189205
}
190206

191207
private log(v: any) {
@@ -460,21 +476,49 @@ class MicrobitWebUSBConnectionImpl
460476
}
461477

462478
private async connectInternal(): Promise<void> {
463-
if (!this.connection) {
464-
const device = await this.chooseDevice();
465-
this.connection = new DAPWrapper(device, this.logging);
479+
if (!this.connection && this.device) {
480+
this.connection = new DAPWrapper(this.device, this.logging);
481+
await withTimeout(this.connection.reconnectAsync(), 10_000);
482+
} else if (!this.connection) {
483+
if (this.deviceConnectMode === DeviceConnectMode.TryInitialAndPrevPair) {
484+
await this.tryPrevConnectedDevices();
485+
}
486+
if (!this.connection) {
487+
this.device = await this.chooseDevice();
488+
this.connection = new DAPWrapper(this.device, this.logging);
489+
await withTimeout(this.connection.reconnectAsync(), 10_000);
490+
}
491+
} else {
492+
await withTimeout(this.connection.reconnectAsync(), 10_000);
466493
}
467-
await withTimeout(this.connection.reconnectAsync(), 10_000);
468494
if (this.addedListeners.serialdata && !this.flashing) {
469495
this.startSerialInternal();
470496
}
471497
this.setStatus(ConnectionStatus.CONNECTED);
472498
}
473499

474-
private async chooseDevice(): Promise<USBDevice> {
475-
if (this.device) {
476-
return this.device;
500+
// Drawn from https://github.com/microsoft/pxt/blob/ab97a2422879824c730f009b15d4bf446b0e8547/pxtlib/webusb.ts#L361
501+
private async tryPrevConnectedDevices(): Promise<void> {
502+
const prevPairedDevices = await this.tryGetDevicesAsync();
503+
for (let i = 0; i < prevPairedDevices.length; ++i) {
504+
const d = prevPairedDevices[i];
505+
this.device = d;
506+
this.log(`connect device: ${d.manufacturerName} ${d.productName}`);
507+
this.log(`serial number: ${d.serialNumber}`);
508+
try {
509+
this.connection = new DAPWrapper(this.device, this.logging);
510+
await withTimeout(this.connection.reconnectAsync(), 10_000);
511+
// Success, stop trying.
512+
} catch (e: any) {
513+
// Clean slate and try next one.
514+
this.device = undefined;
515+
this.connection = undefined;
516+
this.log(`connection attempt failed, ${e.message}`);
517+
}
477518
}
519+
}
520+
521+
private async chooseDevice(): Promise<USBDevice> {
478522
this.dispatchTypedEvent("beforerequestdevice", new BeforeRequestDevice());
479523
this.device = await navigator.usb.requestDevice({
480524
exclusionFilters: this.exclusionFilters,
@@ -484,6 +528,19 @@ class MicrobitWebUSBConnectionImpl
484528
return this.device;
485529
}
486530

531+
// Drawn from https://github.com/microsoft/pxt/blob/ab97a2422879824c730f009b15d4bf446b0e8547/pxtlib/webusb.ts#L530
532+
private async tryGetDevicesAsync(): Promise<USBDevice[]> {
533+
this.log("Getting web usb devices");
534+
try {
535+
const devs = await this.withEnrichedErrors(() =>
536+
navigator.usb?.getDevices(),
537+
);
538+
return devs || [];
539+
} catch (e: any) {
540+
return [];
541+
}
542+
}
543+
487544
protected eventActivated(type: string): void {
488545
switch (type as keyof SerialConnectionEventMap) {
489546
case "serialdata": {

0 commit comments

Comments
 (0)