Skip to content

Commit 9435060

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

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
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: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,17 @@ export const isChromeOS105 = (): boolean => {
3737
return /CrOS/.test(userAgent) && /Chrome\/105\b/.test(userAgent);
3838
};
3939

40+
export enum DeviceConnectMode {
41+
TryInitialPair = "try initial pair",
42+
TryInitialAndPrevPair = "try initial and prev pair",
43+
}
44+
4045
export interface MicrobitWebUSBConnectionOptions {
4146
// We should copy this type when extracting a library, and make it optional.
4247
// Coupling for now to make it easy to evolve.
4348

4449
logging: Logging;
50+
deviceConnectMode: DeviceConnectMode;
4551
}
4652

4753
export interface MicrobitWebUSBConnection
@@ -176,16 +182,21 @@ class MicrobitWebUSBConnectionImpl
176182
};
177183

178184
private logging: Logging;
185+
private deviceConnectMode: DeviceConnectMode;
179186

180187
private addedListeners: Record<string, number> = {
181188
serialdata: 0,
182189
};
183190

184191
constructor(
185-
options: MicrobitWebUSBConnectionOptions = { logging: new NullLogging() },
192+
options: MicrobitWebUSBConnectionOptions = {
193+
logging: new NullLogging(),
194+
deviceConnectMode: DeviceConnectMode.TryInitialPair,
195+
},
186196
) {
187197
super();
188198
this.logging = options.logging;
199+
this.deviceConnectMode = options.deviceConnectMode;
189200
}
190201

191202
private log(v: any) {
@@ -460,21 +471,49 @@ class MicrobitWebUSBConnectionImpl
460471
}
461472

462473
private async connectInternal(): Promise<void> {
463-
if (!this.connection) {
464-
const device = await this.chooseDevice();
465-
this.connection = new DAPWrapper(device, this.logging);
474+
if (!this.connection && this.device) {
475+
this.connection = new DAPWrapper(this.device, this.logging);
476+
await withTimeout(this.connection.reconnectAsync(), 10_000);
477+
} else if (!this.connection) {
478+
if (this.deviceConnectMode === DeviceConnectMode.TryInitialAndPrevPair) {
479+
await this.tryPrevConnectedDevices();
480+
}
481+
if (!this.connection) {
482+
this.device = await this.chooseDevice();
483+
this.connection = new DAPWrapper(this.device, this.logging);
484+
await withTimeout(this.connection.reconnectAsync(), 10_000);
485+
}
486+
} else {
487+
await withTimeout(this.connection.reconnectAsync(), 10_000);
466488
}
467-
await withTimeout(this.connection.reconnectAsync(), 10_000);
468489
if (this.addedListeners.serialdata && !this.flashing) {
469490
this.startSerialInternal();
470491
}
471492
this.setStatus(ConnectionStatus.CONNECTED);
472493
}
473494

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

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

0 commit comments

Comments
 (0)