1use std::{any::Any, collections::BTreeMap, iter::zip};
2
3use kdl::{KdlDocument, KdlEntry, KdlNode, KdlValue};
4use pest::Parser;
5use pest_derive::Parser;
6
7#[derive(Parser)]
8#[grammar = "rw_data/oudiasecond.pest"]
9pub struct OuDiaSecondParser;
10
11#[derive(Debug)]
12enum OuDiaSecondStruct<'a> {
13 Struct(&'a str, Vec<OuDiaSecondStruct<'a>>),
14 Pair(&'a str, OuDiaSecondValue<'a>),
15}
16
17#[derive(Debug)]
18enum OuDiaSecondValue<'a> {
19 Single(&'a str),
20 List(Vec<&'a str>),
21}
22
23use pest::error::Error;
24
25use crate::{
26 basic::TimetableTime,
27 vehicle_set::VehicleSet,
28 vehicles::{ArrivalType, DepartureType, TimetableEntry},
29};
30
31fn parse_oud2_to_ast(file: &str) -> Result<OuDiaSecondStruct<'_>, Error<Rule>> {
32 let oud2 = OuDiaSecondParser::parse(Rule::file, file)?
33 .next()
34 .unwrap()
35 .into_inner()
36 .next()
37 .unwrap();
38 use pest::iterators::Pair;
39 fn parse_struct(pair: Pair<Rule>) -> OuDiaSecondStruct {
40 match pair.as_rule() {
41 Rule::r#struct => {
42 let mut inner = pair.into_inner();
43 let name = inner.next().unwrap().as_str();
44 let mut fields = Vec::new();
45 for field_pair in inner {
46 let field_struct = parse_struct(field_pair);
47 fields.push(field_struct);
48 }
49 OuDiaSecondStruct::Struct(name, fields)
50 }
51 Rule::wrapper => {
52 let inner = pair.into_inner();
53 let name = "file";
54 let mut fields = Vec::new();
55 for field_pair in inner {
56 let field_struct = parse_struct(field_pair);
57 fields.push(field_struct);
58 }
59 OuDiaSecondStruct::Struct(name, fields)
60 }
61 Rule::kvpair => {
62 let mut inner = pair.into_inner();
63 let key = inner.next().unwrap().as_str();
64 let val = inner.next().unwrap();
65 let val = match val.as_rule() {
66 Rule::value => OuDiaSecondValue::Single(val.as_str()),
67 Rule::list => {
68 let list_vals = val.into_inner().map(|v| v.as_str()).collect();
69 OuDiaSecondValue::List(list_vals)
70 }
71 _ => unreachable!(),
72 };
73 OuDiaSecondStruct::Pair(key, val)
74 }
75 _ => unreachable!(),
76 }
77 }
78 Ok(parse_struct(oud2))
79}
80
81struct OUD2Root {
82 file_type: String,
83 line: OUD2Line,
84}
85
86struct OUD2Line {
87 name: String,
88 stations: Vec<OUD2Station>,
90 diagrams: Vec<OUD2Diagram>,
92}
93
94struct OUD2Station {
95 name: String,
96 branch_index: Option<usize>, }
98
99struct OUD2Diagram {
100 name: String,
101 services: Vec<OUD2Service>,
102 }
104
105struct OUD2Service {
106 reverse: bool,
107 name: String,
108 timetable: Vec<OUD2TimetableEntry>,
109}
110
111#[derive(Debug, Clone, Copy)]
112enum OUD2OperationType {
113 After,
114 Before,
115}
116
117enum OUD2ServiceMode {
119 NoPassing,
121 Stop,
123 NonStop,
125}
126
127struct OUD2TimetableEntry {
128 service_mode: OUD2ServiceMode,
129 arrival: ArrivalType,
130 departure: DepartureType,
131 track: Option<nonmax::NonMaxUsize>,
132 operations: Option<
133 Vec<(
135 Vec<(Option<nonmax::NonMaxUsize>, OUD2OperationType)>,
136 Vec<Vec<String>>,
137 )>,
138 >,
139}
140
141fn parse_oud2(file: &str) -> Result<OUD2Root, String> {
142 let ast = parse_oud2_to_ast(file).map_err(|e| e.to_string())?;
143 parse_root(ast)
144}
145
146fn parse_root(input: OuDiaSecondStruct) -> Result<OUD2Root, String> {
147 let input = match input {
148 OuDiaSecondStruct::Struct(_, vals) => vals,
149 _ => {
150 return Err("Invalid OuDiaSecond root structure".to_string());
151 }
152 };
153 let mut file_type: Option<String> = None;
154 let mut line: Option<Vec<OuDiaSecondStruct>> = None;
155 use OuDiaSecondStruct::*;
156 for entry in input {
157 match entry {
158 Pair("FileType", OuDiaSecondValue::Single(ft)) => {
159 if file_type.is_none() {
160 file_type = Some(ft.to_string());
161 }
162 }
163 Struct("Rosen", val) => {
164 if line.is_none() {
165 line = Some(val);
166 }
167 }
168 _ => {}
169 }
170 if file_type.is_some() && line.is_some() {
171 return Ok(OUD2Root {
172 file_type: file_type.unwrap(),
173 line: parse_line(line.unwrap())?,
174 });
175 }
176 }
177 Err("Failed to parse OuDiaSecond file: missing `FileType` or `Rosen` field(s)".to_string())
178}
179
180fn parse_line(input: Vec<OuDiaSecondStruct>) -> Result<OUD2Line, String> {
181 let mut name: Option<String> = None;
182 let mut stations: Vec<OUD2Station> = Vec::new();
183 let mut diagram: Vec<Vec<OuDiaSecondStruct>> = Vec::new();
184 use OuDiaSecondStruct::*;
185 for entry in input {
186 match entry {
187 Pair("Rosenmei", OuDiaSecondValue::Single(n)) => {
188 if name.is_none() {
189 name = Some(n.to_string());
190 }
191 }
192 Struct("Eki", vals) => {
193 let mut station_name = None;
194 let mut branch_index = None;
195 for station_entry in vals {
196 match station_entry {
197 Pair("Ekimei", OuDiaSecondValue::Single(n)) => {
198 station_name = Some(n.to_string());
199 }
200 Pair("BrunchCoreEkiIndex", OuDiaSecondValue::Single(idx)) => {
201 branch_index = idx.parse::<usize>().ok();
202 }
203 _ => {}
204 }
205 if station_name.is_some() && branch_index.is_some() {
206 break;
207 }
208 }
209 stations.push(OUD2Station {
210 name: station_name.unwrap_or_default(),
211 branch_index,
212 });
213 }
214 Struct("Dia", vals) => {
215 info!("Found diagram with {} entries", vals.len());
216 diagram.push(vals);
217 }
218 _ => {}
219 }
220 }
221 if name.is_some() {
222 return Ok(OUD2Line {
223 name: name.unwrap(),
224 stations,
225 diagrams: diagram
226 .into_iter()
227 .map(parse_diagram)
228 .collect::<Result<Vec<_>, _>>()?,
229 });
230 }
231 Err("Failed to parse OuDiaSecond line: incomplete information".to_string())
232}
233
234fn parse_diagram(input: Vec<OuDiaSecondStruct>) -> Result<OUD2Diagram, String> {
235 let mut name: Option<String> = None;
236 let mut services: Vec<OUD2Service> = Vec::new();
237 use OuDiaSecondStruct::*;
238 for entry in input {
239 match entry {
240 Pair("DiaName", OuDiaSecondValue::Single(n)) => {
241 if name.is_none() {
242 name = Some(n.to_string());
243 }
244 }
245 Struct("Kudari", vals) => {
246 services.extend(parse_services(vals, false)?);
247 }
248 Struct("Nobori", vals) => {
249 services.extend(parse_services(vals, true)?);
250 }
251 _ => {}
252 }
253 }
254 if name.is_some() {
255 return Ok(OUD2Diagram {
256 name: name.unwrap(),
257 services,
258 });
259 }
260 Err("Failed to parse OuDiaSecond diagram: missing name".to_string())
261}
262
263fn parse_services(
264 input: Vec<OuDiaSecondStruct>,
265 reverse: bool,
266) -> Result<Vec<OUD2Service>, String> {
267 use OuDiaSecondStruct::*;
268 let mut services = Vec::new();
269 for entry in input {
270 let Struct("Ressya", vals) = entry else {
271 continue;
272 };
273 let service = parse_service(vals, reverse)?;
274 let Some(service) = service else {
275 continue;
276 };
277 services.push(service);
278 }
279 Ok(services)
280}
281
282fn parse_service(
283 input: Vec<OuDiaSecondStruct>,
284 reverse: bool,
285) -> Result<Option<OUD2Service>, String> {
286 let mut name: Option<String> = None;
287 let mut timetable: Option<Vec<OUD2TimetableEntry>> = None;
288 let mut operations: Vec<(
289 Vec<(nonmax::NonMaxUsize, OUD2OperationType)>,
290 Vec<Vec<String>>,
291 )> = Vec::new();
292 use OuDiaSecondStruct::*;
293 for entry in input {
294 match entry {
295 Pair("Ressyabangou", OuDiaSecondValue::Single(n)) => {
296 if name.is_none() {
297 name = Some(n.to_string());
298 }
299 }
300 Pair("EkiJikoku", v) => {
301 let times = match v {
302 OuDiaSecondValue::Single(s) => vec![s],
303 OuDiaSecondValue::List(l) => l,
304 };
305 if timetable.is_none() {
306 timetable = Some(
307 times
308 .iter()
309 .map(|t| parse_timetable_entry(t))
310 .collect::<Result<_, _>>()?,
311 );
312 }
313 }
314 Pair(n, v) if n.starts_with("Operation") => {
315 let tree = n
317 .strip_prefix("Operation")
318 .unwrap()
319 .split(".")
320 .map(|s| {
321 let (num, operation_type) = s.split_at(s.len() - 1);
323 let operation_type = match operation_type {
324 "A" => OUD2OperationType::After,
325 "B" => OUD2OperationType::Before,
326 _ => unreachable!(),
327 };
328 (num.parse::<nonmax::NonMaxUsize>().unwrap(), operation_type)
329 })
330 .collect::<Vec<_>>();
331 let ops = match v {
332 OuDiaSecondValue::Single(s) => vec![s],
333 OuDiaSecondValue::List(l) => l,
334 };
335 let ops = ops
336 .iter()
337 .map(|s| {
338 OuDiaSecondParser::parse(Rule::event, s)
339 .unwrap()
340 .next()
341 .unwrap()
342 .into_inner()
343 .map(|p| p.as_str().to_string())
344 .collect::<Vec<String>>()
345 })
346 .collect::<Vec<_>>();
347 operations.push((tree, ops));
348 }
349 _ => {}
350 }
351 }
352 if timetable.is_some() {
353 return Ok(Some(OUD2Service {
354 reverse,
355 name: name.unwrap_or("<unnamed>".to_string()),
356 timetable: {
357 let mut timetable = timetable.unwrap();
359 for (tree, ops) in operations {
360 let mut tree = tree.iter();
362 let (index, op_type) = tree.next().unwrap();
363 let idx = nonmax::NonMaxUsize::get(index);
364 match tree.next() {
365 None => {
366 if let Some(entry) = timetable.get_mut(idx) {
368 entry
369 .operations
370 .get_or_insert_default()
371 .push((vec![(None, *op_type)], ops));
372 }
373 }
374 Some(entry) => {
375 let mut remainder = vec![entry];
376 remainder.extend(tree);
377 if let Some(entry) = timetable.get_mut(idx) {
378 entry.operations.get_or_insert_default().push((
379 {
380 let mut returned_remainder = vec![(None, *op_type)];
381 returned_remainder.extend(
382 remainder.iter().map(|(idx, op)| (Some(*idx), *op)),
383 );
384 returned_remainder
385 },
386 ops,
387 ));
388 }
389 }
390 }
391 }
392 timetable
393 },
394 }));
395 }
396 Ok(None)
397}
398
399#[inline(always)]
400fn parse_timetable_entry(input: &str) -> Result<OUD2TimetableEntry, String> {
401 let mut service_mode = OUD2ServiceMode::NoPassing;
402 let mut arrival = ArrivalType::Flexible;
403 let mut departure = DepartureType::NonStop;
404 let mut track = None;
405 if input.is_empty() {
406 return Ok(OUD2TimetableEntry {
407 service_mode,
408 arrival,
409 departure,
410 operations: None,
411 track,
412 });
413 }
414 let parsed = OuDiaSecondParser::parse(Rule::timetable_entry, input)
415 .unwrap()
416 .next()
417 .unwrap()
418 .into_inner();
419 for pair in parsed {
420 match pair.as_rule() {
421 Rule::service_mode => match pair.as_str() {
422 "0" => service_mode = OUD2ServiceMode::NoPassing,
423 "1" => service_mode = OUD2ServiceMode::Stop,
424 "2" => service_mode = OUD2ServiceMode::NonStop,
425 _ => return Err(format!("Unknown service mode: {}", pair.as_str())),
426 },
427 Rule::arrival => {
428 arrival = match TimetableTime::from_oud2_str(pair.as_str()) {
429 Some(t) => ArrivalType::At(t),
430 None => return Err(format!("Failed to parse arrival time: {}", pair.as_str())),
431 };
432 }
433 Rule::departure => {
434 departure = match TimetableTime::from_oud2_str(pair.as_str()) {
435 Some(t) => DepartureType::At(t),
436 None => {
437 return Err(format!("Failed to parse departure time: {}", pair.as_str()));
438 }
439 };
440 }
441 Rule::track => {
442 track = pair.as_str().parse::<nonmax::NonMaxUsize>().ok();
443 }
444 _ => unreachable!(),
445 }
446 }
447 match (&arrival, &departure) {
449 (ArrivalType::Flexible, DepartureType::At(time)) => {
450 arrival = ArrivalType::At(*time);
451 departure = DepartureType::Flexible;
452 }
453 _ => {}
454 }
455 Ok(OUD2TimetableEntry {
456 service_mode,
457 arrival,
458 departure,
459 operations: None,
460 track,
461 })
462}
463
464use super::ModifyData;
465use crate::basic::*;
466use crate::intervals::*;
467use crate::vehicles::*;
468use bevy::prelude::{Commands, Entity, MessageReader, Name, Res, info};
469use petgraph::graphmap::GraphMap;
470
471#[rustfmt::skip]
476type StationVehicleSchedulePool =
477 Vec< Vec< BTreeMap< TimetableTime, Vec< Entity>>>> ;
483
484pub fn load_oud2(
485 mut commands: Commands,
486 mut reader: MessageReader<ModifyData>,
487 intervals_resource: Res<IntervalsResource>,
488) {
489 let mut data: Option<&str> = None;
490 for modification in reader.read() {
491 let ModifyData::LoadOuDiaSecond(d) = modification else {
492 continue;
493 };
494 data = Some(d);
495 }
496 let Some(data) = data else {
497 return;
498 };
499 let now = instant::Instant::now();
500 let oud2_data = parse_oud2(data).unwrap();
501 info!("Parsed OuDiaSecond data in {:?}", now.elapsed());
502 #[cfg(not(target_arch = "wasm32"))]
504 {
505 let kdl_string = make_kdl(&parse_oud2_to_ast(data).unwrap());
507 std::fs::write("parsed.kdl", kdl_string).unwrap();
508 }
509 let now = instant::Instant::now();
510 let (graph_map, stations) = make_graph_map(&mut commands, &oud2_data.line.stations);
511 commands.insert_resource(Graph(graph_map));
512 for diagram in oud2_data.line.diagrams {
513 make_vehicle_set(
514 &mut commands,
515 diagram,
516 &stations,
517 intervals_resource.default_depot,
518 );
519 }
520 info!("Loaded OUD2 data in {:?}", now.elapsed());
521}
522
523fn make_vehicle_set(
524 commands: &mut Commands,
525 diagram: OUD2Diagram,
526 stations: &Vec<Entity>,
527 depot: Entity,
528) -> Entity {
529 let vehicle_set_entity = commands.spawn((VehicleSet, Name::new(diagram.name))).id();
530 let mut station_vehicle_schedule_pool: StationVehicleSchedulePool =
532 vec![Vec::new(); stations.len()];
533 let mut service_entities: Vec<Entity> = Vec::new();
534 for service in diagram.services.iter() {
535 let service_entity = commands
536 .spawn((Name::new(service.name.clone()), Service { class: None }))
537 .id();
538 service_entities.push(service_entity);
539 let mut service_schedule: Vec<Entity> = Vec::new();
540 let mut first_stop_info: Option<(usize, usize, TimetableTime)> = None;
541 for (entry_index, timetable_entry) in service.timetable.iter().enumerate() {
542 if matches!(timetable_entry.service_mode, OUD2ServiceMode::NoPassing) {
543 continue;
544 }
545 let station_index = if service.reverse {
546 stations.len() - 1 - entry_index
547 } else {
548 entry_index
549 };
550 let track_index = timetable_entry
551 .track
552 .and_then(|v| Some(v.get()))
553 .unwrap_or(0);
554 if first_stop_info.is_none() {
555 first_stop_info = Some((
556 station_index,
557 track_index,
558 timetable_entry.arrival.time().unwrap(),
559 ))
560 }
561 let entry_entity = commands
562 .spawn(TimetableEntry {
563 arrival: timetable_entry.arrival,
564 departure: timetable_entry.departure,
565 service: Some(service_entity),
566 track: None,
567 station: stations[station_index],
568 })
569 .id();
570 service_schedule.push(entry_entity)
571 }
572 let Some((station_index, track_index, first_stop_arrival_time)) = first_stop_info else {
573 continue;
574 };
575 let station_pool = station_vehicle_schedule_pool
576 .get_mut(station_index)
577 .unwrap();
578 while station_pool.len() <= track_index {
579 station_pool.push(BTreeMap::new())
580 }
581 station_pool[track_index].insert(first_stop_arrival_time, service_schedule);
582 }
583 info!(?station_vehicle_schedule_pool);
584 for (service, service_entity) in zip(diagram.services, service_entities) {
585 }
587 vehicle_set_entity
588}
589
590fn make_graph_map(
592 commands: &mut Commands,
593 oud2_stations: &Vec<OUD2Station>,
594) -> (IntervalGraphType, Vec<Entity>) {
595 let mut stations: Vec<Entity> = Vec::with_capacity(oud2_stations.len());
596 let mut prev_entity = None;
597 let mut graph_map: IntervalGraphType = GraphMap::new();
598 for (_ci, curr_station) in oud2_stations.iter().enumerate() {
599 let branch_index = curr_station.branch_index;
600 let station_entity;
601 station_entity = commands
602 .spawn((Name::new(curr_station.name.clone()), Station))
603 .id();
604 if let Some(prev) = prev_entity
605 && branch_index.is_none()
606 {
607 let interval_entity = commands
608 .spawn(Interval {
609 length: TrackDistance::from_km(1),
611 speed_limit: None,
612 })
613 .id();
614 graph_map.add_edge(prev, station_entity, interval_entity);
615 }
616 stations.push(station_entity);
617 prev_entity = Some(station_entity);
618 }
619 (graph_map, stations)
620}
621
622fn make_kdl(oud2_root: &OuDiaSecondStruct) -> String {
624 fn to_kdl_value(raw: &str) -> KdlValue {
625 KdlValue::String(raw.trim().to_string())
626 }
627
628 fn to_kdl_node(node: &OuDiaSecondStruct) -> KdlNode {
629 match node {
630 OuDiaSecondStruct::Struct(name, fields) => {
631 let mut kdl_node = KdlNode::new(*name);
632 if !fields.is_empty() {
633 let mut children = KdlDocument::new();
634 for field in fields {
635 children.nodes_mut().push(to_kdl_node(field));
636 }
637 kdl_node.set_children(children);
638 }
639 kdl_node
640 }
641 OuDiaSecondStruct::Pair(key, value) => {
642 let mut kdl_node = KdlNode::new(*key);
643 match value {
644 OuDiaSecondValue::Single(val) => {
645 kdl_node.push(KdlEntry::new(to_kdl_value(val)));
646 }
647 OuDiaSecondValue::List(vals) => {
648 for val in vals {
649 kdl_node.push(KdlEntry::new(to_kdl_value(val)));
650 }
651 }
652 }
653 kdl_node
654 }
655 }
656 }
657
658 let mut document = KdlDocument::new();
659 document.nodes_mut().push(to_kdl_node(oud2_root));
660 document.autoformat();
661 document.to_string()
662}