Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ac3333c
Reorg fleet name param
xiyuoh Jul 15, 2025
7daa517
In-place task edit
xiyuoh Jul 16, 2025
c4f8099
Move Create New Task to center window
xiyuoh Jul 16, 2025
3989422
Move task panel to the left
xiyuoh Jul 22, 2025
0a97f7a
Children widget in dialog if create new task
xiyuoh Jul 23, 2025
a4fb9c3
Optional serializing for some items
xiyuoh Jul 23, 2025
dfd30f6
Infer fleet from robot
xiyuoh Jul 23, 2025
45c9a19
Merge remote-tracking branch 'origin/main' into xiyu/improve_tasks
xiyuoh Jul 23, 2025
a0f9023
Remove Grid to prevent glitch
xiyuoh Jul 29, 2025
e45e884
Merge branch 'main' into xiyu/improve_tasks
xiyuoh Aug 25, 2025
4975976
Update test site
xiyuoh Aug 25, 2025
c6211d4
Merge branch 'main' into xiyu/improve_tasks
luca-della-vedova Aug 29, 2025
c23dffa
Check if TaskKind is valid
xiyuoh Sep 1, 2025
f129a4a
In-place editing for task kind
xiyuoh Sep 1, 2025
a2390aa
Implement time sorting for tasks
xiyuoh Sep 2, 2025
73dc9a3
Fix pending tasks TaskParams not saved
xiyuoh Sep 2, 2025
8c278e2
Use Point<Entity> instead of String for GoToPlace locations
xiyuoh Sep 2, 2025
be19460
Merge remote-tracking branch 'origin/main' into xiyu/improve_tasks
xiyuoh Sep 2, 2025
4946083
Task-based visual cue for GoToPlace
xiyuoh Sep 3, 2025
f67cc3b
Use entities instead of name for Robot
xiyuoh Sep 3, 2025
ec9443d
Use timestamp as task id and fix saving
xiyuoh Sep 3, 2025
3c43a73
Fix saving location in tasks
xiyuoh Sep 4, 2025
f79bb7d
Insert valid task location
xiyuoh Sep 4, 2025
5de4cb7
Merge remote-tracking branch 'origin/main' into xiyu/improve_tasks
xiyuoh Sep 5, 2025
7de9d83
Do not allow editing fleet name via task widget
xiyuoh Sep 9, 2025
a92bf8a
Merge remote-tracking branch 'origin/main' into xiyu/improve_tasks
xiyuoh Oct 15, 2025
dec22ac
TODO: check why negotiation loops when goal changes
xiyuoh Oct 15, 2025
1d5c9bb
Merge remote-tracking branch 'origin/main' into xiyu/improve_tasks
xiyuoh Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ ron = "0.10"
thiserror = "*"
glam = { version = "0.29" } # Ensure that this match's bevy_math's glam before updating.
uuid = { version = "1.13"}
chrono = "*"
sdformat_rs = { git = "https://github.com/open-rmf/sdf_rust_experimental", rev = "514949e" }
urdf-rs = { version = "0.7.3"}
once_cell = "1"
Expand Down
68 changes: 64 additions & 4 deletions assets/demo_maps/test.site.json
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,8 @@
}
}
},
"inclusion": "Included"
"inclusion": "Included",
"on_level": 22
},
"62": {
"pose": {
Expand All @@ -879,7 +880,8 @@
]
}
},
"inclusion": "Included"
"inclusion": "Included",
"on_level": 22
},
"63": {
"pose": {
Expand All @@ -894,7 +896,8 @@
}
}
},
"inclusion": "Included"
"inclusion": "Included",
"on_level": 22
},
"122": {
"pose": {
Expand Down Expand Up @@ -994,6 +997,32 @@
}
},
"inclusion": "Included"
},
"138": {
"inclusion": "Included"
},
"139": {
"inclusion": "Included"
}
},
"tasks": {
"138": {
"inclusion": "Included",
"params": {
"unix_millis_earliest_start_time": null,
"unix_millis_request_time": null,
"priority": null,
"labels": []
}
},
"139": {
"inclusion": "Included",
"params": {
"unix_millis_earliest_start_time": null,
"unix_millis_request_time": null,
"priority": null,
"labels": []
}
}
},
"name": "Default Scenario",
Expand All @@ -1016,6 +1045,7 @@
},
"robots": {
"52": {
"fleet": "HospitalRobot",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add tasks to this file so we make it part of our CI pipeline? It can be done in a followup if you prefer

"properties": {
"Collision": {
"config": {
Expand Down Expand Up @@ -1053,7 +1083,7 @@
},
"model_instances": {
"53": {
"parent": 1,
"parent": 22,
"name": "L1_robot",
"pose": {
"trans": [
Expand Down Expand Up @@ -1222,5 +1252,35 @@
},
"description": 129
}
},
"tasks": {
"138": {
"Dispatch": {
"request": {
"id": "task-20250904-030419",
"category": "Go To Place",
"description": {
"location": 121
},
"description_display": "A",
"created_time": 1756955059673
}
}
},
"139": {
"Direct": {
"robot": 53,
"fleet": "HospitalRobot",
"request": {
"id": "task-20250904-030447",
"category": "Wait For",
"description": {
"duration": 30.0
},
"description_display": "30 seconds",
"created_time": 1756955087335
}
}
}
}
}
1 change: 0 additions & 1 deletion crates/rmf_site_editor/src/interaction/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,5 @@ pub fn update_model_instance_visual_cues(
}
}
}
// TODO(@xiyuoh) support task-based visual cues
}
}
4 changes: 2 additions & 2 deletions crates/rmf_site_editor/src/mapf_rse/config_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub struct MapfConfigWidget<'w, 's> {
negotiation_task: Res<'w, NegotiationTask>,
occupancy_display: ResMut<'w, OccupancyDisplay>,
robots: Query<'w, 's, Entity, With<Robot>>,
tasks: Query<'w, 's, &'static Task>,
tasks: Query<'w, 's, &'static Task<Entity>>,
}

impl<'w, 's> WidgetSystem<Tile> for MapfConfigWidget<'w, 's> {
Expand Down Expand Up @@ -80,7 +80,7 @@ impl<'w, 's> MapfConfigWidget<'w, 's> {
.tasks
.iter()
.filter(|task| {
if task.request().category() == GoToPlace::label() {
if task.request().category() == GoToPlace::<Entity>::label() {
true
} else {
false
Expand Down
18 changes: 8 additions & 10 deletions crates/rmf_site_editor/src/mapf_rse/negotiation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::{
occupancy::{Cell, Grid},
site::{
Affiliation, CircleCollision, CurrentLevel, DifferentialDrive, GoToPlace, Group,
LocationTags, ModelMarker, NameInSite, Point, Pose, Robot, Task as RobotTask,
LocationTags, ModelMarker, Point, Pose, Robot, Task as RobotTask,
},
};
use mapf::negotiation::*;
Expand Down Expand Up @@ -225,17 +225,17 @@ pub fn handle_compute_negotiation_complete(
}

pub fn start_compute_negotiation(
locations: Query<(&NameInSite, &Point<Entity>), With<LocationTags>>,
locations: Query<&Point<Entity>, With<LocationTags>>,
anchors: Query<&GlobalTransform>,
negotiation_request: EventReader<NegotiationRequest>,
negotiation_params: Res<NegotiationParams>,
mut negotiation_debug_data: ResMut<NegotiationDebugData>,
current_level: Res<CurrentLevel>,
grids: Query<(Entity, &Grid)>,
child_of: Query<&ChildOf>,
robots: Query<(Entity, &NameInSite, &Pose, &Affiliation<Entity>), With<Robot>>,
robots: Query<(Entity, &Pose, &Affiliation<Entity>), With<Robot>>,
robot_descriptions: Query<(&DifferentialDrive, &CircleCollision)>,
tasks: Query<(&RobotTask, &GoToPlace)>,
tasks: Query<(&RobotTask<Entity>, &GoToPlace<Entity>)>,
mut negotiation_task: ResMut<NegotiationTask>,
) {
if negotiation_request.len() == 0 {
Expand Down Expand Up @@ -286,13 +286,11 @@ pub fn start_compute_negotiation(
let mut agents = BTreeMap::<String, Agent>::new();
// Only loop tasks that have specified a valid robot
for (task, go_to_place) in tasks.iter() {
// Identify robot
let robot_name = task.robot();
for (robot_entity, robot_site_name, robot_pose, robot_group) in robots.iter() {
if robot_name == robot_site_name.0 {
for (robot_entity, robot_pose, robot_group) in robots.iter() {
if task.robot().0.is_some_and(|e| robot_entity == e) {
// Match location to entity
for (location_name, Point(anchor_entity)) in locations.iter() {
if location_name.0 == go_to_place.location {
for Point(anchor_entity) in locations.iter() {
if go_to_place.location.0.is_some_and(|e| e == *anchor_entity) {
let Ok(goal_transform) = anchors.get(*anchor_entity) else {
warn!("Unable to get robot's goal transform");
continue;
Expand Down
5 changes: 4 additions & 1 deletion crates/rmf_site_editor/src/site/inclusion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ impl Property for Inclusion {
fn on_new_element(
for_element: Entity,
in_scenario: Entity,
_value: Inclusion,
value: Inclusion,
world: &mut World,
) {
// Insert inclusion modifier with given value into target scenario
world.trigger(UpdateModifier::modify(in_scenario, for_element, value));

let mut scenario_state: SystemState<
Query<(Entity, &ScenarioModifiers<Entity>, &Affiliation<Entity>)>,
> = SystemState::new(world);
Expand Down
3 changes: 2 additions & 1 deletion crates/rmf_site_editor/src/site/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,9 @@ fn generate_site_entities(
}

for (task_id, task_data) in &site_data.tasks {
let task = task_data.convert(&id_to_entity).for_site(site_id)?;
let task_entity = commands
.spawn(task_data.clone())
.spawn(task.clone())
.insert(SiteID(*task_id))
.insert(Category::Task)
.insert(ChildOf(site_id))
Expand Down
6 changes: 3 additions & 3 deletions crates/rmf_site_editor/src/site/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ impl Plugin for SitePlugin {
ChangePlugin::<ModelProperty<Scale>>::default(),
ChangePlugin::<ModelProperty<IsStatic>>::default(),
ChangePlugin::<ModelProperty<Robot>>::default(),
ChangePlugin::<Task>::default(),
ChangePlugin::<Task<Entity>>::default(),
PropertyPlugin::<Pose, InstanceMarker>::default(),
PropertyPlugin::<Inclusion, InstanceMarker>::default(),
PropertyPlugin::<Inclusion, Task>::default(),
PropertyPlugin::<TaskParams, Task>::default(),
PropertyPlugin::<Inclusion, Task<Entity>>::default(),
PropertyPlugin::<TaskParams, Task<Entity>>::default(),
PropertyPlugin::<OnLevel<Entity>, Robot>::default(),
SlotcarSdfPlugin,
MaterialPlugin::<ExtendedMaterial<StandardMaterial, LaneArrowMaterial>>::default(),
Expand Down
38 changes: 30 additions & 8 deletions crates/rmf_site_editor/src/site/save.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fn assign_site_ids(world: &mut World, site: Entity) -> Result<(), SiteGeneration
Query<Entity, (With<ModelMarker>, With<Group>)>,
Query<Entity, (With<ModelMarker>, Without<Group>, Without<Preview>)>,
Query<(Entity, &Affiliation<Entity>), With<ScenarioModifiers<Entity>>>,
Query<Entity, (With<Task>, Without<Pending>)>,
Query<Entity, (With<Task<Entity>>, Without<Pending>)>,
Query<
Entity,
(
Expand Down Expand Up @@ -1541,15 +1541,37 @@ fn generate_scenarios(
fn generate_tasks(
site: Entity,
world: &mut World,
) -> Result<BTreeMap<u32, Task>, SiteGenerationError> {
let mut state: SystemState<(Query<(&SiteID, &Task), Without<Pending>>, Query<&Children>)> =
SystemState::new(world);
let (tasks, children) = state.get(world);
let mut res = BTreeMap::<u32, Task>::new();
) -> Result<BTreeMap<u32, Task<u32>>, SiteGenerationError> {
let mut state: SystemState<(
Query<(Entity, &SiteID, &Task<Entity>), Without<Pending>>,
Query<&SiteID, (With<Robot>, Without<Pending>)>,
Query<&Children>,
)> = SystemState::new(world);
let (tasks, robots, children) = state.get(world);
let mut res = BTreeMap::<u32, Task<u32>>::new();
if let Ok(children) = children.get(site) {
for child in children.iter() {
if let Ok((site_id, task)) = tasks.get(child) {
res.insert(site_id.0, task.clone());
if let Ok((task_entity, site_id, task)) = tasks.get(child) {
let task = match task {
Task::Dispatch(request) => Task::Dispatch(request.clone()),
Task::Direct(request) => {
let robot_entity = match request.robot.0 {
Some(e) => e,
None => return Err(SiteGenerationError::EmptyAffiliation(task_entity)),
};
Task::Direct(RobotTaskRequest::new(
match robots.get(robot_entity) {
Ok(id) => Affiliation(Some(id.0)),
Err(_) => {
return Err(SiteGenerationError::MissingSiteID(robot_entity))
}
},
request.fleet.clone(),
request.request.clone(),
))
}
};
res.insert(site_id.0, task);
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions crates/rmf_site_editor/src/site/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,24 @@ use std::collections::HashMap;

pub type InsertTaskKindFn = fn(EntityCommands);
pub type RemoveTaskKindFn = fn(EntityCommands);
pub type IsTaskValidFn = fn(Entity, &mut World) -> bool;

#[derive(Resource)]
pub struct TaskKinds(pub HashMap<String, (InsertTaskKindFn, RemoveTaskKindFn)>);
pub struct TaskKinds(pub HashMap<String, (InsertTaskKindFn, RemoveTaskKindFn, IsTaskValidFn)>);

impl FromWorld for TaskKinds {
fn from_world(_world: &mut World) -> Self {
TaskKinds(HashMap::new())
}
}

impl Element for Task {}
impl Element for Task<Entity> {}

impl StandardProperty for TaskParams {}

pub fn update_task_kind_component<T: TaskKind>(
mut commands: Commands,
tasks: Query<(Entity, Ref<Task>, Option<&T>)>,
tasks: Query<(Entity, Ref<Task<Entity>>, Option<&T>)>,
) {
for (entity, task, task_kind) in tasks.iter() {
if task.is_changed() {
Expand Down
Loading
Loading