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