@@ -37,11 +37,17 @@ export const isChromeOS105 = (): boolean => {
3737 return / C r O S / . test ( userAgent ) && / C h r o m e \/ 1 0 5 \b / . test ( userAgent ) ;
3838} ;
3939
40+ export enum DeviceConnectMode {
41+ TryInitialPair = "try initial pair" ,
42+ TryInitialAndPrevPair = "try initial and prev pair" ,
43+ }
44+
4045export 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
4753export 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