Skip to content

Commit 997a4b6

Browse files
Add device connect mode
1 parent 869c6a9 commit 997a4b6

File tree

2 files changed

+62
-12
lines changed

2 files changed

+62
-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: 60 additions & 12 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

44-
logging: Logging;
49+
logging?: Logging;
50+
deviceConnectMode?: DeviceConnectMode;
4551
}
4652

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

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

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

184-
constructor(
185-
options: MicrobitWebUSBConnectionOptions = { logging: new NullLogging() },
186-
) {
191+
constructor(options: MicrobitWebUSBConnectionOptions = {}) {
187192
super();
188-
this.logging = options.logging;
193+
this.logging = options.logging || new NullLogging();
194+
this.deviceConnectMode =
195+
options.deviceConnectMode || DeviceConnectMode.TryInitialPair;
189196
}
190197

191198
private log(v: any) {
@@ -460,21 +467,49 @@ class MicrobitWebUSBConnectionImpl
460467
}
461468

462469
private async connectInternal(): Promise<void> {
463-
if (!this.connection) {
464-
const device = await this.chooseDevice();
465-
this.connection = new DAPWrapper(device, this.logging);
470+
if (!this.connection && this.device) {
471+
this.connection = new DAPWrapper(this.device, this.logging);
472+
await withTimeout(this.connection.reconnectAsync(), 10_000);
473+
} else if (!this.connection) {
474+
if (this.deviceConnectMode === DeviceConnectMode.TryInitialAndPrevPair) {
475+
await this.tryPrevConnectedDevices();
476+
}
477+
if (!this.connection) {
478+
this.device = await this.chooseDevice();
479+
this.connection = new DAPWrapper(this.device, this.logging);
480+
await withTimeout(this.connection.reconnectAsync(), 10_000);
481+
}
482+
} else {
483+
await withTimeout(this.connection.reconnectAsync(), 10_000);
466484
}
467-
await withTimeout(this.connection.reconnectAsync(), 10_000);
468485
if (this.addedListeners.serialdata && !this.flashing) {
469486
this.startSerialInternal();
470487
}
471488
this.setStatus(ConnectionStatus.CONNECTED);
472489
}
473490

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

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

0 commit comments

Comments
 (0)