@@ -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
44- logging : Logging ;
49+ logging ?: Logging ;
50+ deviceConnectMode ?: DeviceConnectMode ;
4551}
4652
4753export 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