Skip to main content

lotus_shared/
pis.rs

1use serde::{Deserialize, Serialize};
2
3use crate::content::ContentId;
4
5#[doc(hidden)]
6#[derive(Clone, Debug)]
7pub struct PisGroup {
8    pub name: String,                       // NAME
9    pub stations: Vec<PisStation>,          // STNS
10    pub special_chars: Vec<PisSpecialChar>, // SPEC
11    pub routes: Vec<PisRoute>,              // ROUT
12    pub server_name: Option<String>,        // SERV
13}
14
15impl PisGroup {
16    /// Holt den Namen der aktiven PIS-Gruppe.
17    #[cfg(feature = "ffi")]
18    pub fn get_name() -> String {
19        let name =
20            lotus_script_sys::FfiObject::from_packed(unsafe { lotus_script_sys::pis::get_name() });
21        name.deserialize()
22    }
23
24    /// Holt die Station mit der gegebenen Code.
25    #[cfg(feature = "ffi")]
26    pub fn get_station(code: u32) -> Option<PisStation> {
27        let station = lotus_script_sys::FfiObject::from_packed(unsafe {
28            lotus_script_sys::pis::get_station(code)
29        });
30        station.deserialize()
31    }
32
33    /// Holt die gesamte Liste sämtlicher Sonderzeichen
34    #[cfg(feature = "ffi")]
35    pub fn get_special_char_with_line(line: u32, special_char_code: u32) -> String {
36        let route_codes = lotus_script_sys::FfiObject::from_packed(unsafe {
37            lotus_script_sys::pis::get_special_char_with_line(line, special_char_code)
38        });
39        route_codes.deserialize()
40    }
41
42    /// Holt die Route mit der gegebenen Linie und Code.
43    #[cfg(feature = "ffi")]
44    pub fn get_route(line_code: (u32, u32)) -> Option<PisRoute> {
45        let route = lotus_script_sys::FfiObject::from_packed(unsafe {
46            lotus_script_sys::pis::get_route(line_code.0, line_code.1)
47        });
48        route.deserialize()
49    }
50
51    /// Liefert eine Liste sämtlicher Route-Codes, die es für die gegebenen Linie gibt.
52    /// Die Liste ist bereits sortiert und frei von Duplikaten.
53    #[cfg(feature = "ffi")]
54    pub fn get_route_codes_by_line(line: u32) -> Vec<u32> {
55        let route_codes = lotus_script_sys::FfiObject::from_packed(unsafe {
56            lotus_script_sys::pis::get_route_codes_by_line(line)
57        });
58        route_codes.deserialize()
59    }
60
61    /// Holt den Namen des Leitstellen-Servers.
62    #[cfg(feature = "ffi")]
63    pub fn get_server_name() -> Option<String> {
64        let server_name = lotus_script_sys::FfiObject::from_packed(unsafe {
65            lotus_script_sys::pis::get_server_name()
66        });
67        server_name.deserialize()
68    }
69}
70
71/// Datensatz für eine Station im PIS.
72#[derive(Clone, Serialize, Deserialize, Debug)]
73pub struct PisStation {
74    /// Mit der ID wird die Station mit der Map verknüpft (auch dort gibt es eine ID für jede Station).
75    /// Dies ist u.A. für die Fahrgäste und die öffentlichen KI-Fahrzeuge notwendig.
76    pub id: String,
77    /// Der Code ist die Zahl, mit der die Station innerhalb des PIS identifiziert wird.
78    /// Dieser Code wird für die Anzeige auf dem PIS-Display verwendet.
79    pub code: u32,
80    /// Die Strings sind die Texte, die auf den Innenanzeigen angezeigt werden. Zwei Strings gibt es
81    /// z.B. für Wechselanzeigen
82    pub interieur_display: [String; 2],
83    /// Die folgenden Strings sind für die Außenanzeigen.
84    pub terminus_front_option: Option<[String; 2]>,
85    pub terminus_side_option: Option<[String; 2]>,
86    pub terminus_oneline: String,
87}
88
89pub enum PisStationTerminusOneLineTo {
90    FirstLine,
91    SecondLine,
92}
93
94impl PisStation {
95    /// Liefert die (zweizeilige) Front-Außenanzeige für das Ziel.
96    /// Verfügt die Ziel-Station über keine zweizeilige Front-Außenanzeige, so wird die einzeilige
97    /// Außenanzeige um eine Leerzeile erweitert, wobei one_to_two_line die Reihenfolge dieser
98    /// Erweiterung bestimmt:
99    /// - FirstLine: Die einzeilige Zeile wird in die erste Zeile und die Leerzeile in die zweite Zeile eingefügt
100    /// - SecondLine: Die einzeilige Zeile wird in die zweite Zeile und die Leerzeile in die erste Zeile eingefügt
101    pub fn terminus_front(&self, one_to_two_line: PisStationTerminusOneLineTo) -> [String; 2] {
102        if let Some(front) = &self.terminus_front_option {
103            front.clone()
104        } else {
105            match one_to_two_line {
106                PisStationTerminusOneLineTo::FirstLine => {
107                    [self.terminus_oneline.clone(), String::new()]
108                }
109                PisStationTerminusOneLineTo::SecondLine => {
110                    [String::new(), self.terminus_oneline.clone()]
111                }
112            }
113        }
114    }
115
116    pub fn terminus_side(&self, one_to_two_line: PisStationTerminusOneLineTo) -> [String; 2] {
117        if let Some(side) = &self.terminus_side_option {
118            side.clone()
119        } else {
120            self.terminus_front(one_to_two_line)
121        }
122    }
123}
124
125/// Datensatz für ein Sonderzeichen im PIS.
126#[derive(Clone, Serialize, Deserialize, Debug)]
127pub struct PisSpecialChar {
128    /// Der Code ist die Zahl, mit der das Sonderzeichen innerhalb des PIS identifiziert wird.
129    pub code: u32,
130    /// Der Sonderzeichen-String, wobei dieser auch über bestimmte Codes verfügen kann, mit denen
131    /// die originale Liniennummer eingefügt werden kann.
132    ///
133    /// So bedeutet z.B. "M(R2-R1)", dass das auf dem Linienfeld ein M, gefolgt von
134    /// den Ziffern 2 bis 1 von RECHTS aus gezählt, angezeigt wird. Wenn die Liniennummer
135    /// z.B. 123 ist, dann wird hier M23 angezeigt.
136    /// Soll nur M2 angezeigt werden, müsste man "M(R2-R2)" als chars eintragen.
137    pub chars: String, // STRN
138}
139
140/// Datensatz für eine Route im PIS. Jeder Datensatz wird über eine
141/// Liniennummer und einen Code innerhalb der Linie identifiziert.
142#[derive(Clone, Serialize, Deserialize, Debug)]
143pub struct PisRoute {
144    /// Liniennummer und Code innerhalb der Linie zur Zuordnung
145    pub line_code: (u32, u32),
146    /// Liste der Codes der Haltestellen, die auf der Route nacheinander
147    /// angefahren werden, inklusive der Abfahrts- und der Endhaltestelle.
148    pub stop_codes: Vec<u32>,
149    /// Der Codes des Sonderzeichens, welcher automatisch ausgewählt werden soll,
150    /// wenn diese Route eingestellt wird. Ob dieser Code überschrieben werden kann usw.
151    /// ist abhängig vom Bordrechner.
152    pub special_char_code: Option<u32>,
153    /// Sind für das Balisensystem/Anforderungen/Weichen individuelle Anmelde-Codes nötig?
154    pub routing_code: Option<u32>,
155    /// Zusätzliches Textfeld, welches aber aktuell keine bestimmte Bedeutung hat.
156    pub text: Option<String>,
157    /// Im einfachsten Fall ist das Ziel (der Zielcode) einer Route einfach die letzte
158    /// Haltestelle. Es kann aber sein, dass das angezeigte Ziel ab einer bestimmten
159    /// Haltestelle wechseln soll, oder auch die Zielhaltestelle anders angezeigt werden soll.
160    /// Für diese Fälle kann man Termini definieren.
161    pub termini: Vec<PisRouteTerminus>,
162    /// Linie/Code für die automatisch zu wählende folgende Route
163    pub following_line_code: Option<(u32, u32)>,
164}
165
166impl PisRoute {
167    pub fn new(
168        line_code: (u32, u32),
169        stop_codes: Vec<u32>,
170        special_char_code: Option<u32>,
171        routing_code: Option<u32>,
172        text: Option<String>,
173        termini: Vec<PisRouteTerminus>,
174        following_line_code: Option<(u32, u32)>,
175    ) -> Self {
176        Self {
177            line_code,
178            stop_codes,
179            special_char_code,
180            routing_code,
181            text,
182            termini,
183            following_line_code,
184        }
185    }
186    pub fn get_current_direction(&self, stop_index: usize) -> Option<PisRouteTerminus> {
187        self.termini
188            .iter()
189            .find(|terminus| stop_index >= terminus.stop_index)
190            .cloned()
191            .or_else(|| {
192                self.stop_codes.last().map(|code| PisRouteTerminus {
193                    stop_index: 0,
194                    code: Some(*code),
195                    line: Some(self.line_code.0),
196                    special_char_code: self.special_char_code,
197                    routing_code: self.routing_code,
198                })
199            })
200    }
201}
202
203#[derive(Default, Clone, Serialize, Deserialize, Debug)]
204pub struct PisRouteTerminus {
205    /// Index of the stop of the route, from which this terminus applies    
206    pub stop_index: usize,
207    /// New terminus code
208    pub code: Option<u32>,
209    /// New line
210    pub line: Option<u32>,
211    /// New special char code
212    pub special_char_code: Option<u32>,
213    /// New routing code
214    pub routing_code: Option<u32>,
215}
216
217impl PisRouteTerminus {
218    fn routing_code_hash(&self) -> u32 {
219        0
220    }
221
222    pub fn routing_code(&self) -> u32 {
223        self.routing_code.unwrap_or(self.routing_code_hash())
224    }
225}
226
227#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct PisSpGroup {
229    pub name: String,
230    pub basic_pis_group: ContentId,
231    pub class: String,
232    pub add_lines: String,
233    pub add_lines_stations: Vec<PisSpAddLines>,
234    pub routes: Vec<PisSpRoute>,
235}
236
237#[derive(Clone, Debug, PartialEq, Eq)]
238pub struct PisSpAddLines {
239    pub code: i32,
240    pub lines: String,
241}
242
243#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
244pub struct PisSpRoute {
245    pub code: i32,
246    pub lines: String,
247    pub stop_lines: Vec<String>,
248}
249
250impl PisSpGroup {
251    /// Liefert die ContentId der PISS-Gruppe, die zur aktiven PISG passt und die gegebene Klasse hat.
252    #[cfg(feature = "ffi")]
253    pub fn get_content_id(class: &str) -> Option<ContentId> {
254        let class = lotus_script_sys::FfiObject::new(&class);
255        let content_id = lotus_script_sys::FfiObject::from_packed(unsafe {
256            lotus_script_sys::pis::get_sp_content_id(class.packed())
257        });
258        content_id.deserialize()
259    }
260
261    /// Liefert die zusätzlichen Linien aus der gegebenen PISS-Gruppe.
262    #[cfg(feature = "ffi")]
263    pub fn get_group_strings(content_id: ContentId) -> String {
264        let content_id = lotus_script_sys::FfiObject::new(&content_id);
265        let lines = lotus_script_sys::FfiObject::from_packed(unsafe {
266            lotus_script_sys::pis::get_sp_group_strings(content_id.packed())
267        });
268        lines.deserialize()
269    }
270
271    /// Liefert die zusätzlichen Linien für eine Station aus der gegebenen PISS-Gruppe.
272    #[cfg(feature = "ffi")]
273    pub fn get_station_strings(content_id: ContentId, station_code: u32) -> Option<String> {
274        let content_id = lotus_script_sys::FfiObject::new(&content_id);
275        let lines = lotus_script_sys::FfiObject::from_packed(unsafe {
276            lotus_script_sys::pis::get_sp_station_strings(content_id.packed(), station_code)
277        });
278        lines.deserialize()
279    }
280
281    /// Liefert die Route mit dem gegebenen Code aus der gegebenen PISS-Gruppe.
282    #[cfg(feature = "ffi")]
283    pub fn get_route(content_id: ContentId, route_code: u32) -> Option<PisSpRoute> {
284        let content_id = lotus_script_sys::FfiObject::new(&content_id);
285        let route = lotus_script_sys::FfiObject::from_packed(unsafe {
286            lotus_script_sys::pis::get_sp_route_data(content_id.packed(), route_code)
287        });
288        route.deserialize()
289    }
290}