From 5c7bbcd58f605952d24a906f4ae3a53dd169f4e8 Mon Sep 17 00:00:00 2001 From: Amal Nanavati Date: Wed, 28 Aug 2024 10:25:35 -0700 Subject: [PATCH 1/2] Allow users to skip acquisition on error --- feedingwebapp/src/Pages/Constants.js | 6 -- feedingwebapp/src/Pages/Home/Home.jsx | 5 ++ .../src/Pages/Home/MealStates/RobotMotion.jsx | 82 +++++++++++++++---- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/feedingwebapp/src/Pages/Constants.js b/feedingwebapp/src/Pages/Constants.js index cc7aae97..0bf1cd4f 100644 --- a/feedingwebapp/src/Pages/Constants.js +++ b/feedingwebapp/src/Pages/Constants.js @@ -43,12 +43,6 @@ export const SERVO_CARTESIAN_TOPIC_MSG = 'geometry_msgs/msg/TwistStamped' export const SERVO_JOINT_TOPIC = '/web_app/servo_node/delta_joint_cmds' export const SERVO_JOINT_TOPIC_MSG = 'control_msgs/msg/JointJog' -// States from which, if they fail, it is NOT okay for the user to retry the -// same action. -let NON_RETRYABLE_STATES = new Set() -NON_RETRYABLE_STATES.add(MEAL_STATE.R_BiteAcquisition) -export { NON_RETRYABLE_STATES } - /** * For states that call ROS actions, this dictionary contains * the action name and the message type diff --git a/feedingwebapp/src/Pages/Home/Home.jsx b/feedingwebapp/src/Pages/Home/Home.jsx index 835e1a72..d9922125 100644 --- a/feedingwebapp/src/Pages/Home/Home.jsx +++ b/feedingwebapp/src/Pages/Home/Home.jsx @@ -118,6 +118,8 @@ function Home(props) { let currentMealState = MEAL_STATE.R_BiteAcquisition let nextMealState = MEAL_STATE.U_BiteAcquisitionCheck let backMealState = MEAL_STATE.R_MovingAbovePlate + let errorMealState = MEAL_STATE.R_MovingToRestingPosition + let errorMealStateDescription = 'Proceed' return ( ) } diff --git a/feedingwebapp/src/Pages/Home/MealStates/RobotMotion.jsx b/feedingwebapp/src/Pages/Home/MealStates/RobotMotion.jsx index b99958a9..8135d530 100644 --- a/feedingwebapp/src/Pages/Home/MealStates/RobotMotion.jsx +++ b/feedingwebapp/src/Pages/Home/MealStates/RobotMotion.jsx @@ -22,7 +22,6 @@ import { useGlobalState } from '../../GlobalState' import { CLEAR_OCTOMAP_SERVICE_NAME, CLEAR_OCTOMAP_SERVICE_TYPE, - NON_RETRYABLE_STATES, ROS_ACTIONS_NAMES, MOTION_STATUS_SUCCESS, ROS_ACTION_STATUS_CANCEL_GOAL, @@ -124,15 +123,28 @@ const RobotMotion = (props) => { [setActionStatus] ) + /** + * Callback function to change the meal state. + */ + const changeMealState = useCallback( + (nextMealState, msg = null) => { + if (msg) { + console.log(msg) + } + setPaused(false) + let setMealState = props.setMealState + setMealState(nextMealState) + }, + [setPaused, props.setMealState] + ) + /** * Callback function for when the robot has finished moving to its staging * location. */ const robotMotionDone = useCallback(() => { - console.log('robotMotionDone') - let setMealState = props.setMealState - setMealState(props.nextMealState) - }, [props.nextMealState, props.setMealState]) + changeMealState(props.nextMealState, 'robotMotionDone') + }, [changeMealState, props.nextMealState]) /** * Callback function for when the action sends a response. It updates the @@ -264,10 +276,8 @@ const RobotMotion = (props) => { }, [clearOctomapService, resumeCallback]) const backCallback = useCallback(() => { - setPaused(false) - let setMealState = props.setMealState - setMealState(props.backMealState) - }, [setPaused, props.backMealState, props.setMealState]) + changeMealState(props.backMealState, 'backCallback') + }, [changeMealState, props.backMealState]) /** * Get the action status text and progress bar or blank view to render. @@ -310,6 +320,22 @@ const RobotMotion = (props) => { ) : ( <> )} + {props.errorMealState ? ( + + ) : ( + <> + )} { ) }, - [dimension, props.waitingText, motionTextFontSize, waitingTextFontSize, retryCallback] + [ + dimension, + props.waitingText, + props.errorMealState, + props.errorMealStateDescription, + motionTextFontSize, + waitingTextFontSize, + retryCallback, + changeMealState + ] ) /** @@ -342,7 +377,6 @@ const RobotMotion = (props) => { let showTime = false let time = 0 let progress = null - let retry = false switch (actionStatus.actionStatus) { case ROS_ACTION_STATUS_EXECUTE: if (actionStatus.feedback) { @@ -377,9 +411,19 @@ const RobotMotion = (props) => { * users on how to troubleshoot/fix it. */ text = 'Robot encountered an error' - retry = NON_RETRYABLE_STATES.has(props.mealState) ? false : true return ( - <>{actionStatusTextAndVisual(flexSizeOuter, flexSizeTextInner, flexSizeVisualInner, text, showTime, time, progress, retry)} + <> + {actionStatusTextAndVisual( + flexSizeOuter, + flexSizeTextInner, + flexSizeVisualInner, + text, + showTime, + time, + progress, + props.allowRetry + )} + ) case ROS_ACTION_STATUS_CANCELED: return <>{actionStatusTextAndVisual(flexSizeOuter, flexSizeTextInner, flexSizeVisualInner, text, showTime, time, progress)} @@ -397,7 +441,7 @@ const RobotMotion = (props) => { } } }, - [paused, dimension, actionStatusTextAndVisual, props.mealState] + [paused, dimension, actionStatusTextAndVisual, props.allowRetry] ) // Render the component @@ -421,7 +465,7 @@ const RobotMotion = (props) => { pauseCallback={pauseCallback} backCallback={props.backMealState ? backCallback : null} backMealState={props.backMealState} - resumeCallback={NON_RETRYABLE_STATES.has(props.mealState) ? null : resumeCallback} + resumeCallback={props.allowRetry ? resumeCallback : null} paused={paused} /> @@ -454,10 +498,16 @@ RobotMotion.propTypes = { // the action client, then calling it again, etc.) actionInput: PropTypes.object.isRequired, // The static text to display while the robot is executing the action - waitingText: PropTypes.string.isRequired + waitingText: PropTypes.string.isRequired, + // Whether to show the retry/resume option if the action fails + allowRetry: PropTypes.bool, + // If error, show the user the option to transition to this meal state + errorMealState: PropTypes.string, + errorMealStateDescription: PropTypes.string } RobotMotion.defaultProps = { + allowRetry: true, debug: false } From eef597aaf88e01e31b1fdf0b894ec459c2006443 Mon Sep 17 00:00:00 2001 From: Amal Nanavati Date: Wed, 28 Aug 2024 11:03:47 -0700 Subject: [PATCH 2/2] Fixes from testing --- feedingwebapp/src/Pages/Home/Home.jsx | 3 +- .../src/Pages/Home/MealStates/RobotMotion.jsx | 86 +++++++++---------- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/feedingwebapp/src/Pages/Home/Home.jsx b/feedingwebapp/src/Pages/Home/Home.jsx index d9922125..86424819 100644 --- a/feedingwebapp/src/Pages/Home/Home.jsx +++ b/feedingwebapp/src/Pages/Home/Home.jsx @@ -118,8 +118,9 @@ function Home(props) { let currentMealState = MEAL_STATE.R_BiteAcquisition let nextMealState = MEAL_STATE.U_BiteAcquisitionCheck let backMealState = MEAL_STATE.R_MovingAbovePlate + // TODO: Add an icon for this errorMealState! let errorMealState = MEAL_STATE.R_MovingToRestingPosition - let errorMealStateDescription = 'Proceed' + let errorMealStateDescription = 'Skip Acquisition' return ( { * @param {showTime} - indicates if elapsed time needs to be shown * @param {time} - calculated elapsed time, 0 if time not available * @param {progress} - progress proportion; if null progress bar not shown - * @param {retry} - indicates if retry needed for error + * @param {error} - indicates if there was an error * * @returns {JSX.Element} the action status text, progress bar or blank view */ const actionStatusTextAndVisual = useCallback( - (flexSizeOuter, flexSizeTextInner, flexSizeVisualInner, text, showTime, time, progress, retry = false) => { + (flexSizeOuter, flexSizeTextInner, flexSizeVisualInner, text, showTime, time, progress, error = false) => { return ( <> @@ -304,35 +304,41 @@ const RobotMotion = (props) => {

{text}

{showTime ?

  Elapsed: {time} sec

: <>} - {retry ? ( - - ) : ( - <> - )} - {props.errorMealState ? ( - + {error ? ( + <> + {props.allowRetry ? ( + + ) : ( + <> + )} + {props.errorMealState ? ( + + ) : ( + <> + )} + ) : ( <> )} @@ -355,6 +361,7 @@ const RobotMotion = (props) => { [ dimension, props.waitingText, + props.allowRetry, props.errorMealState, props.errorMealStateDescription, motionTextFontSize, @@ -377,6 +384,7 @@ const RobotMotion = (props) => { let showTime = false let time = 0 let progress = null + let error = false switch (actionStatus.actionStatus) { case ROS_ACTION_STATUS_EXECUTE: if (actionStatus.feedback) { @@ -411,19 +419,9 @@ const RobotMotion = (props) => { * users on how to troubleshoot/fix it. */ text = 'Robot encountered an error' + error = true return ( - <> - {actionStatusTextAndVisual( - flexSizeOuter, - flexSizeTextInner, - flexSizeVisualInner, - text, - showTime, - time, - progress, - props.allowRetry - )} - + <>{actionStatusTextAndVisual(flexSizeOuter, flexSizeTextInner, flexSizeVisualInner, text, showTime, time, progress, error)} ) case ROS_ACTION_STATUS_CANCELED: return <>{actionStatusTextAndVisual(flexSizeOuter, flexSizeTextInner, flexSizeVisualInner, text, showTime, time, progress)} @@ -441,7 +439,7 @@ const RobotMotion = (props) => { } } }, - [paused, dimension, actionStatusTextAndVisual, props.allowRetry] + [paused, dimension, actionStatusTextAndVisual] ) // Render the component