1use crate::intervals::*;
2use crate::lines::{DisplayedLine, DisplayedLineType};
3use crate::units::canvas::CanvasLength;
4use crate::units::distance::Distance;
6use crate::units::time::{Duration, TimetableTime};
7use crate::vehicles::entries::{TimetableEntry, TravelMode, VehicleSchedule};
8use crate::vehicles::services::VehicleService;
9use crate::vehicles::vehicle_set::VehicleSet;
10use crate::vehicles::*;
11use bevy::platform::collections::HashMap;
12use serde::Deserialize;
13use serde_json;
14
15#[derive(Deserialize)]
16struct RawQETRCRoot {
17 #[serde(rename = "trains")]
20 services: Vec<RawQETRCService>,
21 line: RawQETRCLine,
26 lines: Option<Vec<RawQETRCLine>>,
28 #[serde(rename = "circuits")]
29 vehicles: Vec<RawQETRCVehicle>,
30}
31
32#[derive(Deserialize)]
33struct RawQETRCLine {
34 name: String,
35 stations: Vec<RawQETRCStation>,
36}
37
38#[derive(Deserialize)]
39struct RawQETRCStation {
40 #[serde(rename = "zhanming")]
41 name: String,
42 #[serde(rename = "licheng")]
43 distance: f32,
44}
45
46#[derive(Deserialize)]
47struct RawQETRCService {
48 #[serde(rename = "checi")]
49 service_number: Vec<String>,
50 timetable: Vec<RawQETRCTimetableEntry>,
53}
54
55#[derive(Deserialize)]
56struct RawQETRCTimetableEntry {
57 #[serde(rename = "business")]
58 stops: Option<bool>,
59 #[serde(rename = "ddsj")]
60 arrival: String,
61 #[serde(rename = "cfsj")]
62 departure: String,
63 #[serde(rename = "zhanming")]
64 station_name: String,
65}
66
67#[derive(Deserialize)]
68struct RawQETRCVehicle {
69 #[serde(rename = "model")]
70 make: String,
71 name: String,
72 #[serde(rename = "order")]
73 services: Vec<RawQETRCVehicleServiceEntry>,
74}
75
76#[derive(Deserialize)]
77struct RawQETRCVehicleServiceEntry {
78 #[serde(rename = "checi")]
79 service_number: String,
80}
81
82struct QETRCRoot {
83 services: Vec<QETRCService>,
86 lines: Vec<QETRCLine>,
87 vehicles: Vec<QETRCVehicle>,
88}
89
90struct QETRCLine {
91 name: String,
92 stations: Vec<QETRCStation>,
93}
94
95struct QETRCStation {
96 name: String,
97 distance: f32,
98}
99
100struct QETRCService {
101 name: String,
102 timetable: Vec<QETRCTimetableEntry>,
104}
105
106impl QETRCService {
107 fn shift_time(&mut self, time: Duration) {
108 self.timetable.iter_mut().for_each(|entry| {
109 entry.arrival += time;
110 entry.departure += time;
111 });
112 }
113}
114
115struct QETRCTimetableEntry {
116 stops: bool,
117 arrival: TimetableTime,
118 departure: TimetableTime,
119 station_name: String,
120}
121
122struct QETRCVehicle {
123 make: String,
124 name: String,
125 services: Vec<QETRCService>,
126}
127
128impl TryFrom<RawQETRCRoot> for QETRCRoot {
129 type Error = String;
130 fn try_from(value: RawQETRCRoot) -> Result<Self, Self::Error> {
131 let mut services = HashMap::with_capacity(value.services.len());
132 for raw_service in value.services {
133 let service = QETRCService::try_from(raw_service)?;
134 services.insert(service.name.clone(), service);
135 }
136 let mut vehicles = Vec::with_capacity(value.vehicles.len());
137 for raw_vehicle in value.vehicles {
138 let mut vehicle_services = Vec::with_capacity(raw_vehicle.services.len());
141 let mut last_entry: Option<&QETRCTimetableEntry> = None;
142 for raw_service in raw_vehicle.services {
143 if let Some((_, mut service)) = services.remove_entry(&raw_service.service_number) {
144 let current_first_entry = service.timetable.first();
145 if let (Some(last), Some(current_first)) = (last_entry, current_first_entry)
147 && current_first.arrival < last.departure
148 {
149 service.shift_time(Duration(86400));
151 }
152 vehicle_services.push(service);
153 last_entry = vehicle_services.last().and_then(|s| s.timetable.last());
154 }
155 }
156 vehicles.push(QETRCVehicle {
157 make: raw_vehicle.make,
158 name: raw_vehicle.name,
159 services: vehicle_services,
160 });
161 }
162 let services = services.into_values().collect::<Vec<_>>();
164 let mut lines = Vec::with_capacity(1 + value.lines.iter().len());
165 lines.push(QETRCLine::try_from(value.line)?);
166 if let Some(raw_lines) = value.lines {
167 lines.extend(
168 raw_lines
169 .into_iter()
170 .map(QETRCLine::try_from)
171 .collect::<Result<Vec<_>, _>>()?,
172 );
173 }
174 Ok(QETRCRoot {
175 services,
178 lines,
179 vehicles,
180 })
181 }
182}
183
184impl TryFrom<RawQETRCLine> for QETRCLine {
185 type Error = String;
186 fn try_from(value: RawQETRCLine) -> Result<Self, Self::Error> {
187 Ok(QETRCLine {
188 name: value.name,
189 stations: value
190 .stations
191 .into_iter()
192 .map(QETRCStation::try_from)
193 .collect::<Result<Vec<_>, _>>()?,
194 })
195 }
196}
197
198impl TryFrom<RawQETRCStation> for QETRCStation {
199 type Error = String;
200 fn try_from(value: RawQETRCStation) -> Result<Self, Self::Error> {
201 Ok(QETRCStation {
202 name: value.name,
203 distance: value.distance,
204 })
205 }
206}
207
208impl TryFrom<RawQETRCService> for QETRCService {
209 type Error = String;
210 fn try_from(value: RawQETRCService) -> Result<Self, Self::Error> {
211 let name = value.service_number.first().cloned().unwrap_or_default();
212 let mut timetable = value
213 .timetable
214 .into_iter()
215 .map(QETRCTimetableEntry::try_from)
216 .collect::<Result<Vec<_>, _>>()?;
217 let mut last_departure = TimetableTime(0);
218 for entry in &mut timetable {
219 if entry.arrival < last_departure {
220 entry.arrival.0 += 86400;
221 entry.departure.0 += 86400;
222 }
223 if entry.arrival > entry.departure {
224 entry.departure.0 += 86400;
225 }
226 last_departure = entry.departure;
227 }
228 Ok(QETRCService {
229 name,
230 timetable,
232 })
233 }
234}
235
236impl TryFrom<RawQETRCTimetableEntry> for QETRCTimetableEntry {
237 type Error = String;
238 fn try_from(value: RawQETRCTimetableEntry) -> Result<Self, Self::Error> {
239 Ok(QETRCTimetableEntry {
240 stops: value.stops.unwrap_or(false),
241 arrival: TimetableTime::from_str(&value.arrival).unwrap_or_default(),
242 departure: TimetableTime::from_str(&value.departure).unwrap_or_default(),
243 station_name: value.station_name,
244 })
245 }
246}
247
248fn parse_qetrc(json_str: &str) -> Result<QETRCRoot, String> {
249 let raw: RawQETRCRoot = serde_json::from_str(json_str).map_err(|e| e.to_string())?;
250 let qetrc_data: QETRCRoot = raw.try_into().map_err(|e: String| e.to_string())?;
251 Ok(qetrc_data)
253}
254
255use super::ModifyData;
256use bevy::prelude::*;
257
258pub fn load_qetrc(
261 mut commands: Commands,
262 mut reader: MessageReader<ModifyData>,
263 mut existing_graph: ResMut<Graph>,
264) {
265 let mut data: Option<&str> = None;
266 for modification in reader.read() {
267 let ModifyData::LoadQETRC(d) = modification else {
268 continue;
269 };
270 data = Some(d);
271 }
272 let Some(data) = data else {
273 return;
274 };
275 let vehicle_set_entity = commands
276 .spawn((VehicleSet, Name::new("qETRC Vehicle Set")))
277 .id();
278 let now = instant::Instant::now();
279 let qetrc_data = parse_qetrc(data).map_err(|e| e.to_string()).unwrap();
280 info!("Parsed QETRC data in {:.2?}", now.elapsed());
281 let now = instant::Instant::now();
282 let mut stations = std::collections::HashMap::new();
285 for line in qetrc_data.lines {
288 create_line_entities(&mut commands, line, &mut stations, &mut existing_graph.0);
289 }
290 ensure_stations_exist(
293 &mut commands,
294 &qetrc_data.services,
295 &qetrc_data.vehicles,
296 &mut stations,
297 );
298 for vehicle in qetrc_data.vehicles {
300 let new_vehicle_entity = create_vehicle(&mut commands, vehicle, &stations);
301 commands
302 .entity(vehicle_set_entity)
303 .add_child(new_vehicle_entity);
304 }
305 for service in qetrc_data.services {
307 let new_vehicle_entity = create_vehicle_from_service(&mut commands, service, &stations);
308 commands
309 .entity(vehicle_set_entity)
310 .add_child(new_vehicle_entity);
311 }
312 info!("Loaded QETRC data in {:.2?}", now.elapsed());
313}
314
315fn create_vehicle(
316 commands: &mut Commands,
317 vehicle: QETRCVehicle,
318 stations: &std::collections::HashMap<String, Entity>,
319) -> Entity {
320 let vehicle_entity = commands
321 .spawn((
322 Vehicle,
323 Name::new(format!("{} [{}]", vehicle.name, vehicle.make)),
324 ))
325 .id();
326 let mut timetable_entries = Vec::new();
327 for service in vehicle.services {
328 let service_entity = commands
329 .spawn((VehicleService { class: None }, Name::new(service.name)))
330 .id();
331 commands.entity(vehicle_entity).add_child(service_entity);
332 let service_entries = create_timetable_entries(
333 commands,
334 &service.timetable,
335 stations,
336 vehicle_entity,
337 Some(service_entity),
338 );
339 timetable_entries.extend(service_entries);
340 }
341 commands.entity(vehicle_entity).insert(VehicleSchedule {
342 entities: timetable_entries,
343 ..Default::default()
344 });
345 vehicle_entity
346}
347
348fn create_vehicle_from_service(
349 commands: &mut Commands,
350 service: QETRCService,
351 stations: &std::collections::HashMap<String, Entity>,
352) -> Entity {
353 let vehicle_entity = commands
354 .spawn((Vehicle, Name::new(service.name.clone())))
355 .id();
356 let service_entity = commands
357 .spawn((VehicleService { class: None }, Name::new(service.name)))
358 .id();
359 commands.entity(vehicle_entity).add_child(service_entity);
360 let timetable_entries = create_timetable_entries(
361 commands,
362 &service.timetable,
363 stations,
364 vehicle_entity,
365 Some(service_entity),
366 );
367 commands.entity(vehicle_entity).insert(VehicleSchedule {
368 entities: timetable_entries,
369 ..Default::default()
370 });
371 vehicle_entity
372}
373
374fn create_timetable_entries(
375 commands: &mut Commands,
376 timetable: &[QETRCTimetableEntry],
377 stations: &std::collections::HashMap<String, Entity>,
378 vehicle_entity: Entity,
379 service_entity: Option<Entity>,
380) -> Vec<Entity> {
381 let mut entries = Vec::with_capacity(timetable.len());
382 for (i, entry) in timetable.iter().enumerate() {
383 let Some(&station_entity) = stations.get(&entry.station_name) else {
384 continue;
385 };
386 let timetable_entry = commands
387 .spawn({
388 TimetableEntry {
389 arrival: if entry.stops && entry.arrival == entry.departure {
390 TravelMode::Flexible
391 } else {
392 TravelMode::At(entry.arrival)
393 },
394 departure: if !entry.stops && entry.arrival == entry.departure {
395 None
396 } else {
397 Some(TravelMode::At(entry.departure))
398 },
399 station: station_entity,
400 service: service_entity,
401 track: None,
402 }
403 })
404 .id();
405 entries.push(timetable_entry);
406 commands.entity(vehicle_entity).add_child(timetable_entry);
407 }
408 entries
409}
410
411fn create_line_entities(
412 commands: &mut Commands,
413 line: QETRCLine,
414 stations: &mut std::collections::HashMap<String, Entity>,
415 graph_map: &mut IntervalGraphType,
416) {
417 let mut intervals: DisplayedLineType = Vec::with_capacity(line.stations.len());
418 let Some(first_station) = line.stations.first() else {
419 commands.spawn((
420 DisplayedLine {
421 stations: intervals,
422 ..Default::default()
423 },
424 Name::new(line.name),
425 ));
426 return;
427 };
428 let first_entity = get_or_create_station(commands, stations, first_station);
429 intervals.push((first_entity, 0.0));
430 let mut prev_station = first_station;
431 let mut prev_entity = first_entity;
432 for station in line.stations.iter().skip(1) {
433 let next_entity = get_or_create_station(commands, stations, station);
434 let distance_delta = (station.distance - prev_station.distance).abs();
435 if !graph_map.contains_edge(prev_entity, next_entity) {
436 let interval_entity = commands
437 .spawn(crate::intervals::Interval {
438 length: Distance::from_km(distance_delta),
439 speed_limit: None,
440 })
441 .id();
442 graph_map.add_edge(prev_entity, next_entity, interval_entity);
443 }
444 if !graph_map.contains_edge(next_entity, prev_entity) {
445 let interval_entity = commands
446 .spawn(crate::intervals::Interval {
447 length: Distance::from_km(distance_delta),
448 speed_limit: None,
449 })
450 .id();
451 graph_map.add_edge(next_entity, prev_entity, interval_entity);
452 }
453 intervals.push((next_entity, distance_delta));
454 prev_station = station;
455 prev_entity = next_entity;
456 }
457 commands.spawn((
458 DisplayedLine {
459 stations: intervals,
460 ..Default::default()
461 },
462 Name::new(line.name),
463 ));
464}
465
466fn get_or_create_station(
467 commands: &mut Commands,
468 stations: &mut std::collections::HashMap<String, Entity>,
469 station: &QETRCStation,
470) -> Entity {
471 if let Some(&entity) = stations.get(&station.name) {
472 entity
473 } else {
474 let entity = commands
475 .spawn((Station::default(), Name::new(station.name.clone())))
476 .id();
477 stations.insert(station.name.clone(), entity);
478 entity
479 }
480}
481
482fn ensure_stations_exist(
483 commands: &mut Commands,
484 services: &[QETRCService],
485 vehicles: &[QETRCVehicle],
486 stations: &mut std::collections::HashMap<String, Entity>,
487) {
488 let mut create_station_if_needed = |station_name: &str| {
489 if stations.contains_key(station_name) {
490 return;
491 }
492 let station = commands
493 .spawn((Station::default(), Name::new(station_name.to_string())))
494 .id();
495 stations.insert(station_name.to_string(), station);
496 };
497
498 for vehicle in vehicles.iter() {
499 for service in vehicle.services.iter() {
500 for entry in service.timetable.iter() {
501 create_station_if_needed(&entry.station_name);
502 }
503 }
504 }
505 for service in services.iter() {
506 for entry in service.timetable.iter() {
507 create_station_if_needed(&entry.station_name);
508 }
509 }
510}