Skip to content

Commit 3d03af5

Browse files
authored
Allow users to customize the resting configuration (#135)
* Generalize the AbovePlate component * Added customize resting configuration screen
1 parent d545443 commit 3d03af5

File tree

5 files changed

+139
-77
lines changed

5 files changed

+139
-77
lines changed

feedingwebapp/src/Pages/GlobalState.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export { NON_MOVING_STATES }
8282
export const SETTINGS_STATE = {
8383
MAIN: 'MAIN',
8484
BITE_TRANSFER: 'BITE_TRANSFER',
85-
ABOVE_PLATE: 'ABOVE_PLATE'
85+
ABOVE_PLATE: 'ABOVE_PLATE',
86+
RESTING_CONFIGURATION: 'RESTING_CONFIGURATION'
8687
}
8788

8889
// The name of the default parameter namespace

feedingwebapp/src/Pages/Header/TeleopSubcomponent.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,14 @@ const TeleopSubcomponent = (props) => {
152152
* When the component is unmounted, stop servo.
153153
*/
154154
useEffect(() => {
155-
let stopServoSuccessCallback = props.stopServoSuccessCallback
155+
let unmountCallback = props.unmountCallback
156156
return () => {
157157
console.log('Unmounting teleop subcomponent.')
158158
destroyActionClient(startCartesianControllerAction)
159159
destroyActionClient(startJointControllerAction)
160-
stopServoSuccessCallback.current()
160+
unmountCallback.current()
161161
}
162-
}, [startCartesianControllerAction, startJointControllerAction, props.stopServoSuccessCallback])
162+
}, [startCartesianControllerAction, startJointControllerAction, props.unmountCallback])
163163

164164
/**
165165
* Callback function to publish constant cartesian cartesian velocity commands.
@@ -650,12 +650,12 @@ const TeleopSubcomponent = (props) => {
650650
}
651651
TeleopSubcomponent.propTypes = {
652652
// A reference to a function to be called if StopServo is succesfully run.
653-
stopServoSuccessCallback: PropTypes.object,
653+
unmountCallback: PropTypes.object,
654654
// A function to be called when one of the teleop buttons are released
655655
teleopButtonOnReleaseCallback: PropTypes.func
656656
}
657657
TeleopSubcomponent.defaultProps = {
658-
stopServoSuccessCallback: { current: () => {} },
658+
unmountCallback: { current: () => {} },
659659
teleopButtonOnReleaseCallback: () => {}
660660
}
661661
export default TeleopSubcomponent

feedingwebapp/src/Pages/Settings/AbovePlate.jsx renamed to feedingwebapp/src/Pages/Settings/CustomizeConfiguration.jsx

Lines changed: 84 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { View } from 'react-native'
88
// Local imports
99
import { useROS, createROSService, createROSServiceRequest } from '../../ros/ros_helpers'
1010
import {
11-
ABOVE_PLATE_PARAM_JOINTS,
1211
CAMERA_FEED_TOPIC,
1312
getRobotMotionText,
1413
GET_JOINT_STATE_SERVICE_NAME,
@@ -23,10 +22,16 @@ import SettingsPageParent from './SettingsPageParent'
2322
import VideoFeed from '../Home/VideoFeed'
2423

2524
/**
26-
* The AbovePlate component allows users to configure the "above plate" configuration
27-
* the robot moves to before bite selection.
25+
* The CustomizeConfiguration component allows users to configure the one of the
26+
* fixed configurations the robot uses. In its current form, the node can take in
27+
* multiple parameters, but will set them all to the same joint state positions.
2828
*/
29-
const AbovePlate = (props) => {
29+
const CustomizeConfiguration = (props) => {
30+
// Check the props
31+
if (Object.values(MEAL_STATE).indexOf(props.startingMealState) === -1) {
32+
throw new Error('Invalid starting meal state ' + props.startingMealState)
33+
}
34+
3035
// Get relevant global state variables
3136
const setSettingsState = useGlobalState((state) => state.setSettingsState)
3237
const globalMealState = useGlobalState((state) => state.mealState)
@@ -35,18 +40,17 @@ const AbovePlate = (props) => {
3540

3641
// Create relevant local state variables
3742
// Configure the parameters for SettingsPageParent
38-
const paramNames = useMemo(() => [ABOVE_PLATE_PARAM_JOINTS], [])
39-
const [currentAbovePlateParam, setCurrentAbovePlateParam] = useState([null])
43+
const [currentConfigurationParams, setCurrentConfigurationParams] = useState(props.paramNames.map(() => null))
4044
const [localCurrAndNextMealState, setLocalCurrAndNextMealState] = useState(
4145
globalMealState === MEAL_STATE.U_BiteDone || biteTransferPageAtFace
42-
? [MEAL_STATE.R_MovingFromMouth, MEAL_STATE.R_MovingAbovePlate]
43-
: [MEAL_STATE.R_MovingAbovePlate, null]
46+
? [MEAL_STATE.R_MovingFromMouth, props.startingMealState]
47+
: [props.startingMealState, null]
4448
)
4549
const actionInput = useMemo(() => ({}), [])
4650
const doneButtonIsClicked = useRef(false)
4751
const [zoomLevel, setZoomLevel] = useState(1.0)
4852
const [mountTeleopSubcomponent, setMountTeleopSubcomponent] = useState(false)
49-
const stopServoSuccessCallback = useRef(() => {})
53+
const unmountTeleopSubcomponentCallback = useRef(() => {})
5054

5155
// Flag to check if the current orientation is portrait
5256
const isPortrait = useMediaQuery({ query: '(orientation: portrait)' })
@@ -62,8 +66,8 @@ const AbovePlate = (props) => {
6266
(newLocalCurrMealState, newLocalNextMealState = null) => {
6367
console.log('setLocalCurrMealStateWrapper evaluated')
6468
let oldLocalCurrMealState = localCurrAndNextMealState[0]
65-
// Only mount the teleop subcomponent if the robot finished moving above the plate
66-
if (newLocalCurrMealState === null && oldLocalCurrMealState === MEAL_STATE.R_MovingAbovePlate) {
69+
// Only mount the teleop subcomponent if the robot finished the prereq motion for this page
70+
if (newLocalCurrMealState === null && oldLocalCurrMealState === props.startingMealState) {
6771
setMountTeleopSubcomponent(true)
6872
}
6973
// Start in a moving state, not a paused state
@@ -77,18 +81,24 @@ const AbovePlate = (props) => {
7781
setLocalCurrAndNextMealState([newLocalCurrMealState, newLocalNextMealState])
7882
}
7983
},
80-
[localCurrAndNextMealState, setLocalCurrAndNextMealState, setMountTeleopSubcomponent, doneButtonIsClicked, setPaused, setSettingsState]
84+
[
85+
doneButtonIsClicked,
86+
localCurrAndNextMealState,
87+
props.startingMealState,
88+
setLocalCurrAndNextMealState,
89+
setMountTeleopSubcomponent,
90+
setPaused,
91+
setSettingsState
92+
]
8193
)
8294

8395
// Get the function that sets the local curr meal state and next meal state variables.
8496
// Note this does not execute it. It is used to pass the function to other components.
8597
const getSetLocalCurrMealStateWrapper = useCallback(
8698
(newLocalCurrMealState, newLocalNextMealState = null) => {
87-
console.log('getSetLocalCurrMealStateWrapper evaluated')
8899
let retval = () => {
89-
console.log('getSetLocalCurrMealStateWrapper retval evaluated')
90100
setLocalCurrMealStateWrapper(newLocalCurrMealState, newLocalNextMealState)
91-
stopServoSuccessCallback.current = () => {}
101+
unmountTeleopSubcomponentCallback.current = () => {}
92102
}
93103
// If the teleop subcomponent was mounted, unmount it and let the stopServo
94104
// success callback handle the rest. However, sometimes the success message
@@ -101,7 +111,7 @@ const AbovePlate = (props) => {
101111
}
102112
return retval
103113
},
104-
[mountTeleopSubcomponent, setLocalCurrMealStateWrapper, setMountTeleopSubcomponent, stopServoSuccessCallback]
114+
[mountTeleopSubcomponent, setLocalCurrMealStateWrapper, setMountTeleopSubcomponent, unmountTeleopSubcomponentCallback]
105115
)
106116

107117
// Store the props for the RobotMotion call.
@@ -149,17 +159,17 @@ const AbovePlate = (props) => {
149159
})
150160
service.callService(request, (response) => {
151161
console.log('Got joint state response', response)
152-
setCurrentAbovePlateParam([response.joint_state.position])
162+
setCurrentConfigurationParams(props.paramNames.map(() => response.joint_state.position))
153163
})
154-
}, [getJointStateService, setCurrentAbovePlateParam])
164+
}, [getJointStateService, props.paramNames, setCurrentConfigurationParams])
155165

156166
// Callback to move the robot to another configuration
157167
const moveToButtonClicked = useCallback(
158168
(nextMealState) => {
159169
doneButtonIsClicked.current = false
160-
stopServoSuccessCallback.current = getSetLocalCurrMealStateWrapper(nextMealState)
170+
unmountTeleopSubcomponentCallback.current = getSetLocalCurrMealStateWrapper(nextMealState)
161171
},
162-
[getSetLocalCurrMealStateWrapper, doneButtonIsClicked, stopServoSuccessCallback]
172+
[getSetLocalCurrMealStateWrapper, doneButtonIsClicked, unmountTeleopSubcomponentCallback]
163173
)
164174

165175
// Callback to return to the main settings page
@@ -191,11 +201,11 @@ const AbovePlate = (props) => {
191201
localCurrMealState = MEAL_STATE.R_MovingAbovePlate
192202
break
193203
}
194-
stopServoSuccessCallback.current = getSetLocalCurrMealStateWrapper(localCurrMealState, localNextMealState)
195-
}, [getSetLocalCurrMealStateWrapper, globalMealState, doneButtonIsClicked, stopServoSuccessCallback])
204+
unmountTeleopSubcomponentCallback.current = getSetLocalCurrMealStateWrapper(localCurrMealState, localNextMealState)
205+
}, [getSetLocalCurrMealStateWrapper, globalMealState, doneButtonIsClicked, unmountTeleopSubcomponentCallback])
196206

197207
// Callback to render the main contents of the page
198-
const renderAbovePlateSettings = useCallback(() => {
208+
const renderSettings = useCallback(() => {
199209
return (
200210
<View
201211
style={{
@@ -216,14 +226,14 @@ const AbovePlate = (props) => {
216226
<>
217227
<View style={{ flex: 5, alignItems: 'center', justifyContent: 'center', width: '95%', height: '95%' }}>
218228
<TeleopSubcomponent
219-
stopServoSuccessCallback={stopServoSuccessCallback}
229+
unmountCallback={unmountTeleopSubcomponentCallback}
220230
teleopButtonOnReleaseCallback={storeJointStatesAsLocalParam}
221231
/>
222232
</View>
223233
</>
224234
) : (
225235
<p style={{ textAlign: 'center', fontSize: (textFontSize * 0.75).toString() + sizeSuffix, margin: 0 }} className='txt-huge'>
226-
To tune the &ldquo;Above Plate&rdquo; configuration, first &ldquo;Move Above Plate.&rdquo;
236+
To tune the &ldquo;{props.configurationName}&rdquo; configuration, first &ldquo;{props.buttonName}.&rdquo;
227237
</p>
228238
)}
229239
</View>
@@ -238,38 +248,24 @@ const AbovePlate = (props) => {
238248
height: '100%'
239249
}}
240250
>
241-
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}>
242-
<Button
243-
variant='warning'
244-
className='mx-2 mb-2 btn-huge'
245-
size='lg'
246-
style={{
247-
fontSize: (textFontSize * 0.5).toString() + sizeSuffix,
248-
width: '90%',
249-
height: '90%',
250-
color: 'black'
251-
}}
252-
onClick={() => moveToButtonClicked(MEAL_STATE.R_MovingToStagingConfiguration)}
253-
>
254-
Move To Staging
255-
</Button>
256-
</View>
257-
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}>
258-
<Button
259-
variant='warning'
260-
className='mx-2 mb-2 btn-huge'
261-
size='lg'
262-
style={{
263-
fontSize: (textFontSize * 0.5).toString() + sizeSuffix,
264-
width: '90%',
265-
height: '90%',
266-
color: 'black'
267-
}}
268-
onClick={() => moveToButtonClicked(MEAL_STATE.R_MovingToRestingPosition)}
269-
>
270-
Move To Resting
271-
</Button>
272-
</View>
251+
{props.otherButtonConfigs.map(({ name, mealState }) => (
252+
<View key={name} style={{ flex: 1, alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}>
253+
<Button
254+
variant='warning'
255+
className='mx-2 mb-2 btn-huge'
256+
size='lg'
257+
style={{
258+
fontSize: (textFontSize * 0.5).toString() + sizeSuffix,
259+
width: '90%',
260+
height: '90%',
261+
color: 'black'
262+
}}
263+
onClick={() => moveToButtonClicked(mealState)}
264+
>
265+
{name}
266+
</Button>
267+
</View>
268+
))}
273269
<View style={{ flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}>
274270
<Button
275271
variant='warning'
@@ -281,9 +277,9 @@ const AbovePlate = (props) => {
281277
height: '90%',
282278
color: 'black'
283279
}}
284-
onClick={() => moveToButtonClicked(MEAL_STATE.R_MovingAbovePlate)}
280+
onClick={() => moveToButtonClicked(props.startingMealState)}
285281
>
286-
Move Above Plate
282+
{props.buttonName}
287283
</Button>
288284
</View>
289285
</View>
@@ -295,10 +291,14 @@ const AbovePlate = (props) => {
295291
textFontSize,
296292
sizeSuffix,
297293
zoomLevel,
294+
props.buttonName,
295+
props.configurationName,
296+
props.otherButtonConfigs,
297+
props.startingMealState,
298298
props.webrtcURL,
299299
mountTeleopSubcomponent,
300300
moveToButtonClicked,
301-
stopServoSuccessCallback,
301+
unmountTeleopSubcomponentCallback,
302302
storeJointStatesAsLocalParam
303303
])
304304

@@ -311,6 +311,7 @@ const AbovePlate = (props) => {
311311

312312
// Render the modal body, for calling robot code from within this settings page
313313
const renderModalBody = useCallback(() => {
314+
console.log('renderModalBody', localCurrAndNextMealState, robotMotionProps)
314315
let localCurrMealState = localCurrAndNextMealState[0]
315316
switch (localCurrMealState) {
316317
case MEAL_STATE.R_MovingToStagingConfiguration:
@@ -338,23 +339,38 @@ const AbovePlate = (props) => {
338339

339340
return (
340341
<SettingsPageParent
341-
title='Above Plate &#9881;'
342+
title={props.configurationName + ' \u2699'}
342343
doneCallback={doneButtonClicked}
343344
modalShow={localCurrAndNextMealState[0] !== null}
344345
modalOnHide={() => setLocalCurrMealStateWrapper(null)}
345346
modalChildren={renderModalBody()}
346-
paramNames={paramNames}
347-
localParamValues={currentAbovePlateParam}
348-
setLocalParamValues={setCurrentAbovePlateParam}
349-
resetToPresetSuccessCallback={() => moveToButtonClicked(MEAL_STATE.R_MovingAbovePlate)}
347+
paramNames={props.paramNames}
348+
localParamValues={currentConfigurationParams}
349+
setLocalParamValues={setCurrentConfigurationParams}
350+
resetToPresetSuccessCallback={() => moveToButtonClicked(props.startingMealState)}
350351
>
351-
{renderAbovePlateSettings()}
352+
{renderSettings()}
352353
</SettingsPageParent>
353354
)
354355
}
355-
AbovePlate.propTypes = {
356+
CustomizeConfiguration.propTypes = {
357+
// The meal state that must be executed before this page is rendered
358+
startingMealState: PropTypes.string.isRequired,
359+
// The names of the parameter this component should tune.
360+
paramNames: PropTypes.arrayOf(PropTypes.string).isRequired,
361+
// The name of the configuration this component tunes.
362+
configurationName: PropTypes.string.isRequired,
363+
// The name of the button that should be clicked to tune the configuration.
364+
buttonName: PropTypes.string.isRequired,
365+
// Other button configs
366+
otherButtonConfigs: PropTypes.arrayOf(
367+
PropTypes.shape({
368+
name: PropTypes.string.isRequired,
369+
mealState: PropTypes.string.isRequired
370+
})
371+
).isRequired,
356372
// The URL of the webrtc signalling server
357373
webrtcURL: PropTypes.string.isRequired
358374
}
359375

360-
export default AbovePlate
376+
export default CustomizeConfiguration

feedingwebapp/src/Pages/Settings/Main.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ const Main = () => {
156156
// Get icon image for move to mouth
157157
let moveToMouthConfigurationImage = MOVING_STATE_ICON_DICT[MEAL_STATE.R_MovingToMouth]
158158
let moveAbovePlateConfigurationImage = MOVING_STATE_ICON_DICT[MEAL_STATE.R_MovingAbovePlate]
159+
let moveToRestingConfigurationImage = MOVING_STATE_ICON_DICT[MEAL_STATE.R_MovingToRestingPosition]
159160

160161
// Configure the different options in the settings menu
161162
let settingsConfig = [
@@ -168,6 +169,11 @@ const Main = () => {
168169
title: 'Above Plate',
169170
icon: moveAbovePlateConfigurationImage,
170171
onClick: () => onClickSettingsPage(SETTINGS_STATE.ABOVE_PLATE)
172+
},
173+
{
174+
title: 'Resting Position',
175+
icon: moveToRestingConfigurationImage,
176+
onClick: () => onClickSettingsPage(SETTINGS_STATE.RESTING_CONFIGURATION)
171177
}
172178
]
173179

0 commit comments

Comments
 (0)