1use bevy::platform::collections::HashMap;
2use bevy::prelude::*;
3use moonshine_core::kind::Instance;
4use serde::{Deserialize, Serialize};
5use serde_json;
6
7use crate::class::{Class, ClassBundle, DisplayedStroke};
8use crate::entry::{EntryBundle, TravelMode};
9use crate::graph::Graph;
10use crate::route::Route;
11use crate::station::Station;
12use crate::trip::{TripBundle, TripClass};
13use crate::units::distance::Distance;
14use crate::units::time::TimetableTime;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct TimetableData {
18 pub stations: Vec<String>,
19 pub lines: Vec<Line>,
20 pub trains: Vec<Train>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Line {
25 pub code: i64,
26 pub name: String,
27 pub stations: Vec<LineStation>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct LineStation {
32 pub station: String,
33 pub telecode: String,
34 #[serde(rename = "routeFlag")]
35 pub route_flag: i64,
36 pub distance: i64,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Train {
41 pub number: String,
42 #[serde(rename = "type")]
43 pub r#type: String,
44 pub stops: Vec<TrainStop>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct TrainStop {
49 pub station: String,
50 #[serde(rename = "type")]
51 pub r#type: StopType,
52 pub line_code: i64,
53 pub arrival_time: String,
54 pub departure_time: String,
55 pub mileage: i64,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "lowercase")]
60pub enum StopType {
61 Stop,
62 Crossline,
63}
64
65pub fn load_llt(msg: On<super::LoadLlt>, mut graph: ResMut<Graph>, mut commands: Commands) {
66 let root: TimetableData = serde_json::from_str(&msg.content).unwrap();
67 let mut station_map: HashMap<String, Instance<Station>> = HashMap::new();
68 let mut class_map: HashMap<String, Instance<Class>> = HashMap::new();
69 for station_name in root.stations {
70 super::make_station(&station_name, &mut station_map, &mut graph, &mut commands);
71 }
72 for Line {
73 code: _,
74 name: line_name,
75 stations,
76 } in root.lines
77 {
78 let mut station_entities = Vec::with_capacity(stations.len());
79 let mut distances: Vec<f32> = Vec::with_capacity(stations.len());
80 let mut station_iter = stations.into_iter();
81 let Some(station) = station_iter.next() else {
82 continue;
83 };
84 let mut previous_station = super::make_station(
85 &station.station,
86 &mut station_map,
87 &mut graph,
88 &mut commands,
89 );
90 let mut previous_distance = station.distance;
91 station_entities.push(previous_station.entity());
92 distances.push(0.0f32);
93 for station in station_iter {
94 let current_station = super::make_station(
95 &station.station,
96 &mut station_map,
97 &mut graph,
98 &mut commands,
99 );
100 let interval_length = station.distance - previous_distance;
101 super::add_interval_pair(
102 &mut graph,
103 &mut commands,
104 previous_station.entity(),
105 current_station.entity(),
106 Distance::from_m(interval_length as i32 * 1000),
107 );
108 previous_distance = station.distance;
109 previous_station = current_station;
110 station_entities.push(previous_station.entity());
111 distances.push(interval_length as f32);
112 }
113 commands.spawn((
114 Route {
115 stops: station_entities,
116 lengths: distances,
117 },
118 Name::new(line_name),
119 ));
120 }
121 for Train {
122 number: trip_name,
123 r#type,
124 stops,
125 } in root.trains
126 {
127 let mut entries: Vec<_> = stops
128 .into_iter()
129 .map(|e| {
130 (
131 TimetableTime::from_str(&e.arrival_time),
132 TimetableTime::from_str(&e.departure_time),
133 super::make_station(&e.station, &mut station_map, &mut graph, &mut commands),
134 )
135 })
136 .collect();
137 super::normalize_times(
138 entries
139 .iter_mut()
140 .flat_map(|(a, d, _)| a.iter_mut().chain(d.iter_mut())),
141 );
142 let trip_class =
143 super::make_class(&r#type, &mut class_map, &mut commands, || ClassBundle {
144 class: Class::default(),
145 name: Name::new(r#type.clone()),
146 stroke: DisplayedStroke::from_seed(r#type.as_bytes()),
147 });
148
149 let nominal_schedule: Vec<_> = entries
150 .into_iter()
151 .map(|(arr, dep, stop)| {
152 let (arr, dep) = match (arr, dep) {
153 (None, None) => (None, TravelMode::Flexible),
154 (Some(t), None) | (None, Some(t)) => (None, TravelMode::At(t)),
155 (Some(at), Some(dt)) => {
156 if at == dt {
157 (None, TravelMode::At(dt))
158 } else {
159 (Some(TravelMode::At(at)), TravelMode::At(dt))
160 }
161 }
162 };
163 commands
164 .spawn(EntryBundle::new(arr, dep, stop.entity()))
165 .id()
166 })
167 .collect();
168
169 commands
170 .spawn_empty()
171 .add_children(&nominal_schedule)
172 .insert(TripBundle::new(
173 &trip_name,
174 TripClass(trip_class.entity()),
175 nominal_schedule,
176 ));
177 }
178}