paiagram/rw_data/
oudiasecond.rs

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            // create new intervals
133            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    // kudari
237    Down,
238    // nobori
239    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            // The "brunch" here is intended - it is spelling mistake in the original software
345            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                // hard coded eh
393                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                    // TODO
448                }
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}