@@ -35,6 +35,32 @@ import { Radio } from "./radio";
3535import { RangeSensor , State } from "./state" ;
3636import { ModuleWrapper } from "./wasm" ;
3737
38+ enum StopKind {
39+ /**
40+ * The main Wasm function returned control to us in a normal way.
41+ */
42+ Default = "default" ,
43+ /**
44+ * The program called panic.
45+ */
46+ Panic = "panic" ,
47+ /**
48+ * The program requested a reset.
49+ */
50+ Reset = "reset" ,
51+ /**
52+ * An internal mode where we do not display the stop state UI as we plan to immediately reset.
53+ * Used for user-requested flash or reset.
54+ */
55+ BriefStop = "brief" ,
56+ /**
57+ * The user requested the program be interrupted.
58+ *
59+ * Note the program could finish for other reasons, but should always count as a user stop.
60+ */
61+ UserStop = "user" ,
62+ }
63+
3864export class PanicError extends Error {
3965 constructor ( public code : number ) {
4066 super ( "panic" ) ;
@@ -74,8 +100,6 @@ export class Board {
74100 radio : Radio ;
75101 dataLogging : DataLogging ;
76102
77- private panicTimeout : any ;
78-
79103 public serialInputBuffer : number [ ] = [ ] ;
80104
81105 private stoppedOverlay : HTMLDivElement ;
@@ -108,10 +132,19 @@ export class Board {
108132 */
109133 private module : ModuleWrapper | undefined ;
110134 /**
111- * If undefined, then when main finishes we stay stopped.
112- * Otherwise we perform the action then clear this field.
135+ * Controls the action after the user program completes.
136+ *
137+ * Determined by a combination of user actions (stop, reset etc) and program actions.
113138 */
114- private afterStopped : ( ( ) => void ) | undefined ;
139+ private stopKind : StopKind = StopKind . Default ;
140+ /**
141+ * Timeout for a pending start call due to StopKind.Reset.
142+ */
143+ private pendingRestartTimeout : any ;
144+ /**
145+ * Timeout for the next frame of the panic animation.
146+ */
147+ private panicTimeout : any ;
115148
116149 constructor (
117150 private notifications : Notifications ,
@@ -356,6 +389,8 @@ export class Board {
356389 if ( this . modulePromise || this . module ) {
357390 throw new Error ( "Module already exists!" ) ;
358391 }
392+ clearTimeout ( this . pendingRestartTimeout ) ;
393+ this . pendingRestartTimeout = null ;
359394
360395 this . modulePromise = this . createModule ( ) ;
361396 const module = await this . modulePromise ;
@@ -365,11 +400,17 @@ export class Board {
365400 this . displayRunningState ( ) ;
366401 await module . start ( ) ;
367402 } catch ( e : any ) {
403+ // Take care not to overwrite another kind of stop just because the program
404+ // called restart or panic.
368405 if ( e instanceof PanicError ) {
369- panicCode = e . code ;
406+ if ( this . stopKind === StopKind . Default ) {
407+ this . stopKind = StopKind . Panic ;
408+ panicCode = e . code ;
409+ }
370410 } else if ( e instanceof ResetError ) {
371- const noChangeRestart = ( ) => { } ;
372- this . afterStopped = noChangeRestart ;
411+ if ( this . stopKind === StopKind . Default ) {
412+ this . stopKind = StopKind . Reset ;
413+ }
373414 } else {
374415 this . notifications . onInternalError ( e ) ;
375416 }
@@ -386,30 +427,52 @@ export class Board {
386427 this . modulePromise = undefined ;
387428 this . module = undefined ;
388429
389- if ( panicCode !== undefined ) {
390- this . displayPanic ( panicCode ) ;
391- } else {
392- if ( this . afterStopped ) {
393- this . afterStopped ( ) ;
394- this . afterStopped = undefined ;
395- setTimeout ( ( ) => this . start ( ) , 0 ) ;
396- } else {
430+ switch ( this . stopKind ) {
431+ case StopKind . Panic : {
432+ if ( panicCode === undefined ) {
433+ throw new Error ( "Must be set" ) ;
434+ }
435+ this . displayPanic ( panicCode ) ;
436+ break ;
437+ }
438+ case StopKind . Reset : {
439+ this . pendingRestartTimeout = setTimeout ( ( ) => this . start ( ) , 0 ) ;
440+ break ;
441+ }
442+ case StopKind . BriefStop : {
443+ // Skip the stopped state.
444+ break ;
445+ }
446+ case StopKind . UserStop : /* Fall through */
447+ case StopKind . Default : {
397448 this . displayStoppedState ( ) ;
449+ break ;
450+ }
451+ default : {
452+ throw new Error ( "Unknown stop kind: " + this . stopKind ) ;
398453 }
399454 }
455+ this . stopKind = StopKind . Default ;
400456 }
401457
402- async stop (
403- afterStopped : ( ( ) => void ) | undefined = undefined
404- ) : Promise < void > {
405- this . afterStopped = afterStopped ;
458+ async stop ( brief : boolean = false ) : Promise < void > {
406459 if ( this . panicTimeout ) {
407460 clearTimeout ( this . panicTimeout ) ;
408461 this . panicTimeout = null ;
409462 this . display . clear ( ) ;
410- this . displayStoppedState ( ) ;
463+ if ( ! brief ) {
464+ this . displayStoppedState ( ) ;
465+ }
466+ }
467+ if ( this . pendingRestartTimeout ) {
468+ clearTimeout ( this . pendingRestartTimeout ) ;
469+ this . pendingRestartTimeout = null ;
470+ if ( ! brief ) {
471+ this . displayStoppedState ( ) ;
472+ }
411473 }
412474 if ( this . modulePromise ) {
475+ this . stopKind = brief ? StopKind . BriefStop : StopKind . UserStop ;
413476 // Avoid this.module as we might still be creating it (async).
414477 const module = await this . modulePromise ;
415478 module . requestStop ( ) ;
@@ -422,11 +485,10 @@ export class Board {
422485
423486 /**
424487 * An external reset.
425- * reset() in MicroPython code throws ResetError.
426488 */
427489 async reset ( ) : Promise < void > {
428- const noChangeRestart = ( ) => { } ;
429- this . stop ( noChangeRestart ) ;
490+ await this . stop ( true ) ;
491+ return this . start ( ) ;
430492 }
431493
432494 async flash ( filesystem : Record < string , Uint8Array > ) : Promise < void > {
@@ -440,7 +502,7 @@ export class Board {
440502 } ;
441503 if ( this . modulePromise ) {
442504 // If it's running then we need to stop before flash.
443- return this . stop ( flashFileSystem ) ;
505+ await this . stop ( true ) ;
444506 }
445507 flashFileSystem ( ) ;
446508 return this . start ( ) ;
0 commit comments