paiagram/
troubleshoot.rs

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}