@@ -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 ) {
@@ -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