1- import React , { ReactNode } from "react" ;
2- import { abortCurrentPlan , submitAndRunPlanImmediately } from "./blueapi" ;
1+ import React , { ReactNode , useCallback , useEffect } from "react" ;
2+ import {
3+ abortCurrentPlan ,
4+ submitAndRunPlanImmediately ,
5+ getWorkerStatus ,
6+ type BlueApiWorkerState ,
7+ } from "./blueapi" ;
38import {
49 Alert ,
510 Button ,
@@ -36,6 +41,9 @@ export function RunPlanButton(props: RunPlanButtonProps) {
3641 const [ openSnackbar , setOpenSnackbar ] = React . useState < boolean > ( false ) ;
3742 const [ msg , setMsg ] = React . useState < string > ( "Running plan..." ) ;
3843 const [ severity , setSeverity ] = React . useState < SeverityLevel > ( "info" ) ;
44+ const [ isPolling , setIsPolling ] = React . useState < boolean > ( false ) ;
45+ const [ initialWorkerState , setInitialWorkerState ] =
46+ React . useState < BlueApiWorkerState | null > ( null ) ;
3947
4048 let fullVisit : string ;
4149 if ( props . currentVisit === undefined ) {
@@ -54,28 +62,68 @@ export function RunPlanButton(props: RunPlanButtonProps) {
5462 const sx = props . sx ? { ...buttonStyles , ...props . sx } : { } ; // Style for the button component which is the most likely to be customised
5563 const tooltipSx = props . tooltipSx ? props . tooltipSx : { } ;
5664
57- const handleClick = ( ) => {
65+ const pollWorkerStatus = useCallback ( async ( ) => {
66+ try {
67+ const currentState : BlueApiWorkerState = await getWorkerStatus ( ) ;
68+
69+ if ( initialWorkerState === "RUNNING" && currentState === "IDLE" ) {
70+ setSeverity ( "success" ) ;
71+ setMsg ( "Plan completed successfully" ) ;
72+ setIsPolling ( false ) ;
73+ setInitialWorkerState ( null ) ;
74+ return ;
75+ }
76+
77+ if ( currentState === "PANICKED" ) {
78+ setSeverity ( "error" ) ;
79+ setMsg ( "Plan failed." ) ;
80+ setIsPolling ( false ) ;
81+ setInitialWorkerState ( null ) ;
82+ return ;
83+ }
84+ } catch ( error ) {
85+ console . error ( "Error polling worker status:" , error ) ;
86+ setIsPolling ( false ) ;
87+ setInitialWorkerState ( null ) ;
88+ }
89+ } , [ initialWorkerState ] ) ;
90+
91+ useEffect ( ( ) => {
92+ let intervalId : NodeJS . Timeout | null = null ;
93+ if ( isPolling ) {
94+ intervalId = setInterval ( pollWorkerStatus , 1000 ) ;
95+ }
96+ return ( ) => {
97+ if ( intervalId ) {
98+ clearInterval ( intervalId ) ;
99+ }
100+ } ;
101+ } , [ isPolling , pollWorkerStatus ] ) ;
102+
103+ const handleClick = async ( ) => {
58104 setOpenSnackbar ( true ) ;
59105 try {
106+ setSeverity ( "info" ) ;
107+ setMsg ( "Running plan..." ) ;
108+
109+ const initialState = await getWorkerStatus ( ) ;
110+ setInitialWorkerState ( initialState ) ;
60111 instrumentSession = parseInstrumentSession ( fullVisit ) ;
61112 console . log ( `Current instrument session: ${ instrumentSession } ` ) ;
62- submitAndRunPlanImmediately ( {
113+ await submitAndRunPlanImmediately ( {
63114 planName : props . planName ,
64115 planParams : params ,
65116 instrumentSession : instrumentSession ,
66- } ) . catch ( ( error ) => {
67- setSeverity ( "error" ) ;
68- setMsg (
69- `Failed to run plan ${ props . planName } , see console and logs for full error` ,
70- ) ;
71- console . log ( `${ msg } . Reason: ${ error } ` ) ;
72117 } ) ;
118+ setIsPolling ( true ) ;
73119 } catch ( error ) {
74120 setSeverity ( "error" ) ;
75121 setMsg (
76- `Failed to run plan ${ props . planName } , please check visit PV is set. ` ,
122+ `Failed to run plan ${ props . planName } , see console and logs for full error ` ,
77123 ) ;
78- console . log ( `An error occurred ${ error } ` ) ;
124+ console . error ( `${ msg } . Reason: ${ error } ` ) ;
125+ setIsPolling ( false ) ;
126+ setInitialWorkerState ( null ) ;
79127 }
80128 } ;
81129
@@ -173,7 +221,7 @@ export function AbortButton() {
173221 anchorOrigin = { { vertical : "bottom" , horizontal : "right" } }
174222 >
175223 < Alert onClose = { handleMsgClose } severity = "warning" >
176- Aborting plan ...
224+ Aborting plan...
177225 </ Alert >
178226 </ Snackbar >
179227 </ div >
0 commit comments