diff --git a/feedingwebapp/src/Pages/Constants.js b/feedingwebapp/src/Pages/Constants.js index 0ac1b99..36268dd 100644 --- a/feedingwebapp/src/Pages/Constants.js +++ b/feedingwebapp/src/Pages/Constants.js @@ -122,6 +122,8 @@ export const GET_PARAMETERS_SERVICE_NAME = 'ada_feeding_action_servers/get_param export const GET_PARAMETERS_SERVICE_TYPE = 'rcl_interfaces/srv/GetParameters' export const SET_PARAMETERS_SERVICE_NAME = 'ada_feeding_action_servers/set_parameters_atomically' export const SET_PARAMETERS_SERVICE_TYPE = 'rcl_interfaces/srv/SetParametersAtomically' +export const PLANNING_SCENE_GET_PARAMETERS_SERVICE_NAME = 'ada_feeding_action_servers/get_parameters' +export const PLANNING_SCENE_GET_PARAMETERS_SERVICE_TYPE = 'rcl_interfaces/srv/GetParameters' // The names of parameters users can change in the settings menu export const DISTANCE_TO_MOUTH_PARAM = 'MoveToMouth.tree_kwargs.plan_distance_from_mouth' @@ -129,11 +131,12 @@ export const MOVE_TO_MOUTH_SPEED_PARAM = 'MoveToMouth.tree_kwargs.max_linear_spe export const MOVE_TO_MOUTH_SPEED_NEAR_MOUTH_PARAM = 'MoveToMouth.tree_kwargs.linear_speed_near_mouth' export const MOVE_FROM_MOUTH_SPEED_PARAM = 'MoveFromMouth.tree_kwargs.max_linear_speed_to_staging_configuration' export const MOVE_FROM_MOUTH_SPEED_NEAR_MOUTH_PARAM = 'MoveFromMouth.tree_kwargs.linear_speed_near_mouth' +export const PLANNING_SCENE_PARAM = 'planning_scene_namespace_to_use' export const ABOVE_PLATE_PARAM_JOINTS = 'MoveAbovePlate.tree_kwargs.joint_positions' export const STAGING_PARAM_JOINTS = 'MoveToStagingConfiguration.tree_kwargs.goal_configuration' export const STAGING_PARAM_POSITION = 'MoveFromMouth.tree_kwargs.staging_configuration_position' export const STAGING_PARAM_ORIENTATION = 'MoveFromMouth.tree_kwargs.staging_configuration_quat_xyzw' -// TODO: Eventually, we should break AcquireFood into two actionss to avoid these +// TODO: Eventually, we should break AcquireFood into two actions to avoid these // two different resting parameters. export const RESTING_PARAM_JOINTS_1 = 'AcquireFood.tree_kwargs.resting_joint_positions' // TODO: We may need to remove the orientation constraint from the below action. diff --git a/feedingwebapp/src/Pages/GlobalState.jsx b/feedingwebapp/src/Pages/GlobalState.jsx index 5d6e211..c530b38 100644 --- a/feedingwebapp/src/Pages/GlobalState.jsx +++ b/feedingwebapp/src/Pages/GlobalState.jsx @@ -93,7 +93,8 @@ export const SETTINGS_STATE = { ABOVE_PLATE: 'ABOVE_PLATE', RESTING_CONFIGURATION: 'RESTING_CONFIGURATION', STAGING_CONFIGURATION: 'STAGING_CONFIGURATION', - STOW_CONFIGURATION: 'STOW_CONFIGURATION' + STOW_CONFIGURATION: 'STOW_CONFIGURATION', + PLANNING_SCENE: 'PLANNING_SCENE' } // The name of the default parameter namespace diff --git a/feedingwebapp/src/Pages/Settings/Main.jsx b/feedingwebapp/src/Pages/Settings/Main.jsx index 09c1022..1b91470 100644 --- a/feedingwebapp/src/Pages/Settings/Main.jsx +++ b/feedingwebapp/src/Pages/Settings/Main.jsx @@ -186,6 +186,11 @@ const Main = () => { title: 'Stow Position', icon: moveToStowConfigurationImage, onClick: () => onClickSettingsPage(SETTINGS_STATE.STOW_CONFIGURATION) + }, + { + title: 'Planning Scene', + icon: null, + onClick: () => onClickSettingsPage(SETTINGS_STATE.PLANNING_SCENE) } ] diff --git a/feedingwebapp/src/Pages/Settings/PlanningScene.jsx b/feedingwebapp/src/Pages/Settings/PlanningScene.jsx new file mode 100644 index 0000000..069a90d --- /dev/null +++ b/feedingwebapp/src/Pages/Settings/PlanningScene.jsx @@ -0,0 +1,125 @@ +// React imports +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useMediaQuery } from 'react-responsive' +import ButtonGroup from 'react-bootstrap/ButtonGroup' +import Dropdown from 'react-bootstrap/Dropdown' +import DropdownButton from 'react-bootstrap/DropdownButton' +import { View } from 'react-native' + +// Local imports +import { PLANNING_SCENE_PARAM, PLANNING_SCENE_GET_PARAMETERS_SERVICE_NAME, PLANNING_SCENE_GET_PARAMETERS_SERVICE_TYPE } from '../Constants' +import { useGlobalState, SETTINGS_STATE } from '../GlobalState' +import { useROS, createROSService, createROSServiceRequest, getValueFromParameter } from '../../ros/ros_helpers' +import SettingsPageParent from './SettingsPageParent' + +/** + * The PlanningScene component allows users to change the planning scene that a particular + * settings namespace is configured with + */ +const PlanningScene = () => { + // Get relevant global state variables + const setSettingsState = useGlobalState((state) => state.setSettingsState) + + // Flag to check if the current orientation is portrait + const isPortrait = useMediaQuery({ query: '(orientation: portrait)' }) + // Indicator of how to arrange screen elements based on orientation + let dimension = isPortrait ? 'column' : 'row' + // Rendering variables + let textFontSize = '3.5vh' + + // Configure the parameters for SettingsPageParent + const paramNames = useMemo(() => [PLANNING_SCENE_PARAM], []) + const [currentParams, setCurrentParams] = useState([null]) + + /** + * Connect to ROS, if not already connected. Put this in useRef to avoid + * re-connecting upon re-renders. + */ + const ros = useRef(useROS().ros) + + // Get the options for the planning scene + let getParametersService = useRef( + createROSService(ros.current, PLANNING_SCENE_GET_PARAMETERS_SERVICE_NAME, PLANNING_SCENE_GET_PARAMETERS_SERVICE_TYPE) + ) + const [planningSceneNamespaces, setPlanningSceneNamespaces] = useState([]) + useEffect(() => { + let service = getParametersService.current + let request = createROSServiceRequest({ + names: ['namespaces'] + }) + service.callService(request, (response) => { + if (response.values.length > 0 && response.values[0].type === 9) { + setPlanningSceneNamespaces(getValueFromParameter(response.values[0])) + } else { + console.error('PlanningScene: Error getting planning scene namespaces') + } + }) + }, [getParametersService, setPlanningSceneNamespaces]) + + // Render the settings for the planning scene + const renderPlanningSceneSettings = useCallback(() => { + if (currentParams.some((param) => param === null)) { + return ( + <> + +
Loading...
+
+ + ) + } else { + return ( + + + {planningSceneNamespaces.map((namespace) => ( + setCurrentParams([namespace])} active={namespace === currentParams[0]}> + {namespace} + + ))} + + + ) + } + }, [currentParams, dimension, planningSceneNamespaces, setCurrentParams, textFontSize]) + + return ( + setSettingsState(SETTINGS_STATE.MAIN)} + modalShow={false} + modalOnHide={null} + modalChildren={null} + paramNames={paramNames} + localParamValues={currentParams} + setLocalParamValues={setCurrentParams} + > + {renderPlanningSceneSettings()} + + ) +} + +export default PlanningScene diff --git a/feedingwebapp/src/Pages/Settings/Settings.jsx b/feedingwebapp/src/Pages/Settings/Settings.jsx index b325b71..dcf30b1 100644 --- a/feedingwebapp/src/Pages/Settings/Settings.jsx +++ b/feedingwebapp/src/Pages/Settings/Settings.jsx @@ -22,6 +22,7 @@ import { STAGING_PARAM_POSITION, STOW_PARAM_JOINTS } from '../Constants' +import PlanningScene from './PlanningScene' /** * The Settings components displays the appropriate settings page based on the @@ -138,6 +139,8 @@ const Settings = (props) => { webrtcURL={props.webrtcURL} /> ) + case SETTINGS_STATE.PLANNING_SCENE: + return default: console.log('Invalid settings state', settingsState) return