Skip to content

Commit f75b514

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

File tree

2 files changed

+63
-12
lines changed

2 files changed

+63
-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: 61 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) {
@@ -384,6 +391,7 @@ class MicrobitWebUSBConnectionImpl
384391
private setStatus(newStatus: ConnectionStatus) {
385392
this.status = newStatus;
386393
this.visibilityReconnect = false;
394+
console.log(this)
387395
this.log("USB connection status " + newStatus);
388396
this.dispatchTypedEvent("status", new ConnectionStatusEvent(newStatus));
389397
}
@@ -460,21 +468,49 @@ class MicrobitWebUSBConnectionImpl
460468
}
461469

462470
private async connectInternal(): Promise<void> {
463-
if (!this.connection) {
464-
const device = await this.chooseDevice();
465-
this.connection = new DAPWrapper(device, this.logging);
471+
if (!this.connection && this.device) {
472+
this.connection = new DAPWrapper(this.device, this.logging);
473+
await withTimeout(this.connection.reconnectAsync(), 10_000);
474+
} else if (!this.connection) {
475+
if (this.deviceConnectMode === DeviceConnectMode.TryInitialAndPrevPair) {
476+
await this.tryPrevConnectedDevices();
477+
}
478+
if (!this.connection) {
479+
this.device = await this.chooseDevice();
480+
this.connection = new DAPWrapper(this.device, this.logging);
481+
await withTimeout(this.connection.reconnectAsync(), 10_000);
482+
}
483+
} else {
484+
await withTimeout(this.connection.reconnectAsync(), 10_000);
466485
}
467-
await withTimeout(this.connection.reconnectAsync(), 10_000);
468486
if (this.addedListeners.serialdata && !this.flashing) {
469487
this.startSerialInternal();
470488
}
471489
this.setStatus(ConnectionStatus.CONNECTED);
472490
}
473491

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

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

0 commit comments

Comments
 (0)