From 5ed124028e7a3aebb628d74e0afdac6162d74711 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 04:09:55 +0000 Subject: [PATCH 01/10] Rename place to waypoint Signed-off-by: Aaron Chong --- packages/dashboard/src/components/appbar.tsx | 26 +++++++------------ packages/dashboard/src/components/map-app.tsx | 14 +++++----- packages/react-components/lib/index.ts | 2 +- .../lib/{place.ts => waypoint.ts} | 10 +++---- 4 files changed, 23 insertions(+), 29 deletions(-) rename packages/react-components/lib/{place.ts => waypoint.ts} (58%) diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index ac9000603..12065a629 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -35,7 +35,7 @@ import { AppBarTab, CreateTaskForm, CreateTaskFormProps, - getPlaces, + getWaypoints, HeaderBar, LogoButton, NavigationBar, @@ -167,8 +167,10 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const [brandingIconPath, setBrandingIconPath] = React.useState(''); const [settingsAnchor, setSettingsAnchor] = React.useState(null); const [openCreateTaskForm, setOpenCreateTaskForm] = React.useState(false); - const [placeNames, setPlaceNames] = React.useState([]); - const [workcells, setWorkcells] = React.useState(); + const [waypointNames, setWaypointNames] = React.useState([]); + const [cleanWaypointNames, setCleanWaypointNames] = React.useState([]); + const [pickupWaypointNames, setPickupWaypointNames] = React.useState([]); + const [dropoffWaypointNames, setDropoffWaypointNames] = React.useState([]); const [favoritesTasks, setFavoritesTasks] = React.useState([]); const [refreshTaskAppCount, setRefreshTaskAppCount] = React.useState(0); const [username, setUsername] = React.useState(null); @@ -214,13 +216,6 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea })(); }, [logoResourcesContext, safeAsync, curTheme]); - React.useEffect(() => { - if (!resourceManager?.dispensers) { - return; - } - setWorkcells(Object.keys(resourceManager.dispensers.dispensers)); - }, [resourceManager]); - React.useEffect(() => { if (!rmf) { return; @@ -229,7 +224,7 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const subs: Subscription[] = []; subs.push( rmf.buildingMapObs.subscribe((map) => - setPlaceNames(getPlaces(map).map((p) => p.vertex.name)), + setWaypointNames(getWaypoints(map).map((w) => w.vertex.name)), ), ); subs.push( @@ -578,11 +573,10 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea {openCreateTaskForm && ( setOpenCreateTaskForm(false)} diff --git a/packages/dashboard/src/components/map-app.tsx b/packages/dashboard/src/components/map-app.tsx index 9db8f5869..06dc68c67 100644 --- a/packages/dashboard/src/components/map-app.tsx +++ b/packages/dashboard/src/components/map-app.tsx @@ -12,10 +12,10 @@ import { affineImageBounds, ColorManager, fromRmfCoords, - getPlaces, + getWaypoints, LMap, loadAffineImage, - Place, + Waypoint, RobotTableData, TrajectoryTimeControl, } from 'react-components'; @@ -125,7 +125,7 @@ export const MapApp = styled( const [fleets, setFleets] = React.useState([]); - const [waypoints, setWaypoints] = React.useState([]); + const [waypoints, setWaypoints] = React.useState([]); const [trajectories, setTrajectories] = React.useState([]); const [mapSettings, setMapSettings] = React.useState(() => { @@ -194,8 +194,8 @@ export const MapApp = styled( const currentLevel = newMap.levels[0]; setCurrentLevel(currentLevel); setWaypoints( - getPlaces(newMap).filter( - (p) => p.level === currentLevel.name && p.vertex.name.length > 0, + getWaypoints(newMap).filter( + (w) => w.level === currentLevel.name && w.vertex.name.length > 0, ), ); }), @@ -260,8 +260,8 @@ export const MapApp = styled( buildingMap && setWaypoints( - getPlaces(buildingMap).filter( - (p) => p.level === currentLevel.name && p.vertex.name.length > 0, + getWaypoints(buildingMap).filter( + (w) => w.level === currentLevel.name && w.vertex.name.length > 0, ), ); }, [buildingMap, currentLevel]); diff --git a/packages/react-components/lib/index.ts b/packages/react-components/lib/index.ts index e97619ce4..fc58dba2c 100644 --- a/packages/react-components/lib/index.ts +++ b/packages/react-components/lib/index.ts @@ -20,7 +20,6 @@ export * from './locale'; export * from './logo-button'; export * from './map'; export * from './navigation-bar'; -export * from './place'; export * from './robots'; export * from './simple-filter'; export * from './simple-info'; @@ -34,5 +33,6 @@ export * from './tooltip'; export * from './transfer-list'; export * from './use-async'; export * from './utils'; +export * from './waypoint'; export * from './window'; export * from './workcells'; diff --git a/packages/react-components/lib/place.ts b/packages/react-components/lib/waypoint.ts similarity index 58% rename from packages/react-components/lib/place.ts rename to packages/react-components/lib/waypoint.ts index 55acfdad6..f73177bdc 100644 --- a/packages/react-components/lib/place.ts +++ b/packages/react-components/lib/waypoint.ts @@ -1,20 +1,20 @@ import type { BuildingMap, GraphNode } from 'api-client'; -export interface Place { +export interface Waypoint { level: string; vertex: GraphNode; } -export function getPlaces(buildingMap: BuildingMap): Place[] { - const places: Place[] = []; +export function getWaypoints(buildingMap: BuildingMap): Waypoint[] { + const waypoints: Waypoint[] = []; for (const level of buildingMap.levels) { for (const graphs of level.nav_graphs) { for (const vertex of graphs.vertices) { if (vertex.name) { - places.push({ level: level.name, vertex }); + waypoints.push({ level: level.name, vertex }); } } } } - return places; + return waypoints; } From d67062b4b5e9d0ba569e1a0577f6c411081c9d93 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 04:10:43 +0000 Subject: [PATCH 02/10] Reduce delivery form requirements to just pickup and dropoff waypoint names Signed-off-by: Aaron Chong --- .../lib/tasks/create-task.tsx | 149 +++++------------- 1 file changed, 41 insertions(+), 108 deletions(-) diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 38382c1e0..7f7a8813b 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -45,7 +45,6 @@ import { PositiveIntField } from '../form-inputs'; interface DeliveryTaskDescription { pickup: { place: string; - handler: string; payload: { sku: string; quantity: number; @@ -53,7 +52,6 @@ interface DeliveryTaskDescription { }; dropoff: { place: string; - handler: string; payload: { sku: string; quantity: number; @@ -136,29 +134,27 @@ function FormToolbar({ onSelectFileClick }: FormToolbarProps) { interface DeliveryTaskFormProps { taskDesc: DeliveryTaskDescription; - deliveryWaypoints: string[]; - dispensers: string[]; - ingestors: string[]; + pickupWaypoints: string[]; + dropoffWaypoints: string[]; onChange(taskDesc: TaskDescription): void; } function DeliveryTaskForm({ taskDesc, - deliveryWaypoints, - dispensers, - ingestors, + pickupWaypoints, + dropoffWaypoints, onChange, }: DeliveryTaskFormProps) { const theme = useTheme(); return ( - + newValue !== null && @@ -182,93 +178,6 @@ function DeliveryTaskForm({ renderInput={(params) => } /> - - - newValue !== null && - onChange({ - ...taskDesc, - pickup: { - ...taskDesc.pickup, - handler: newValue, - }, - }) - } - onBlur={(ev) => - onChange({ - ...taskDesc, - pickup: { - ...taskDesc.pickup, - handler: (ev.target as HTMLInputElement).value, - }, - }) - } - renderInput={(params) => } - /> - - - - newValue !== null && - onChange({ - ...taskDesc, - dropoff: { - ...taskDesc.dropoff, - place: newValue, - }, - }) - } - onBlur={(ev) => - onChange({ - ...taskDesc, - dropoff: { - ...taskDesc.dropoff, - place: (ev.target as HTMLInputElement).value, - }, - }) - } - renderInput={(params) => } - /> - - - - newValue !== null && - onChange({ - ...taskDesc, - dropoff: { - ...taskDesc.dropoff, - handler: newValue, - }, - }) - } - onBlur={(ev) => - onChange({ - ...taskDesc, - dropoff: { - ...taskDesc.dropoff, - handler: (ev.target as HTMLInputElement).value, - }, - }) - } - renderInput={(params) => } - /> - } /> + + + newValue !== null && + onChange({ + ...taskDesc, + dropoff: { + ...taskDesc.dropoff, + place: newValue, + }, + }) + } + onBlur={(ev) => + onChange({ + ...taskDesc, + dropoff: { + ...taskDesc.dropoff, + place: (ev.target as HTMLInputElement).value, + }, + }) + } + renderInput={(params) => } + /> + ; tasksFromFile?(): Promise | TaskRequest[]; @@ -750,9 +685,8 @@ export function CreateTaskForm({ user, cleaningZones = [], loopWaypoints = [], - deliveryWaypoints = [], - dispensers = [], - ingestors = [], + pickupWaypoints = [], + dropoffWaypoints = [], favoritesTasks = [], submitTasks, tasksFromFile, @@ -853,9 +787,8 @@ export function CreateTaskForm({ return ( handleTaskDescriptionChange('delivery', desc)} /> ); From 08eea0c82631c4bc32bcd3e7d0650f593a3eeddb Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 08:45:10 +0000 Subject: [PATCH 03/10] Adding WaypointCollection, which filters each waypoint type, display waypoint options only for that type Signed-off-by: Aaron Chong --- packages/dashboard/src/components/appbar.tsx | 16 ++++--- .../src/components/waypoints-overlay.tsx | 4 +- .../lib/tasks/create-task.tsx | 2 +- packages/react-components/lib/waypoint.ts | 46 +++++++++++++++++++ 4 files changed, 59 insertions(+), 9 deletions(-) diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 12065a629..92f7d9a1f 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -35,7 +35,7 @@ import { AppBarTab, CreateTaskForm, CreateTaskFormProps, - getWaypoints, + getWaypointCollection, HeaderBar, LogoButton, NavigationBar, @@ -168,7 +168,7 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const [settingsAnchor, setSettingsAnchor] = React.useState(null); const [openCreateTaskForm, setOpenCreateTaskForm] = React.useState(false); const [waypointNames, setWaypointNames] = React.useState([]); - const [cleanWaypointNames, setCleanWaypointNames] = React.useState([]); + const [cleaningWaypointNames, setCleaningWaypointNames] = React.useState([]); const [pickupWaypointNames, setPickupWaypointNames] = React.useState([]); const [dropoffWaypointNames, setDropoffWaypointNames] = React.useState([]); const [favoritesTasks, setFavoritesTasks] = React.useState([]); @@ -223,9 +223,13 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const subs: Subscription[] = []; subs.push( - rmf.buildingMapObs.subscribe((map) => - setWaypointNames(getWaypoints(map).map((w) => w.vertex.name)), - ), + rmf.buildingMapObs.subscribe((map) => { + const waypointCollection = getWaypointCollection(map); + setPickupWaypointNames(waypointCollection.pickupWaypoints.map((w) => w.vertex.name)); + setDropoffWaypointNames(waypointCollection.dropoffWaypoints.map((w) => w.vertex.name)); + setCleaningWaypointNames(waypointCollection.cleaningWaypoints.map((w) => w.vertex.name)); + setWaypointNames(waypointCollection.waypoints.map((w) => w.vertex.name)); + }), ); subs.push( AppEvents.refreshAlertCount.subscribe((_) => { @@ -573,7 +577,7 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea {openCreateTaskForm && ( { - waypoints: Place[]; + waypoints: Waypoint[]; hideLabels?: boolean; } diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 7f7a8813b..8b1473828 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -394,6 +394,7 @@ interface LoopTaskFormProps { function LoopTaskForm({ taskDesc, loopWaypoints, onChange }: LoopTaskFormProps) { const theme = useTheme(); + // console.log(loopWaypoints); return ( @@ -998,7 +999,6 @@ export function CreateTaskForm({ value={taskRequest.category} onChange={handleTaskTypeChange} > - Clean Loop Delivery diff --git a/packages/react-components/lib/waypoint.ts b/packages/react-components/lib/waypoint.ts index f73177bdc..b6257128c 100644 --- a/packages/react-components/lib/waypoint.ts +++ b/packages/react-components/lib/waypoint.ts @@ -1,10 +1,21 @@ import type { BuildingMap, GraphNode } from 'api-client'; +const DEFAULT_PICKUP_WAYPOINT_PARAM_NAME = 'pickup_dispenser'; +const DEFAULT_DROPOFF_WAYPOINT_PARAM_NAME = 'dropoff_ingestor'; +const DEFAULT_CLEANING_WAYPOINT_PARAM_NAME = 'is_cleaning_zone'; + export interface Waypoint { level: string; vertex: GraphNode; } +export interface WaypointCollection { + waypoints: Waypoint[]; + pickupWaypoints: Waypoint[]; + dropoffWaypoints: Waypoint[]; + cleaningWaypoints: Waypoint[]; +} + export function getWaypoints(buildingMap: BuildingMap): Waypoint[] { const waypoints: Waypoint[] = []; for (const level of buildingMap.levels) { @@ -18,3 +29,38 @@ export function getWaypoints(buildingMap: BuildingMap): Waypoint[] { } return waypoints; } + +export function getWaypointCollection(buildingMap: BuildingMap): WaypointCollection { + const waypoints = new Map(); + const pickupWaypoints = new Map(); + const dropoffWaypoints = new Map(); + const cleaningWaypoints = new Map(); + + for (const level of buildingMap.levels) { + for (const graphs of level.nav_graphs) { + for (const vertex of graphs.vertices) { + if (vertex.name) { + const waypoint: Waypoint = { level: level.name, vertex }; + for (const p of vertex.params) { + if (p.name === DEFAULT_PICKUP_WAYPOINT_PARAM_NAME) { + pickupWaypoints.set(vertex.name, waypoint); + } + if (p.name === DEFAULT_DROPOFF_WAYPOINT_PARAM_NAME) { + dropoffWaypoints.set(vertex.name, waypoint); + } + if (p.name === DEFAULT_CLEANING_WAYPOINT_PARAM_NAME) { + cleaningWaypoints.set(vertex.name, waypoint); + } + } + waypoints.set(vertex.name, waypoint); + } + } + } + } + return { + waypoints: Array.from(waypoints.values()), + pickupWaypoints: Array.from(pickupWaypoints.values()), + dropoffWaypoints: Array.from(dropoffWaypoints.values()), + cleaningWaypoints: Array.from(cleaningWaypoints.values()), + }; +} From 17439d4bffaec4888de9bfcd1fab3d0b5501a3bb Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 08:53:58 +0000 Subject: [PATCH 04/10] Disable menu item if task is unavailable Signed-off-by: Aaron Chong --- .../lib/tasks/create-task.tsx | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 8b1473828..8a819dd8e 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -999,8 +999,29 @@ export function CreateTaskForm({ value={taskRequest.category} onChange={handleTaskTypeChange} > - Loop - Delivery + + Clean + + + Loop + + + Delivery + From 74a7a41feb1f25229ae74cc3265cfe405f6ae6a7 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 09:04:39 +0000 Subject: [PATCH 05/10] Fix error of passing a value to element by int instead of string Signed-off-by: Aaron Chong --- packages/react-components/lib/tasks/create-task.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 8a819dd8e..0e6c1f36a 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -218,7 +218,7 @@ function DeliveryTaskForm({ id="pickup_quantity" freeSolo fullWidth - value={taskDesc.pickup.payload.quantity} + value={`${taskDesc.pickup.payload.quantity}`} options={[]} onChange={(_ev, newValue) => newValue !== null && @@ -245,7 +245,7 @@ function DeliveryTaskForm({ }, }) } - renderInput={(params) => } + renderInput={(params) => } /> @@ -317,7 +317,7 @@ function DeliveryTaskForm({ id="dropoff_quantity" freeSolo fullWidth - value={taskDesc.dropoff.payload.quantity} + value={`${taskDesc.dropoff.payload.quantity}`} options={[]} onChange={(_ev, newValue) => newValue !== null && @@ -344,7 +344,7 @@ function DeliveryTaskForm({ }, }) } - renderInput={(params) => } + renderInput={(params) => } /> From 2c06d394c13ba96872cdbb8cbdbbc73a61e1772f Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 09:19:15 +0000 Subject: [PATCH 06/10] Using patrol instead of loop Signed-off-by: Aaron Chong --- .../tests/ui-interactions/submit-task.test.ts | 12 +++--- packages/dashboard/src/components/appbar.tsx | 2 +- .../lib/tasks/create-task.tsx | 40 +++++++++---------- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/dashboard-e2e/tests/ui-interactions/submit-task.test.ts b/packages/dashboard-e2e/tests/ui-interactions/submit-task.test.ts index b9448604e..7c7531244 100644 --- a/packages/dashboard-e2e/tests/ui-interactions/submit-task.test.ts +++ b/packages/dashboard-e2e/tests/ui-interactions/submit-task.test.ts @@ -1,24 +1,24 @@ import { getAppBar } from '../utils'; describe('submit task', () => { - it('can submit loop task', async () => { + it('can submit patrol task', async () => { const appBar = await getAppBar(); await (await appBar.$('button[aria-label="Tasks"]')).click(); await (await appBar.$('button[aria-label="new task"]')).click(); await (await $('#task-type')).click(); - const getLoopOption = async () => { + const getPatrolOption = async () => { const options = await $$('[role=option]'); for (const opt of options) { const text = await opt.getText(); - if (text === 'Loop') { + if (text === 'Patrol') { return opt; } } return null; }; - await browser.waitUntil(async () => !!(await getLoopOption())); - const loopOption = (await getLoopOption())!; - await loopOption.click(); + await browser.waitUntil(async () => !!(await getPatrolOption())); + const patrolOption = (await getPatrolOption())!; + await patrolOption.click(); await (await $('#place-input')).setValue('coe'); diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 92f7d9a1f..6c24c4d34 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -578,7 +578,7 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea @@ -403,7 +401,7 @@ function LoopTaskForm({ taskDesc, loopWaypoints, onChange }: LoopTaskFormProps) id="place-input" freeSolo fullWidth - options={loopWaypoints} + options={patrolWaypoints} onChange={(_ev, newValue) => newValue !== null && onChange({ @@ -539,7 +537,7 @@ function defaultCleanTask(): CleanTaskDescription { }; } -function defaultLoopTask(): LoopTaskDescription { +function defaultPatrolTask(): PatrolTaskDescription { return { places: [], rounds: 1, @@ -570,7 +568,7 @@ function defaultTaskDescription(taskCategory: string): TaskDescription | undefin case 'clean': return defaultCleanTask(); case 'patrol': - return defaultLoopTask(); + return defaultPatrolTask(); case 'delivery': return defaultDeliveryTask(); default: @@ -581,7 +579,7 @@ function defaultTaskDescription(taskCategory: string): TaskDescription | undefin function defaultTask(): TaskRequest { return { category: 'patrol', - description: defaultLoopTask(), + description: defaultPatrolTask(), unix_millis_earliest_start_time: 0, unix_millis_request_time: Date.now(), priority: { type: 'binary', value: 0 }, @@ -651,7 +649,7 @@ const defaultFavoriteTask = (): TaskFavorite => { id: '', name: '', category: 'patrol', - description: defaultLoopTask(), + description: defaultPatrolTask(), unix_millis_earliest_start_time: 0, priority: { type: 'binary', value: 0 }, user: '', @@ -666,7 +664,7 @@ export interface CreateTaskFormProps user: string; allowBatch?: boolean; cleaningZones?: string[]; - loopWaypoints?: string[]; + patrolWaypoints?: string[]; pickupWaypoints?: string[]; dropoffWaypoints?: string[]; favoritesTasks: TaskFavorite[]; @@ -685,7 +683,7 @@ export interface CreateTaskFormProps export function CreateTaskForm({ user, cleaningZones = [], - loopWaypoints = [], + patrolWaypoints = [], pickupWaypoints = [], dropoffWaypoints = [], favoritesTasks = [], @@ -778,9 +776,9 @@ export function CreateTaskForm({ ); case 'patrol': return ( - handleTaskDescriptionChange('patrol', desc)} /> ); @@ -1007,9 +1005,9 @@ export function CreateTaskForm({ - Loop + Patrol Date: Wed, 23 Aug 2023 09:23:45 +0000 Subject: [PATCH 07/10] Fix documentation and create-task story Signed-off-by: Aaron Chong --- packages/dashboard-e2e/README.md | 12 ++++++------ .../lib/tasks/create-task.stories.tsx | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/dashboard-e2e/README.md b/packages/dashboard-e2e/README.md index ae9ed894a..598362965 100644 --- a/packages/dashboard-e2e/README.md +++ b/packages/dashboard-e2e/README.md @@ -52,13 +52,13 @@ browser.overwriteCommand( ``` ```ts -// this will fail depending on the context on which `getLoopOption` is called. -const getLoopOption = async () => { - return $$('[role=option]').find(async (elem) => await elem.getText() === 'Loop'); +// this will fail depending on the context on which `getPatrolOption` is called. +const getPatrolOption = async () => { + return $$('[role=option]').find(async (elem) => await elem.getText() === 'Patrol'); }; -await browser.waitUntil(async () => !!(await getLoopOption)); // ok -await console.log(await getLoopOption()); // error +await browser.waitUntil(async () => !!(await getPatrolOption)); // ok +await console.log(await getPatrolOption()); // error ``` -Possible reason is because async selectors chaining relies on a "finalizer" or some kind of context to resolve the promises. As a result, when running `getLoopOption` without a wdio await, the chaining does not work. But this is all speculation, the inner workings of async selector chainings are very complex. +Possible reason is because async selectors chaining relies on a "finalizer" or some kind of context to resolve the promises. As a result, when running `getPatrolOption` without a wdio await, the chaining does not work. But this is all speculation, the inner workings of async selector chainings are very complex. diff --git a/packages/react-components/lib/tasks/create-task.stories.tsx b/packages/react-components/lib/tasks/create-task.stories.tsx index 08e98daa0..ae73b0c30 100644 --- a/packages/react-components/lib/tasks/create-task.stories.tsx +++ b/packages/react-components/lib/tasks/create-task.stories.tsx @@ -24,8 +24,7 @@ export const CreateTask: Story = (args) => { CreateTask.args = { submitTasks: async () => new Promise((res) => setTimeout(res, 500)), cleaningZones: ['test_zone_0', 'test_zone_1'], - loopWaypoints: ['test_waypoint_0', 'test_waypoint_1'], - deliveryWaypoints: ['test_waypoint_0', 'test_waypoint_1'], - dispensers: ['test_dispenser_0', 'test_dispenser_1'], - ingestors: ['test_ingestor_0', 'test_ingestor_1'], + patrolWaypoints: ['test_waypoint_0', 'test_waypoint_1'], + pickupWaypoints: ['test_waypoint_0'], + dropoffWaypoints: ['test_waypoint_1'], }; From 49d6a355c3b500580e3092ec410f649e77c78d39 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 10:01:53 +0000 Subject: [PATCH 08/10] Revert to Place, as Waypoint implies routes Signed-off-by: Aaron Chong --- packages/dashboard/src/components/appbar.tsx | 24 +++---- packages/dashboard/src/components/map-app.tsx | 10 +-- .../src/components/waypoints-overlay.tsx | 4 +- packages/react-components/lib/index.ts | 2 +- packages/react-components/lib/place.ts | 66 +++++++++++++++++++ .../lib/tasks/create-task.tsx | 32 ++++----- packages/react-components/lib/waypoint.ts | 66 ------------------- 7 files changed, 102 insertions(+), 102 deletions(-) create mode 100644 packages/react-components/lib/place.ts delete mode 100644 packages/react-components/lib/waypoint.ts diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 6c24c4d34..ba95a5856 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -35,7 +35,7 @@ import { AppBarTab, CreateTaskForm, CreateTaskFormProps, - getWaypointCollection, + getNamedPlaces, HeaderBar, LogoButton, NavigationBar, @@ -168,9 +168,9 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const [settingsAnchor, setSettingsAnchor] = React.useState(null); const [openCreateTaskForm, setOpenCreateTaskForm] = React.useState(false); const [waypointNames, setWaypointNames] = React.useState([]); - const [cleaningWaypointNames, setCleaningWaypointNames] = React.useState([]); - const [pickupWaypointNames, setPickupWaypointNames] = React.useState([]); - const [dropoffWaypointNames, setDropoffWaypointNames] = React.useState([]); + const [cleaningZoneNames, setCleaningZoneNames] = React.useState([]); + const [pickupPointNames, setPickupPointNames] = React.useState([]); + const [dropoffPointNames, setDropoffPointNames] = React.useState([]); const [favoritesTasks, setFavoritesTasks] = React.useState([]); const [refreshTaskAppCount, setRefreshTaskAppCount] = React.useState(0); const [username, setUsername] = React.useState(null); @@ -224,11 +224,11 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const subs: Subscription[] = []; subs.push( rmf.buildingMapObs.subscribe((map) => { - const waypointCollection = getWaypointCollection(map); - setPickupWaypointNames(waypointCollection.pickupWaypoints.map((w) => w.vertex.name)); - setDropoffWaypointNames(waypointCollection.dropoffWaypoints.map((w) => w.vertex.name)); - setCleaningWaypointNames(waypointCollection.cleaningWaypoints.map((w) => w.vertex.name)); - setWaypointNames(waypointCollection.waypoints.map((w) => w.vertex.name)); + const namedPlaces = getNamedPlaces(map); + setPickupPointNames(namedPlaces.pickupPoints.map((w) => w.vertex.name)); + setDropoffPointNames(namedPlaces.dropoffPoints.map((w) => w.vertex.name)); + setCleaningZoneNames(namedPlaces.cleaningZones.map((w) => w.vertex.name)); + setWaypointNames(namedPlaces.places.map((w) => w.vertex.name)); }), ); subs.push( @@ -577,10 +577,10 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea {openCreateTaskForm && ( setOpenCreateTaskForm(false)} diff --git a/packages/dashboard/src/components/map-app.tsx b/packages/dashboard/src/components/map-app.tsx index 06dc68c67..8d7b7c172 100644 --- a/packages/dashboard/src/components/map-app.tsx +++ b/packages/dashboard/src/components/map-app.tsx @@ -12,10 +12,10 @@ import { affineImageBounds, ColorManager, fromRmfCoords, - getWaypoints, + getPlaces, LMap, loadAffineImage, - Waypoint, + Place, RobotTableData, TrajectoryTimeControl, } from 'react-components'; @@ -125,7 +125,7 @@ export const MapApp = styled( const [fleets, setFleets] = React.useState([]); - const [waypoints, setWaypoints] = React.useState([]); + const [waypoints, setWaypoints] = React.useState([]); const [trajectories, setTrajectories] = React.useState([]); const [mapSettings, setMapSettings] = React.useState(() => { @@ -194,7 +194,7 @@ export const MapApp = styled( const currentLevel = newMap.levels[0]; setCurrentLevel(currentLevel); setWaypoints( - getWaypoints(newMap).filter( + getPlaces(newMap).filter( (w) => w.level === currentLevel.name && w.vertex.name.length > 0, ), ); @@ -260,7 +260,7 @@ export const MapApp = styled( buildingMap && setWaypoints( - getWaypoints(buildingMap).filter( + getPlaces(buildingMap).filter( (w) => w.level === currentLevel.name && w.vertex.name.length > 0, ), ); diff --git a/packages/dashboard/src/components/waypoints-overlay.tsx b/packages/dashboard/src/components/waypoints-overlay.tsx index 71a2e7418..d6d02f83c 100644 --- a/packages/dashboard/src/components/waypoints-overlay.tsx +++ b/packages/dashboard/src/components/waypoints-overlay.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { fromRmfCoords, + Place, SVGOverlay, SVGOverlayProps, useAutoScale, viewBoxFromLeafletBounds, - Waypoint, WaypointMarker as WaypointMarker_, withLabel, } from 'react-components'; @@ -14,7 +14,7 @@ import { const WaypointMarker = withLabel(WaypointMarker_); export interface WaypointsOverlayProps extends Omit { - waypoints: Waypoint[]; + waypoints: Place[]; hideLabels?: boolean; } diff --git a/packages/react-components/lib/index.ts b/packages/react-components/lib/index.ts index fc58dba2c..e97619ce4 100644 --- a/packages/react-components/lib/index.ts +++ b/packages/react-components/lib/index.ts @@ -20,6 +20,7 @@ export * from './locale'; export * from './logo-button'; export * from './map'; export * from './navigation-bar'; +export * from './place'; export * from './robots'; export * from './simple-filter'; export * from './simple-info'; @@ -33,6 +34,5 @@ export * from './tooltip'; export * from './transfer-list'; export * from './use-async'; export * from './utils'; -export * from './waypoint'; export * from './window'; export * from './workcells'; diff --git a/packages/react-components/lib/place.ts b/packages/react-components/lib/place.ts new file mode 100644 index 000000000..583bd0ae7 --- /dev/null +++ b/packages/react-components/lib/place.ts @@ -0,0 +1,66 @@ +import type { BuildingMap, GraphNode } from 'api-client'; + +const DEFAULT_PICKUP_POINT_PARAM_NAME = 'pickup_dispenser'; +const DEFAULT_DROPOFF_POINT_PARAM_NAME = 'dropoff_ingestor'; +const DEFAULT_CLEANING_ZONE_PARAM_NAME = 'is_cleaning_zone'; + +export interface Place { + level: string; + vertex: GraphNode; +} + +export interface NamedPlaces { + places: Place[]; + pickupPoints: Place[]; + dropoffPoints: Place[]; + cleaningZones: Place[]; +} + +export function getPlaces(buildingMap: BuildingMap): Place[] { + const places: Place[] = []; + for (const level of buildingMap.levels) { + for (const graphs of level.nav_graphs) { + for (const vertex of graphs.vertices) { + if (vertex.name) { + places.push({ level: level.name, vertex }); + } + } + } + } + return places; +} + +export function getNamedPlaces(buildingMap: BuildingMap): NamedPlaces { + const places = new Map(); + const pickupPoints = new Map(); + const dropoffPoints = new Map(); + const cleaningZones = new Map(); + + for (const level of buildingMap.levels) { + for (const graphs of level.nav_graphs) { + for (const vertex of graphs.vertices) { + if (vertex.name) { + const place: Place = { level: level.name, vertex }; + for (const p of vertex.params) { + if (p.name === DEFAULT_PICKUP_POINT_PARAM_NAME) { + pickupPoints.set(vertex.name, place); + } + if (p.name === DEFAULT_DROPOFF_POINT_PARAM_NAME) { + dropoffPoints.set(vertex.name, place); + } + if (p.name === DEFAULT_CLEANING_ZONE_PARAM_NAME) { + cleaningZones.set(vertex.name, place); + } + } + places.set(vertex.name, place); + } + } + } + } + return { + places: Array.from(places.values()), + pickupPoints: Array.from(pickupPoints.values()), + dropoffPoints: Array.from(dropoffPoints.values()), + cleaningZones: Array.from(cleaningZones.values()), + }; +} diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 08f05f180..271f8059a 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -134,15 +134,15 @@ function FormToolbar({ onSelectFileClick }: FormToolbarProps) { interface DeliveryTaskFormProps { taskDesc: DeliveryTaskDescription; - pickupWaypoints: string[]; - dropoffWaypoints: string[]; + pickupPoints: string[]; + dropoffPoints: string[]; onChange(taskDesc: TaskDescription): void; } function DeliveryTaskForm({ taskDesc, - pickupWaypoints, - dropoffWaypoints, + pickupPoints, + dropoffPoints, onChange, }: DeliveryTaskFormProps) { const theme = useTheme(); @@ -154,7 +154,7 @@ function DeliveryTaskForm({ id="pickup-location" freeSolo fullWidth - options={pickupWaypoints} + options={pickupPoints} value={taskDesc.pickup.place} onChange={(_ev, newValue) => newValue !== null && @@ -253,7 +253,7 @@ function DeliveryTaskForm({ id="dropoff-location" freeSolo fullWidth - options={dropoffWaypoints} + options={dropoffPoints} value={taskDesc.dropoff.place} onChange={(_ev, newValue) => newValue !== null && @@ -665,8 +665,8 @@ export interface CreateTaskFormProps allowBatch?: boolean; cleaningZones?: string[]; patrolWaypoints?: string[]; - pickupWaypoints?: string[]; - dropoffWaypoints?: string[]; + pickupPoints?: string[]; + dropoffPoints?: string[]; favoritesTasks: TaskFavorite[]; submitTasks?(tasks: TaskRequest[], schedule: Schedule | null): Promise; tasksFromFile?(): Promise | TaskRequest[]; @@ -684,8 +684,8 @@ export function CreateTaskForm({ user, cleaningZones = [], patrolWaypoints = [], - pickupWaypoints = [], - dropoffWaypoints = [], + pickupPoints = [], + dropoffPoints = [], favoritesTasks = [], submitTasks, tasksFromFile, @@ -786,8 +786,8 @@ export function CreateTaskForm({ return ( handleTaskDescriptionChange('delivery', desc)} /> ); @@ -1012,10 +1012,10 @@ export function CreateTaskForm({ Delivery diff --git a/packages/react-components/lib/waypoint.ts b/packages/react-components/lib/waypoint.ts deleted file mode 100644 index b6257128c..000000000 --- a/packages/react-components/lib/waypoint.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { BuildingMap, GraphNode } from 'api-client'; - -const DEFAULT_PICKUP_WAYPOINT_PARAM_NAME = 'pickup_dispenser'; -const DEFAULT_DROPOFF_WAYPOINT_PARAM_NAME = 'dropoff_ingestor'; -const DEFAULT_CLEANING_WAYPOINT_PARAM_NAME = 'is_cleaning_zone'; - -export interface Waypoint { - level: string; - vertex: GraphNode; -} - -export interface WaypointCollection { - waypoints: Waypoint[]; - pickupWaypoints: Waypoint[]; - dropoffWaypoints: Waypoint[]; - cleaningWaypoints: Waypoint[]; -} - -export function getWaypoints(buildingMap: BuildingMap): Waypoint[] { - const waypoints: Waypoint[] = []; - for (const level of buildingMap.levels) { - for (const graphs of level.nav_graphs) { - for (const vertex of graphs.vertices) { - if (vertex.name) { - waypoints.push({ level: level.name, vertex }); - } - } - } - } - return waypoints; -} - -export function getWaypointCollection(buildingMap: BuildingMap): WaypointCollection { - const waypoints = new Map(); - const pickupWaypoints = new Map(); - const dropoffWaypoints = new Map(); - const cleaningWaypoints = new Map(); - - for (const level of buildingMap.levels) { - for (const graphs of level.nav_graphs) { - for (const vertex of graphs.vertices) { - if (vertex.name) { - const waypoint: Waypoint = { level: level.name, vertex }; - for (const p of vertex.params) { - if (p.name === DEFAULT_PICKUP_WAYPOINT_PARAM_NAME) { - pickupWaypoints.set(vertex.name, waypoint); - } - if (p.name === DEFAULT_DROPOFF_WAYPOINT_PARAM_NAME) { - dropoffWaypoints.set(vertex.name, waypoint); - } - if (p.name === DEFAULT_CLEANING_WAYPOINT_PARAM_NAME) { - cleaningWaypoints.set(vertex.name, waypoint); - } - } - waypoints.set(vertex.name, waypoint); - } - } - } - } - return { - waypoints: Array.from(waypoints.values()), - pickupWaypoints: Array.from(pickupWaypoints.values()), - dropoffWaypoints: Array.from(dropoffWaypoints.values()), - cleaningWaypoints: Array.from(cleaningWaypoints.values()), - }; -} From b0a563f6104ba04316da238c73d8398f9d503317 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 23 Aug 2023 12:28:54 +0000 Subject: [PATCH 09/10] Revert changes to variable names Signed-off-by: Aaron Chong --- packages/dashboard/src/components/map-app.tsx | 4 ++-- packages/react-components/lib/tasks/create-task.stories.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dashboard/src/components/map-app.tsx b/packages/dashboard/src/components/map-app.tsx index 8d7b7c172..9db8f5869 100644 --- a/packages/dashboard/src/components/map-app.tsx +++ b/packages/dashboard/src/components/map-app.tsx @@ -195,7 +195,7 @@ export const MapApp = styled( setCurrentLevel(currentLevel); setWaypoints( getPlaces(newMap).filter( - (w) => w.level === currentLevel.name && w.vertex.name.length > 0, + (p) => p.level === currentLevel.name && p.vertex.name.length > 0, ), ); }), @@ -261,7 +261,7 @@ export const MapApp = styled( buildingMap && setWaypoints( getPlaces(buildingMap).filter( - (w) => w.level === currentLevel.name && w.vertex.name.length > 0, + (p) => p.level === currentLevel.name && p.vertex.name.length > 0, ), ); }, [buildingMap, currentLevel]); diff --git a/packages/react-components/lib/tasks/create-task.stories.tsx b/packages/react-components/lib/tasks/create-task.stories.tsx index ae73b0c30..7b39ce6de 100644 --- a/packages/react-components/lib/tasks/create-task.stories.tsx +++ b/packages/react-components/lib/tasks/create-task.stories.tsx @@ -25,6 +25,6 @@ CreateTask.args = { submitTasks: async () => new Promise((res) => setTimeout(res, 500)), cleaningZones: ['test_zone_0', 'test_zone_1'], patrolWaypoints: ['test_waypoint_0', 'test_waypoint_1'], - pickupWaypoints: ['test_waypoint_0'], - dropoffWaypoints: ['test_waypoint_1'], + pickupPoints: ['test_waypoint_0'], + dropoffPoints: ['test_waypoint_1'], }; From 9837319c40d7f40c6522e5c6dfe37bd9cd8481bf Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Thu, 24 Aug 2023 04:08:55 +0000 Subject: [PATCH 10/10] Refactor if case Signed-off-by: Aaron Chong --- packages/react-components/lib/place.ts | 27 +++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/react-components/lib/place.ts b/packages/react-components/lib/place.ts index 583bd0ae7..6afaba637 100644 --- a/packages/react-components/lib/place.ts +++ b/packages/react-components/lib/place.ts @@ -39,21 +39,22 @@ export function getNamedPlaces(buildingMap: BuildingMap): NamedPlaces { for (const level of buildingMap.levels) { for (const graphs of level.nav_graphs) { for (const vertex of graphs.vertices) { - if (vertex.name) { - const place: Place = { level: level.name, vertex }; - for (const p of vertex.params) { - if (p.name === DEFAULT_PICKUP_POINT_PARAM_NAME) { - pickupPoints.set(vertex.name, place); - } - if (p.name === DEFAULT_DROPOFF_POINT_PARAM_NAME) { - dropoffPoints.set(vertex.name, place); - } - if (p.name === DEFAULT_CLEANING_ZONE_PARAM_NAME) { - cleaningZones.set(vertex.name, place); - } + if (!vertex.name) { + continue; + } + const place: Place = { level: level.name, vertex }; + for (const p of vertex.params) { + if (p.name === DEFAULT_PICKUP_POINT_PARAM_NAME) { + pickupPoints.set(vertex.name, place); + } + if (p.name === DEFAULT_DROPOFF_POINT_PARAM_NAME) { + dropoffPoints.set(vertex.name, place); + } + if (p.name === DEFAULT_CLEANING_ZONE_PARAM_NAME) { + cleaningZones.set(vertex.name, place); } - places.set(vertex.name, place); } + places.set(vertex.name, place); } } }