1use bevy::prelude::*;
2
3use crate::{
4 units::time::Duration,
5 vehicles::{
6 AdjustTimetableEntry,
7 entries::{TimetableEntry, TimetableEntryCache, TravelMode, VehicleSchedule},
8 },
9};
10
11pub struct TroubleShootPlugin;
12
13impl Plugin for TroubleShootPlugin {
14 fn build(&self, app: &mut App) {
15 app.add_systems(
16 FixedPostUpdate,
17 analyze_entry.run_if(on_message::<AdjustTimetableEntry>),
18 );
19 }
20}
21
22#[derive(Component)]
23#[component(storage = "SparseSet")]
24pub struct ScheduleProblem(pub Vec<ScheduleProblemType>);
25pub enum ScheduleProblemType {
26 TooShort,
27 CollidesWithAnotherVehicle {
28 own_entry: Entity,
29 other_entry: Entity,
30 location: Entity,
31 },
32}
33
34#[derive(Component)]
35#[component(storage = "SparseSet")]
36pub struct EntryProblem(pub Vec<EntryProblemType>);
37#[derive(PartialEq, Eq)]
38pub enum EntryProblemType {
39 NoEstimation,
40 TravelDurationTooShort,
41 ReversedFlexibleMode,
42 CollidesWithAnotherEntry(Entity),
43}
44
45pub fn analyze_schedules(
46 mut commands: Commands,
47 regular_schedules: Query<&VehicleSchedule, Without<ScheduleProblem>>,
48 problematic_schedules: Query<&VehicleSchedule, With<ScheduleProblem>>,
49) {
50}
51
52pub fn analyze_entry(
53 mut commands: Commands,
54 mut msg_read_entry: MessageReader<AdjustTimetableEntry>,
55 mut entries: Query<(
56 Option<&mut EntryProblem>,
57 &TimetableEntry,
58 &TimetableEntryCache,
59 )>,
60) {
61 for entry_entity in msg_read_entry.read().map(|msg| msg.entity) {
62 let Ok((mut existing_problem, entry, entry_cache)) = entries.get_mut(entry_entity) else {
63 continue;
64 };
65
66 let check = |problems: &mut Vec<EntryProblemType>| {
67 if entry_cache.estimate.is_none() {
68 problems.push(EntryProblemType::NoEstimation)
69 }
70 if let TravelMode::For(a) = entry.arrival
71 && a <= Duration(0)
72 {
73 problems.push(EntryProblemType::TravelDurationTooShort);
74 } else if let Some(TravelMode::For(d)) = entry.departure
75 && d <= Duration(0)
76 {
77 problems.push(EntryProblemType::TravelDurationTooShort);
78 }
79 if matches!(entry.arrival, TravelMode::Flexible)
80 && matches!(entry.departure, Some(TravelMode::At(_)))
81 {
82 problems.push(EntryProblemType::ReversedFlexibleMode)
83 }
84 problems.dedup();
85 };
86
87 if let Some(ref mut problem) = existing_problem {
88 problem.0.clear();
89 check(&mut problem.0);
90 if problem.0.is_empty() {
91 commands.entity(entry_entity).remove::<EntryProblem>();
92 }
93 } else {
94 let mut problems = Vec::new();
95 check(&mut problems);
96 if !problems.is_empty() {
97 commands.entity(entry_entity).insert(EntryProblem(problems));
98 }
99 }
100 }
101}