1use bevy::prelude::*;
2use itertools::Itertools;
3
4use crate::entry::{
5 DerivedEntryBundle, EntryEstimate, EntryMode, EntryStop, IsDerivedEntry, TravelMode,
6};
7use crate::graph::Graph;
8use crate::interval::IntervalQuery;
9use crate::station::ParentStationOrStation;
10use crate::trip::{TripClass, TripNominalSchedule, TripQuery, TripSchedule};
11use crate::units::distance::Distance;
12use crate::units::time::{Duration, TimetableTime};
13
14pub struct RoutingPlugin;
15
16impl Plugin for RoutingPlugin {
17 fn build(&self, app: &mut App) {
18 app.add_message::<AddEntryToTrip>().add_systems(
19 Update,
20 (add_entries, recalculate_route, recalculate_estimate).chain(),
21 );
22 }
23}
24
25#[derive(Message, Clone, Copy)]
26pub struct AddEntryToTrip {
27 pub trip: Entity,
28 pub entry: Entity,
29}
30
31pub fn add_entries(
32 mut msgs: MessageReader<AddEntryToTrip>,
33 mut commands: Commands,
34 mut trips: Query<&mut TripNominalSchedule>,
35) {
36 for AddEntryToTrip { trip, entry } in msgs.read().copied() {
37 commands.entity(trip).add_child(entry);
38 trips.get_mut(trip).unwrap().push(entry);
39 }
40}
41
42pub fn recalculate_route(
43 changed_schedule: Query<
44 (Entity, &TripNominalSchedule, &mut TripSchedule),
45 Changed<TripNominalSchedule>,
46 >,
47 entry_q: Query<(Entity, &EntryStop)>,
48 derived_q: Query<Entity, With<IsDerivedEntry>>,
49 graph: Res<Graph>,
50 parent_station_or_station: Query<ParentStationOrStation>,
51 mut commands: Commands,
52 interval_q: Query<IntervalQuery>,
53) {
54 for (trip_entity, nominal_schedule, mut actual_schedule) in changed_schedule {
55 for derived_entity in derived_q.iter_many(actual_schedule.iter()) {
56 commands.entity(derived_entity).despawn();
57 }
58 recalculate_inner(
59 nominal_schedule,
60 trip_entity,
61 &mut actual_schedule,
62 &graph,
63 &mut commands,
64 &parent_station_or_station,
65 &entry_q,
66 &interval_q,
67 );
68 }
69}
70
71fn recalculate_inner(
72 route: &[Entity],
73 trip_entity: Entity,
74 buffer: &mut Vec<Entity>,
75 graph: &Graph,
76 commands: &mut Commands,
77 parent_station_or_station: &Query<ParentStationOrStation>,
78 entry_q: &Query<(Entity, &EntryStop)>,
79 interval_q: &Query<IntervalQuery>,
80) {
81 buffer.clear();
82 let mut route_iter = entry_q.iter_many(route.iter()).map(|(a, c)| {
83 (
84 a,
85 parent_station_or_station.get(c.entity()).unwrap().parent(),
86 )
87 });
88 let Some((prev_entity, mut prev_stop)) = route_iter.next() else {
89 return;
90 };
91 buffer.push(prev_entity);
92 for (curr_entity, stops) in route_iter.map(|(curr_entity, curr_stop)| {
93 if prev_stop == curr_stop || graph.contains_edge(prev_stop.entity(), curr_stop.entity()) {
94 prev_stop = curr_stop;
95 return (curr_entity, Vec::new());
96 }
97 let Some((_, mut station_list)) =
98 graph.route_between(prev_stop.entity(), curr_stop.entity(), interval_q)
99 else {
100 prev_stop = curr_stop;
101 return (curr_entity, Vec::new());
102 };
103 prev_stop = curr_stop;
104 station_list.remove(0);
105 station_list.pop();
106 return (curr_entity, station_list);
107 }) {
108 for stop in stops {
109 let e = commands.spawn(DerivedEntryBundle::new(stop)).id();
110 commands.entity(trip_entity).add_child(e);
111 buffer.push(e)
112 }
113 buffer.push(curr_entity);
114 }
115}
116
117enum UnwindParams {
119 At(TimetableTime),
120 ForAt(Duration, TimetableTime),
121 ForFor(Duration, Duration),
122}
123
124fn recalculate_estimate(
127 changed_trips: Query<Entity, (Changed<TripSchedule>, With<TripClass>)>,
128 changed_entries: Query<&ChildOf, Changed<EntryMode>>,
129 trip_q: Query<TripQuery>,
130 entry_q: Query<(Entity, &EntryMode, &EntryStop)>,
131 parent_station_or_station: Query<ParentStationOrStation>,
132 interval_q: Query<IntervalQuery>,
133 mut commands: Commands,
134 graph: Res<Graph>,
135) {
136 let mut to_recalculate = changed_entries
137 .iter()
138 .map(|c| c.parent())
139 .chain(changed_trips.iter())
140 .collect::<Vec<_>>();
141 to_recalculate.sort_unstable();
142 to_recalculate.dedup();
143 for q in trip_q.iter_many(to_recalculate) {
144 let mut flexible_stack: Vec<(Entity, Entity, Duration)> = Vec::new();
145 let mut last_stable: Option<(TimetableTime, Entity)> = None;
146 let mut next_stable: Option<(TimetableTime, Entity)> = None;
147 let mut unwind_params: Option<UnwindParams> = None;
148 'iter_entries: for (entry_entity, mode, stop) in entry_q.iter_many(q.schedule.iter()) {
149 if let Some(v) = next_stable.take() {
150 last_stable = Some(v);
151 }
152 match (mode.arr.unwrap_or(TravelMode::Flexible), mode.dep) {
153 (TravelMode::At(at), TravelMode::At(dt)) => {
154 commands
155 .entity(entry_entity)
156 .insert(EntryEstimate::new(at, dt));
157 next_stable = Some((dt, stop.entity()));
158 unwind_params = Some(UnwindParams::At(at));
159 }
160 (TravelMode::At(at), TravelMode::For(dd)) => {
161 commands
162 .entity(entry_entity)
163 .insert(EntryEstimate::new(at, at + dd));
164 next_stable = Some((at + dd, stop.entity()));
165 unwind_params = Some(UnwindParams::At(at));
166 }
167 (TravelMode::At(at), TravelMode::Flexible) => {
168 commands
169 .entity(entry_entity)
170 .insert(EntryEstimate::new(at, at));
171 next_stable = Some((at, stop.entity()));
172 unwind_params = Some(UnwindParams::At(at));
173 }
174 (TravelMode::For(ad), TravelMode::At(dt)) => {
175 next_stable = Some((dt, stop.entity()));
177 unwind_params = Some(UnwindParams::ForAt(ad, dt));
178 }
179 (TravelMode::For(ad), TravelMode::For(dd)) => {
180 unwind_params = Some(UnwindParams::ForFor(ad, dd));
182 }
183 (TravelMode::For(ad), TravelMode::Flexible) => {
184 unwind_params = Some(UnwindParams::ForFor(ad, Duration::ZERO));
186 }
187 (TravelMode::Flexible, TravelMode::At(dt)) => {
188 commands
189 .entity(entry_entity)
190 .insert(EntryEstimate::new(dt, dt));
191 next_stable = Some((dt, stop.entity()));
192 unwind_params = Some(UnwindParams::At(dt))
193 }
194 (TravelMode::Flexible, TravelMode::For(dd)) => {
195 flexible_stack.push((entry_entity, stop.entity(), dd))
196 }
197 (TravelMode::Flexible, TravelMode::Flexible) => {
198 flexible_stack.push((entry_entity, stop.entity(), Duration::ZERO))
199 }
200 }
201 let Some(params) = unwind_params.take() else {
202 continue;
203 };
204 let Some((last_t, last_s)) = last_stable else {
205 for (e, _, _) in flexible_stack.drain(..) {
206 commands.entity(e).remove::<EntryEstimate>();
207 }
208 continue;
209 };
210 let initial_t = last_t;
211 let total_stop_dur: Duration = flexible_stack.iter().map(|(_, _, d)| *d).sum();
212 let total_dur = match params {
213 UnwindParams::ForAt(d, _t) => d,
214 UnwindParams::ForFor(ad, _dd) => ad,
215 UnwindParams::At(t) => t - initial_t,
216 };
217 let travel_dur = total_dur - total_stop_dur;
219 let mut distance_stack = Vec::with_capacity(flexible_stack.len());
220
221 for (ps, cs) in std::iter::once(last_s)
222 .chain(flexible_stack.iter().map(|(_, s, _)| *s))
223 .chain(std::iter::once(stop.entity()))
224 .map(|e| parent_station_or_station.get(e).unwrap().parent())
225 .tuple_windows()
226 {
227 let Some(weight) = graph
228 .edge_weight(ps, cs)
229 .copied()
230 .map(|w| interval_q.get(w).ok())
231 .flatten()
232 else {
233 for (e, _, _) in flexible_stack.drain(..) {
234 commands.entity(e).remove::<EntryEstimate>();
235 }
236 continue 'iter_entries;
237 };
238 distance_stack.push(weight.distance())
239 }
240 debug_assert_eq!(distance_stack.len(), flexible_stack.len() + 1);
241 let total_dis = distance_stack.iter().cloned().sum::<Distance>();
242 let mut fi = flexible_stack.drain(..);
243 let mut di = distance_stack.drain(..);
244 let total_dis_m = total_dis.0 as f64;
245 let travel_dur_s = travel_dur.0 as f64;
246 let mut last_t_f = last_t.0 as f64;
247 while let (Some((e, _, dur)), Some(dis)) = (fi.next(), di.next()) {
248 let dis_m = dis.0 as f64;
249 let travel_leg_s = if total_dis_m == 0.0 {
250 0.0
251 } else {
252 travel_dur_s * (dis_m / total_dis_m)
253 };
254 last_t_f += travel_leg_s;
255 let arr = TimetableTime(last_t_f.round() as i32);
256 commands.entity(e).insert(EntryEstimate {
257 arr,
258 dep: arr + dur,
259 });
260 last_t_f += dur.0 as f64;
261 }
262 match params {
263 UnwindParams::At(_) => {}
264 UnwindParams::ForAt(d, t) => {
265 commands.entity(entry_entity).insert(EntryEstimate {
266 arr: initial_t + d,
267 dep: t,
268 });
269 }
270 UnwindParams::ForFor(ad, dd) => {
271 commands.entity(entry_entity).insert(EntryEstimate {
272 arr: initial_t + ad,
273 dep: initial_t + ad + dd,
274 });
275 next_stable = Some((initial_t + ad + dd, stop.entity()))
276 }
277 }
278 }
279 for (e, _, _) in flexible_stack {
280 commands.entity(e).remove::<EntryEstimate>();
281 }
282 }
283}