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