Skip to content

Commit ce18651

Browse files
authored
Fail softly and carry a big error (#1865)
* Prevent freewheeling when channel is closed * Add soft-errors and check if robot wants to walk into rule-obstacle * Impl PathSerde for serde_json::Value * Print tick number with soft error message * Add soft error exception for penalty kick striker * Restrict midfielders x position during penalty kick
1 parent 9a11dd6 commit ce18651

8 files changed

Lines changed: 116 additions & 4 deletions

File tree

crates/bevyhavior_simulator/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub mod robot;
1717
pub mod scenario;
1818
pub mod server;
1919
pub mod simulator;
20+
pub mod soft_error;
21+
pub mod test_rules;
2022
pub mod time;
2123
pub mod visual_referee;
2224
pub mod whistle;

crates/bevyhavior_simulator/src/server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ async fn timeline_server(
5757
if !progress.is_finished() {
5858
progress.finish();
5959
}
60+
continue;
6061
}
6162
}
6263
}

crates/bevyhavior_simulator/src/simulator.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bevy::{
1010
time::Time,
1111
};
1212
use color_eyre::{
13-
eyre::{eyre, Context, ContextCompat},
13+
eyre::{bail, eyre, Context, ContextCompat},
1414
Result,
1515
};
1616

@@ -25,6 +25,8 @@ use crate::{
2525
recorder::Recording,
2626
robot::{cycle_robots, move_robots, Messages},
2727
server::Parameters,
28+
soft_error::{soft_error_plugin, SoftErrorResource},
29+
test_rules::check_robots_dont_walk_into_rule_obstacles,
2830
time::{update_time, Ticks},
2931
visual_referee::VisualRefereeResource,
3032
whistle::WhistleResource,
@@ -54,6 +56,7 @@ impl Plugin for SimulatorPlugin {
5456
))
5557
.add_plugins(autoref_plugin)
5658
.add_plugins(game_controller_plugin)
59+
.add_plugins(soft_error_plugin)
5760
.insert_resource(SimulatorFieldDimensions::from(parameters.field_dimensions))
5861
.insert_resource(GameController::default())
5962
.insert_resource(BallResource::default())
@@ -64,6 +67,12 @@ impl Plugin for SimulatorPlugin {
6467
.insert_resource(Time::<Ticks>::default())
6568
.add_systems(First, update_time)
6669
.add_systems(Update, cycle_robots.before(move_robots).after(autoref))
70+
.add_systems(
71+
Update,
72+
check_robots_dont_walk_into_rule_obstacles
73+
.before(move_robots)
74+
.after(cycle_robots),
75+
)
6776
.add_systems(Update, move_robots)
6877
.add_systems(Update, move_ball.after(move_robots));
6978

@@ -94,10 +103,19 @@ impl AppExt for App {
94103
recording.join()?
95104
}
96105

97-
match exit {
98-
AppExit::Success => Ok(()),
99-
AppExit::Error(code) => Err(eyre!("Scenario exited with error code {code}")),
106+
if let AppExit::Error(code) = exit {
107+
return Err(eyre!("Scenario exited with error code {code}"));
100108
}
109+
110+
let soft_errors = self
111+
.world_mut()
112+
.get_resource_mut::<SoftErrorResource>()
113+
.expect("soft error storage should exist");
114+
if !soft_errors.errors.is_empty() {
115+
bail!("{} soft error(s) found", soft_errors.errors.len())
116+
}
117+
118+
Ok(())
101119
}
102120
}
103121

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use bevy::{
2+
app::App,
3+
ecs::system::{Res, ResMut, Resource, SystemParam},
4+
time::Time,
5+
};
6+
7+
use crate::time::{Ticks, TicksTime};
8+
9+
#[derive(Clone)]
10+
pub struct SoftError;
11+
12+
#[derive(Default, Resource)]
13+
pub struct SoftErrorResource {
14+
pub errors: Vec<SoftError>,
15+
}
16+
17+
#[derive(SystemParam)]
18+
pub struct SoftErrorSender<'w> {
19+
time: Res<'w, Time<Ticks>>,
20+
resource: ResMut<'w, SoftErrorResource>,
21+
}
22+
23+
impl SoftErrorSender<'_> {
24+
pub fn send(&mut self, message: impl Into<String>) {
25+
let message = message.into();
26+
let tick = self.time.ticks();
27+
println!("{tick} {message}");
28+
if self.resource.errors.is_empty() {
29+
self.resource.errors.push(SoftError);
30+
}
31+
}
32+
}
33+
34+
pub fn soft_error_plugin(app: &mut App) {
35+
app.insert_resource(SoftErrorResource::default());
36+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use bevy::ecs::system::{Query, ResMut};
2+
use types::{motion_command::MotionCommand, planned_path::PathSegment, roles::Role};
3+
4+
use crate::{game_controller::GameController, robot::Robot, soft_error::SoftErrorSender};
5+
6+
pub fn check_robots_dont_walk_into_rule_obstacles(
7+
robots: Query<&Robot>,
8+
game_controller: ResMut<GameController>,
9+
mut soft_error: SoftErrorSender,
10+
) {
11+
for robot in robots.iter() {
12+
let rule_obstacles = &robot.database.main_outputs.rule_obstacles;
13+
let motion_command = &robot.database.main_outputs.motion_command;
14+
let MotionCommand::Walk { path, .. } = motion_command else {
15+
continue;
16+
};
17+
let Some(PathSegment::LineSegment(segment)) = path.last() else {
18+
continue;
19+
};
20+
let destination_in_field = robot.ground_to_field() * segment.1;
21+
22+
if game_controller.state.sub_state == Some(spl_network_messages::SubState::PenaltyKick)
23+
&& robot.database.main_outputs.role == Role::Striker
24+
{
25+
continue;
26+
}
27+
28+
for obstacle in rule_obstacles {
29+
if obstacle.contains(destination_in_field) {
30+
soft_error.send(format!(
31+
"Robot {} ran into rule obstacle",
32+
robot.parameters.player_number
33+
));
34+
}
35+
}
36+
}
37+
}

crates/control/src/behavior/support.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ fn support_pose(
9595
(Some(FilteredGameState::Ready), Some(Some(SubState::PenaltyKick))) => {
9696
supporting_position.x().max(field_dimensions.length / 4.0)
9797
}
98+
(Some(FilteredGameState::Playing { .. }), Some(Some(SubState::GoalKick))) => {
99+
supporting_position.x().min(field_dimensions.length / 4.0)
100+
}
98101
(Some(FilteredGameState::Ready), _)
99102
| (
100103
Some(FilteredGameState::Playing {

crates/geometry/src/rectangle.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ impl<Frame> Rectangle<Frame> {
4242
let dimensions = self.max - self.min;
4343
dimensions.x() * dimensions.y()
4444
}
45+
46+
pub fn contains(self, point: Point2<Frame>) -> bool {
47+
(self.min.x()..=self.max.x()).contains(&point.x())
48+
&& (self.min.y()..=self.max.y()).contains(&point.y())
49+
}
4550
}

crates/types/src/rule_obstacles.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use geometry::{circle::Circle, rectangle::Rectangle};
2+
use linear_algebra::Point2;
23
use path_serde::{PathDeserialize, PathIntrospect, PathSerialize};
34
use serde::{Deserialize, Serialize};
45

@@ -11,3 +12,12 @@ pub enum RuleObstacle {
1112
Circle(Circle<Field>),
1213
Rectangle(Rectangle<Field>),
1314
}
15+
16+
impl RuleObstacle {
17+
pub fn contains(&self, point: Point2<Field>) -> bool {
18+
match self {
19+
RuleObstacle::Circle(circle) => circle.contains(point),
20+
RuleObstacle::Rectangle(rectangle) => rectangle.contains(point),
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)