1use crate::{
2 graph::{Graph, Interval},
3 lines::DisplayedLine,
4 rw_data::ModifyData,
5 units::{distance::Distance, time::TimetableTime},
6 vehicles::{
7 Vehicle,
8 entries::{TravelMode, VehicleSchedule},
9 vehicle_set::VehicleSet,
10 },
11};
12use bevy::prelude::*;
13use egui_i18n::tr;
14use moonshine_core::kind::*;
15use pest::Parser;
16use pest_derive::Parser;
17
18#[derive(Parser)]
19#[grammar = "rw_data/oudiasecond.pest"]
20pub struct OUD2Parser;
21
22#[derive(Debug)]
23enum Structure<'a> {
24 Struct(&'a str, Vec<Structure<'a>>),
25 Pair(&'a str, Value<'a>),
26}
27
28#[derive(Debug)]
29enum Value<'a> {
30 Single(&'a str),
31 List(Vec<&'a str>),
32}
33
34fn parse_oud2_to_ast(file: &str) -> Result<Structure<'_>, pest::error::Error<Rule>> {
35 let oud2 = OUD2Parser::parse(Rule::file, file)?
36 .next()
37 .unwrap()
38 .into_inner()
39 .next()
40 .unwrap();
41 use pest::iterators::Pair;
42 fn parse_struct(pair: Pair<Rule>) -> Structure {
43 match pair.as_rule() {
44 Rule::r#struct => {
45 let mut inner = pair.into_inner();
46 let name = inner.next().unwrap().as_str();
47 let mut fields = Vec::new();
48 for field_pair in inner {
49 let field_struct = parse_struct(field_pair);
50 fields.push(field_struct);
51 }
52 Structure::Struct(name, fields)
53 }
54 Rule::wrapper => {
55 let inner = pair.into_inner();
56 let name = "file";
57 let mut fields = Vec::new();
58 for field_pair in inner {
59 let field_struct = parse_struct(field_pair);
60 fields.push(field_struct);
61 }
62 Structure::Struct(name, fields)
63 }
64 Rule::kvpair => {
65 let mut inner = pair.into_inner();
66 let key = inner.next().unwrap().as_str();
67 let val = inner.next().unwrap();
68 let val = match val.as_rule() {
69 Rule::value => Value::Single(val.as_str()),
70 Rule::list => {
71 let list_vals = val.into_inner().map(|v| v.as_str()).collect();
72 Value::List(list_vals)
73 }
74 _ => unreachable!(),
75 };
76 Structure::Pair(key, val)
77 }
78 _ => unreachable!(),
79 }
80 }
81 Ok(parse_struct(oud2))
82}
83
84pub fn load_oud2(
85 mut commands: Commands,
86 mut reader: MessageReader<ModifyData>,
87 mut graph: ResMut<Graph>,
88) {
89 let mut str: Option<&str> = None;
90 for modification in reader.read() {
91 match modification {
92 ModifyData::LoadOuDiaSecond(s) => str = Some(s.as_str()),
93 _ => {}
94 }
95 }
96 let Some(str) = str else {
97 return;
98 };
99 graph.clear();
100 info!("Loading OUD2 data...");
101 let ast = parse_oud2_to_ast(str).expect("Failed to parse OUD2 file");
102 let root = parse_ast(&ast).expect("Failed to convert OUD2 AST to internal representation");
103 for line in root.lines {
104 let mut stations: Vec<(Instance<crate::graph::Station>, bool)> = Vec::new();
105 for station in line.stations.iter() {
106 let station_entity = commands
107 .spawn(Name::new(station.name.clone()))
108 .insert_instance(crate::graph::Station::default())
109 .into();
110 stations.push((station_entity, station.break_interval));
111 }
112 for (i, station) in line.stations.iter().enumerate() {
113 if let Some(branch_index) = station.branch_index {
114 let (e, _) = stations[i];
115 commands.entity(e.entity()).despawn();
116 stations[i].0 = stations[branch_index].0;
117 }
118 if let Some(loop_index) = station.loop_index {
119 let (e, _) = stations[i];
120 commands.entity(e.entity()).despawn();
121 stations[i].0 = stations[loop_index].0;
122 }
123 }
124 commands.spawn((
125 Name::new(tr!("oud2-default-line")),
126 DisplayedLine::new(stations.iter().map(|(e, _)| (*e, 0.0)).collect()),
127 ));
128 for w in stations.windows(2) {
129 let [(prev, break_interval), (next, _)] = w else {
130 unreachable!()
131 };
132 if *break_interval {
134 continue;
135 }
136 let e1 = commands
137 .spawn_instance(Interval {
138 length: Distance::from_m(1000),
139 speed_limit: None,
140 })
141 .into();
142 let e2 = commands
143 .spawn_instance(Interval {
144 length: Distance::from_m(1000),
145 speed_limit: None,
146 })
147 .into();
148 graph.add_edge(*prev, *next, e1);
149 graph.add_edge(*next, *prev, e2);
150 }
151 for diagram in line.diagrams {
152 let vehicle_set_entity = commands.spawn((Name::new(diagram.name), VehicleSet)).id();
153 for train in diagram.trains {
154 let vehicle_entity = commands.spawn((Vehicle, Name::new(train.name))).id();
155 let mut schedule = Vec::new();
156 commands
157 .entity(vehicle_set_entity)
158 .add_child(vehicle_entity);
159 for (i, time) in train.times.into_iter().enumerate() {
160 let Some(time) = time else {
161 continue;
162 };
163 if matches!(time.passing_mode, PassingMode::NoOperation) {
164 continue;
165 }
166 let arrival = if matches!(time.passing_mode, PassingMode::Pass) {
167 TravelMode::Flexible
168 } else {
169 time.arrival
170 .map_or(TravelMode::Flexible, |t| TravelMode::At(t))
171 };
172 let departure = if matches!(time.passing_mode, PassingMode::Pass) {
173 None
174 } else {
175 Some(
176 time.departure
177 .map_or(TravelMode::Flexible, |t| TravelMode::At(t)),
178 )
179 };
180 let entry_entity = commands
181 .spawn(crate::vehicles::entries::TimetableEntry {
182 arrival,
183 departure,
184 station: stations[match train.direction {
185 Direction::Down => i,
186 Direction::Up => stations.len() - 1 - i,
187 }]
188 .0
189 .entity(),
190 service: None,
191 track: None,
192 })
193 .id();
194 schedule.push(entry_entity);
195 commands.entity(vehicle_entity).add_child(entry_entity);
196 }
197 commands.entity(vehicle_entity).insert(VehicleSchedule {
198 entities: schedule,
199 ..Default::default()
200 });
201 }
202 }
203 }
204}
205
206#[derive(Debug)]
207struct Root {
208 version: String,
209 lines: Vec<LineMeta>,
210}
211
212#[derive(Debug)]
213struct LineMeta {
214 name: String,
215 stations: Vec<Station>,
216 diagrams: Vec<Diagram>,
217}
218
219#[derive(Debug)]
220struct Station {
221 name: String,
222 branch_index: Option<usize>,
223 loop_index: Option<usize>,
224 break_interval: bool,
225}
226
227#[derive(Debug)]
228struct Diagram {
229 name: String,
230 trains: Vec<Train>,
231 is_timing_foundation: bool,
232}
233
234#[derive(Debug, Clone, Copy)]
235enum Direction {
236 Down,
238 Up,
240}
241
242#[derive(Debug)]
243struct Train {
244 direction: Direction,
245 name: String,
246 times: Vec<Option<TimetableEntry>>,
247}
248
249#[derive(Debug, Clone, Copy)]
250enum PassingMode {
251 Stop,
252 Pass,
253 NoOperation,
254}
255
256#[derive(Debug, Clone, Copy)]
257struct TimetableEntry {
258 passing_mode: PassingMode,
259 arrival: Option<TimetableTime>,
260 departure: Option<TimetableTime>,
261 track: Option<usize>,
262}
263
264use Structure::*;
265use Value::*;
266fn parse_ast(ast: &Structure) -> Result<Root, String> {
267 let Struct(_, v) = ast else {
268 return Err("Expected root structure".to_string());
269 };
270 let mut version = Option::None;
271 let mut lines = Vec::new();
272 let mut unnamed_line_counter = 0;
273 for field in v {
274 match field {
275 Struct(k, v) if *k == "Rosen" => {
276 lines.push(parse_line_meta(v, &mut unnamed_line_counter)?);
277 }
278 Pair(k, Single(v)) if *k == "FileType" => {
279 version = Some(v.to_string());
280 }
281 _ => {}
282 }
283 }
284 Ok(Root {
285 version: version.ok_or("File does not have a version")?,
286 lines,
287 })
288}
289fn parse_line_meta(
290 fields: &[Structure],
291 unnamed_line_counter: &mut usize,
292) -> Result<LineMeta, String> {
293 let mut name: Option<String> = None;
294 let mut stations = Vec::new();
295 let mut diagrams = Vec::new();
296 let mut unnamed_station_counter = 0;
297 let mut unnamed_diagram_counter = 0;
298 let mut unnamed_train_counter = 0;
299 for field in fields {
300 match field {
301 Pair(k, Single(v)) if *k == "Rosenmei" => {
302 name = Some(v.to_string());
303 }
304 Struct(k, v) if *k == "Eki" => {
305 stations.push(parse_station(v, &mut unnamed_station_counter)?);
306 }
307 Struct(k, v) if *k == "Dia" => {
308 diagrams.push(parse_diagram(
309 v,
310 &mut unnamed_diagram_counter,
311 &mut unnamed_train_counter,
312 )?);
313 }
314 _ => {}
315 }
316 }
317 Ok(LineMeta {
318 name: name.unwrap_or_else(|| {
319 *unnamed_line_counter += 1;
320 let name = tr!("oud2-unnamed-line", {
321 number: unnamed_line_counter.to_string()
322 });
323 name
324 }),
325 stations,
326 diagrams,
327 })
328}
329
330fn parse_station(
331 fields: &[Structure],
332 unnamed_station_counter: &mut usize,
333) -> Result<Station, String> {
334 let mut name: Option<String> = None;
335 let mut branch_index: Option<usize> = None;
336 let mut loop_index: Option<usize> = None;
337 let mut kudari_display = false;
338 let mut nobori_display = false;
339 for field in fields {
340 match field {
341 Pair(k, Single(v)) if *k == "Ekimei" => {
342 name = Some(v.to_string());
343 }
344 Pair(k, Single(v)) if *k == "BrunchCoreEkiIndex" => {
346 branch_index = Some(
347 v.parse::<usize>()
348 .map_err(|e| format!("Failed to parse branch index: {}", e))?,
349 );
350 }
351 Pair(k, Single(v)) if *k == "LoopOriginEkiIndex" => {
352 loop_index = Some(
353 v.parse::<usize>()
354 .map_err(|e| format!("Failed to parse loop index: {}", e))?,
355 );
356 }
357 Pair(k, List(v)) if *k == "JikokuhyouJikokuDisplayKudari" => {
358 kudari_display = v.len() == 2 && [v[0], v[1]] == ["1", "0"];
359 }
360 Pair(k, List(v)) if *k == "JikokuhyouJikokuDisplayNobori" => {
361 nobori_display = v.len() == 2 && [v[0], v[1]] == ["0", "1"];
362 }
363 _ => {}
364 }
365 }
366 let break_interval = kudari_display && nobori_display;
367 Ok(Station {
368 name: name.unwrap_or_else(|| {
369 *unnamed_station_counter += 1;
370 let name = tr!("oud2-unnamed-station", {
371 number: unnamed_station_counter.to_string()
372 });
373 name
374 }),
375 branch_index,
376 loop_index,
377 break_interval,
378 })
379}
380
381fn parse_diagram(
382 fields: &[Structure],
383 unnamed_diagram_counter: &mut usize,
384 unnamed_train_counter: &mut usize,
385) -> Result<Diagram, String> {
386 let mut name: Option<String> = None;
387 let mut trains: Vec<Train> = Vec::new();
388 let mut is_timing_foundation = false;
389 for field in fields {
390 match field {
391 Pair(k, Single(v)) if *k == "DiaName" => {
392 is_timing_foundation = *v == "基準運転時分";
394 name = Some(v.to_string());
395 }
396 Struct(k, v) if *k == "Kudari" => {
397 trains.extend(parse_trains(Direction::Down, v, unnamed_train_counter)?);
398 }
399 Struct(k, v) if *k == "Nobori" => {
400 trains.extend(parse_trains(Direction::Up, v, unnamed_train_counter)?);
401 }
402 _ => {}
403 }
404 }
405 Ok(Diagram {
406 name: name.unwrap_or_else(|| {
407 *unnamed_diagram_counter += 1;
408 let name = tr!("oud2-unnamed-diagram", {
409 number: unnamed_diagram_counter.to_string()
410 });
411 name
412 }),
413 trains,
414 is_timing_foundation,
415 })
416}
417
418fn parse_trains(
419 direction: Direction,
420 fields: &[Structure],
421 unnamed_train_counter: &mut usize,
422) -> Result<Vec<Train>, String> {
423 fn parse_time(str: &str) -> Result<Option<TimetableEntry>, String> {
424 let mut entry = TimetableEntry {
425 passing_mode: PassingMode::NoOperation,
426 arrival: None,
427 departure: None,
428 track: None,
429 };
430 if str.is_empty() {
431 return Ok(None);
432 }
433 let parts = OUD2Parser::parse(Rule::timetable_entry, str)
434 .map_err(|e| e.to_string())?
435 .next()
436 .ok_or("Unexpected error while unwrapping")?;
437 for field in parts.into_inner() {
438 match field.as_rule() {
439 Rule::service_mode => match field.as_str() {
440 "1" => entry.passing_mode = PassingMode::Stop,
441 "2" => entry.passing_mode = PassingMode::Pass,
442 _ => entry.passing_mode = PassingMode::NoOperation,
443 },
444 Rule::arrival => entry.arrival = TimetableTime::from_oud2_str(field.as_str()),
445 Rule::departure => entry.departure = TimetableTime::from_oud2_str(field.as_str()),
446 Rule::track => {
447 }
449 _ => {}
450 }
451 }
452 Ok(Some(entry))
453 }
454 let mut parse_trains = |fields: &[Structure]| -> Result<Train, String> {
455 let mut name: Option<String> = None;
456 let mut entries: Vec<Option<TimetableEntry>> = Vec::new();
457 for field in fields {
458 match field {
459 Pair(k, Single(v)) if *k == "Ressyabangou" && !v.trim().is_empty() => {
460 name = Some(v.to_string());
461 }
462 Pair(k, v) if *k == "EkiJikoku" => {
463 let times = match v {
464 Single(s) => &vec![*s],
465 List(l) => l,
466 };
467 for time in times {
468 entries.push(parse_time(time)?);
469 }
470 }
471 _ => {}
472 }
473 }
474 let time_iter = entries.iter_mut().flat_map(|t| {
475 std::iter::once(t).flatten().flat_map(|t| {
476 std::iter::once(&mut t.arrival)
477 .flatten()
478 .chain(std::iter::once(&mut t.departure).flatten())
479 })
480 });
481 super::normalize_times(time_iter);
482 Ok(Train {
483 direction,
484 name: name.unwrap_or_else(|| {
485 *unnamed_train_counter += 1;
486 let name = tr!("oud2-unnamed-train", {
487 number: unnamed_train_counter.to_string()
488 });
489 name
490 }),
491 times: entries,
492 })
493 };
494 let mut trains = Vec::new();
495 for field in fields {
496 match field {
497 Struct(k, v) if *k == "Ressya" => {
498 trains.push(parse_trains(v)?);
499 }
500 _ => {}
501 }
502 }
503 Ok(trains)
504}