lotus_extra/messages/std.rs
1use std::borrow::Cow;
2
3use lotus_script::prelude::*;
4use serde::{Deserialize, Serialize};
5
6use lotus_script::time::delta;
7
8//===================================================================
9// Vehicle number
10//===================================================================
11
12///This is/should be sent via broadcast to the own vehicle and all modules when sending. There is no
13/// transmission by bus via the coupler.
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct VehNumber<'a> {
16 pub value: Cow<'a, str>,
17}
18
19message_type!(VehNumber<'_>, "Std", "Vehiclenumber");
20
21impl<'a> VehNumber<'a> {
22 pub fn new(value: &'a str) -> Self {
23 Self {
24 value: Cow::Borrowed(value),
25 }
26 }
27
28 pub fn send(&self) {
29 send_message(
30 &(super::std::VehNumber::new(&self.value)),
31 [MessageTarget::Broadcast {
32 across_couplings: false,
33 include_self: true,
34 }],
35 );
36 }
37}
38
39//===================================================================
40// Batterymainswitch & Batteryvoltage
41//===================================================================
42
43///We have decided to merge these two pieces of information. Rust offers the option of variants of
44/// an enum carrying a data payload. We want to use this to transfer both pieces of information together.
45/// As you can see in the code snippet for enum Batteryvoltage, a float number is also transmitted
46/// with the `On` state. This represents the normalised battery voltage (i.e. 0.0 = no voltage, 1.0 = default voltage).
47/// In the `Off` state, no voltage needs to be transmitted as the battery is disconnected.
48///
49///This message replaces the previous `BATTERYSWITCH` (integer) and `VOLTAGE` (single).
50///
51/// As with the car number, this information is only sent to all recipients in a single vehicle.
52///
53/// Another important thing to note is that Battery = Off is the default for all objects.
54/// If a vehicle is spawned with the battery switched on at the start, this information must be sent once initially.
55#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
56pub enum Batteryvoltage {
57 On(f32), // Battery is on, with voltage as f32
58 #[default]
59 Off, // Battery is off, no voltage needed
60}
61
62impl Batteryvoltage {
63 // Custom PartialEq to only compare state type, not inner values
64 pub fn on_off_equal(&self, other: &Self) -> bool {
65 matches!(
66 (self, other),
67 (Batteryvoltage::On(_), Batteryvoltage::On(_))
68 | (Batteryvoltage::Off, Batteryvoltage::Off)
69 )
70 }
71
72 pub fn is_on(&self) -> bool {
73 match self {
74 Batteryvoltage::On(_) => true,
75 Batteryvoltage::Off => false,
76 }
77 }
78
79 pub fn is_off(&self) -> bool {
80 !self.is_on()
81 }
82}
83
84//---------------------------
85
86message_type!(Batteryvoltage, "Std", "Batteryvoltage");
87
88#[derive(Debug)]
89pub struct BatteryvoltageSender {
90 targets: Vec<MessageTarget>,
91 state_last: bool,
92 value_last: f32,
93}
94
95impl BatteryvoltageSender {
96 pub fn new(targets: impl IntoIterator<Item = MessageTarget>) -> Self {
97 Self {
98 targets: targets.into_iter().collect::<Vec<_>>(),
99 state_last: false,
100 value_last: 0.0,
101 }
102 }
103
104 // This function only sends updates if the values have changed
105 pub fn send(&mut self, state: bool, value: f32) {
106 // Sends message if state or value changed significantly
107 if (value - self.value_last).abs() > 0.01 || self.state_last != state {
108 send_message(
109 &(match state {
110 true => super::std::Batteryvoltage::On(value),
111 false => super::std::Batteryvoltage::Off,
112 }),
113 self.targets.clone(),
114 );
115 self.value_last = value;
116 self.state_last = state;
117 }
118 }
119}
120
121impl Default for BatteryvoltageSender {
122 fn default() -> Self {
123 BatteryvoltageSender::new(vec![MessageTarget::Broadcast {
124 across_couplings: false,
125 include_self: true,
126 }])
127 }
128}
129
130//===================================================================
131// Power Signal
132//===================================================================
133
134#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
135pub enum PowerSignalCabin {
136 A, // Cabin A (Index = 0)
137 B, // Cabin B (Index = 1)
138 No, // A other Cab (not in this Car)
139}
140
141impl From<i32> for PowerSignalCabin {
142 fn from(val: i32) -> Self {
143 match val {
144 0 => PowerSignalCabin::A,
145 1 => PowerSignalCabin::B,
146 _ => PowerSignalCabin::No,
147 }
148 }
149}
150
151///This message will replace and extend the old message `MAINSWITCH` (integer).
152/// We want to extend the information content as follows.
153/// Firstly, for each switch-on/switch-off message there is information
154/// on where the status has just been changed. For example, central on-board
155/// computers that are technically included in an IBIS terminal can also
156/// switch on and off correctly. The values `ACab` and `BCab` indicate
157/// whether one of the two driver's cabs has been activated in the respective vehicle.
158/// With the `NoCab` status, the modules in the attached wagons also receive
159/// the information that a driver's cab has now been activated in the train set.
160/// The other change is a Boolean parameter in the switch-on message. This
161/// can be used to transmit the ‘Quickstart’ command (value = true).
162/// As a result, modules should skip the boot process and run directly in steady operation.
163/// One of the advantages of this is that you don't have to wait for
164/// long start procedures when debugging the module and it also ensures that the module is
165/// directly available in the active driver's cab if the
166/// ‘Place vehicle’ start option ‘Ready to drive’ is selected. Previously, you had to cheat a little here.
167///
168/// The included information about the driver's cab means that this message is also sent
169/// directly to all modules (and the driver's own vehicle) via broadcast.
170/// The possibility of modules knowing in future which driver's cab they belong to means
171/// that they can react appropriately. The message is also NOT transmitted via the coupling
172/// and must be generated independently by each vehicle for its modules.
173/// Here, the default status is switched off for all objects, too.
174#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
175pub enum PowerSignalState {
176 On {
177 // Cabin is activated
178 // quickstart: true = do quickstart, false = do regular start
179 // For cabin_id see PowerSignalCabin
180 quickstart: bool,
181 cabin_id: PowerSignalCabin,
182 },
183 Off {
184 // Cabin is deactivated
185 // For cabin_id see PowerSignalCabin
186 cabin_id: PowerSignalCabin,
187 },
188}
189
190impl PowerSignalState {
191 pub fn is_on(&self) -> bool {
192 match self {
193 PowerSignalState::On {
194 quickstart: _,
195 cabin_id: _,
196 } => true,
197 PowerSignalState::Off { cabin_id: _ } => false,
198 }
199 }
200
201 pub fn is_off(&self) -> bool {
202 !self.is_on()
203 }
204}
205
206impl Default for PowerSignalState {
207 fn default() -> Self {
208 PowerSignalState::Off {
209 cabin_id: PowerSignalCabin::No,
210 }
211 }
212}
213
214//---------------------------
215
216message_type!(PowerSignalState, "Std", "PowerSignal");
217
218#[derive(Debug)]
219pub struct PowerSignalSender {
220 targets: Vec<MessageTarget>,
221 state_last: super::std::PowerSignalState,
222}
223
224impl PowerSignalSender {
225 pub fn new(targets: impl IntoIterator<Item = MessageTarget>) -> Self {
226 Self {
227 targets: targets.into_iter().collect::<Vec<_>>(),
228 state_last: super::std::PowerSignalState::default(),
229 }
230 }
231
232 // Send quickstart for cabin_id, only if cabin_activ
233 pub fn quickstart(&mut self, cabin_activ: bool, cabin_id: super::std::PowerSignalCabin) {
234 if cabin_activ {
235 send_message(
236 &(super::std::PowerSignalState::On {
237 quickstart: true,
238 cabin_id,
239 }),
240 self.targets.clone(),
241 );
242 self.state_last = super::std::PowerSignalState::On {
243 quickstart: true,
244 cabin_id,
245 };
246 }
247 }
248
249 // Sends state to cabin_id
250 // This function only sends updates if the values have changed
251 pub fn send(&mut self, state: bool, cabin_id: super::std::PowerSignalCabin) {
252 let value = match state {
253 true => super::std::PowerSignalState::On {
254 quickstart: false,
255 cabin_id,
256 },
257 false => super::std::PowerSignalState::Off { cabin_id },
258 };
259
260 let equal = match (value, self.state_last) {
261 (
262 super::std::PowerSignalState::On {
263 quickstart: _,
264 cabin_id: cab1,
265 },
266 super::std::PowerSignalState::On {
267 quickstart: _,
268 cabin_id: cab2,
269 },
270 ) => cab1 == cab2,
271 (
272 super::std::PowerSignalState::Off { cabin_id: cab1 },
273 super::std::PowerSignalState::Off { cabin_id: cab2 },
274 ) => cab1 == cab2,
275 _ => false,
276 };
277
278 if !equal {
279 self.state_last = value;
280 send_message(&(value), self.targets.clone());
281 }
282 }
283}
284
285impl Default for PowerSignalSender {
286 fn default() -> Self {
287 PowerSignalSender::new(vec![MessageTarget::Broadcast {
288 across_couplings: false,
289 include_self: true,
290 }])
291 }
292}
293
294//---------------------------
295
296pub fn send_mainswitch(
297 value: super::std::PowerSignalState,
298 targets: impl IntoIterator<Item = MessageTarget>,
299) {
300 send_message(&(value), targets);
301}
302
303//===================================================================
304// Light
305//===================================================================
306
307///Here again we have less to suggest. We are replacing the previous broadcast `LIGHT` (single).
308/// This is now a value of type f32.
309/// The value range should be the normalised brightness, i.e. 0.0 = light off & 1.0 = default brightness.
310///
311/// In principle, all modules should receive this information. A deviation is also possible if different
312/// modules are connected to different lighting circuits, for example.
313/// The message is explicitly NOT transmitted via the coupling and must be sent in/for each vehicle in the train itself.
314#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
315pub struct Light {
316 pub value: f32,
317}
318
319message_type!(Light, "Std", "Light");
320
321#[derive(Debug)]
322pub struct LightSender {
323 targets: Vec<MessageTarget>,
324 value_last: f32,
325}
326
327impl LightSender {
328 pub fn new(targets: impl IntoIterator<Item = MessageTarget>) -> Self {
329 Self {
330 targets: targets.into_iter().collect::<Vec<_>>(),
331 value_last: 0.0,
332 }
333 }
334
335 // Sends value, if changed significantly, to all defined targets
336 pub fn send(&mut self, value: f32) {
337 if (value - self.value_last).abs() > 0.01 {
338 send_message(&(super::std::Light { value }), self.targets.clone());
339 self.value_last = value;
340 }
341 }
342}
343
344impl Default for LightSender {
345 fn default() -> Self {
346 LightSender::new(vec![MessageTarget::Broadcast {
347 across_couplings: false,
348 include_self: true,
349 }])
350 }
351}
352
353//===================================================================
354// Stop Request
355//===================================================================
356
357///The broadcast `STOPREQUEST` (single) was previously used here. In our proposal, the function is retained,
358/// only the data type is changed to Boolean.
359///
360/// As with the previous messages, this is NOT transmitted via the coupler.
361/// However, it should otherwise reach all modules in the vehicle.
362/// If there are several stop request circuits in a vehicle (e.g. a bus in the front vehicle and in the rear vehicle),
363/// messages can of course be sent with different receivers.
364/// Vehicles without a stop request (e.g. historic trains or underground trains) do not have to provide this information.
365#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
366pub struct StopRequest {
367 pub value: bool,
368}
369
370message_type!(StopRequest, "Std", "StopRequest");
371
372#[derive(Debug)]
373pub struct StopRequestSender {
374 targets: Vec<MessageTarget>,
375 value_last: bool,
376}
377
378impl StopRequestSender {
379 pub fn new(targets: impl IntoIterator<Item = MessageTarget>) -> Self {
380 Self {
381 targets: targets.into_iter().collect::<Vec<_>>(),
382 value_last: false,
383 }
384 }
385
386 // Sends value to all moduls defined targets
387 // This function only sends updates if the values have changed
388 pub fn send(&mut self, value: bool) {
389 if value != self.value_last {
390 send_message(&(super::std::StopRequest { value }), self.targets.clone());
391 self.value_last = value;
392 }
393 }
394}
395
396impl Default for StopRequestSender {
397 fn default() -> Self {
398 StopRequestSender::new(vec![MessageTarget::Broadcast {
399 across_couplings: false,
400 include_self: true,
401 }])
402 }
403}
404
405//===================================================================
406// Velocity
407//===================================================================
408
409///We have been thinking about this topic for a while.
410/// We are taking up the idea of the message `VELOCITY` (single), which has so far only reached
411/// the on-board computer. We also considered the idea of using the distance travelled
412/// or a fixed distance pulse instead of the vehicle's speed. However, we abandoned these ideas,
413/// as speed is simply the most versatile and also provides the most information.
414///
415/// The value is sent in metres per second. The +/- sign represents the direction of travel.
416/// This message is also NOT sent via the coupling, but must be sent in every vehicle.
417/// It is important here that different modules also receive different speeds
418#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
419pub struct Velocity {
420 pub value: f32,
421}
422
423message_type!(Velocity, "Std", "Velocity");
424
425#[derive(Debug)]
426pub struct VelocitySender {
427 targets: Vec<MessageTarget>,
428 timer: f32,
429}
430
431impl VelocitySender {
432 pub fn new(targets: impl IntoIterator<Item = MessageTarget>) -> Self {
433 Self {
434 targets: targets.into_iter().collect::<Vec<_>>(),
435 timer: 0.0,
436 }
437 }
438
439 // Sends value every 0.3 seconds to the defined target
440 // Call this function every tick!
441 pub fn tick(&mut self, value: f32) {
442 self.timer -= delta();
443 if self.timer < 0.0 {
444 send_message(&(super::std::Velocity { value }), self.targets.clone());
445 self.timer += 0.3;
446 }
447 }
448}
449
450impl Default for VelocitySender {
451 fn default() -> Self {
452 VelocitySender::new(vec![MessageTarget::Broadcast {
453 across_couplings: false,
454 include_self: true,
455 }])
456 }
457}
458
459//===================================================================
460// Door State
461//===================================================================
462
463#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq)]
464pub enum DoorSide {
465 #[default]
466 None, // No side set
467 Right, // Doors on the right side are targeted
468 Left, // Doors on the left side are targeted
469 Both, // Doors on the both sides are targeted
470}
471
472///Now, we come to the door status information. Two different messages were used here in the previous Lotus.
473///One is `ATBUSSTOP` (integer) and the other is `DOORSOPEN` (integer). We now want to merge these.
474/// This will no longer only contain the information ‘door release’ yes/no,
475/// but also the status of whether the doors have generally been opened and on which side the doors are released/opened.
476///
477/// As with the other messages before, this information is NOT transmitted via the coupling
478/// so that the door side is not twisted in the case of asymmetrically coupled vehicles.
479/// Of course, there can also be several circuits here, e.g. in buses for the front end and the rear end.
480/// It is only important that all modules receive this information once.
481#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq)]
482pub enum DoorControlTarget {
483 #[default]
484 Closed, // Doors are not released or opened by the door control unit. Emergency releases etc. are not taken into account here.
485 Released(DoorSide), // Doors are released by the door control unit.
486 Open(DoorSide), // Doors are forced open by the door control unit.
487}
488
489//---------------------------
490
491message_type!(DoorControlTarget, "Std", "DoorState");
492
493pub struct DoorStateSender {
494 targets: Vec<MessageTarget>,
495 value_last: super::std::DoorControlTarget,
496}
497
498impl DoorStateSender {
499 pub fn new(targets: impl IntoIterator<Item = MessageTarget>) -> Self {
500 Self {
501 targets: targets.into_iter().collect::<Vec<_>>(),
502 value_last: super::std::DoorControlTarget::default(),
503 }
504 }
505
506 // Sends value to all moduls defined targets
507 // This function only sends updates if the values have changed
508 pub fn send(&mut self, value: super::std::DoorControlTarget) {
509 if value != self.value_last {
510 send_message(&(value), self.targets.clone());
511 self.value_last = value;
512 }
513 }
514}
515
516impl Default for DoorStateSender {
517 fn default() -> Self {
518 DoorStateSender::new(vec![MessageTarget::Broadcast {
519 across_couplings: false,
520 include_self: true,
521 }])
522 }
523}
524
525//---------------------------
526
527pub fn send_door_control_msg(
528 value: super::std::DoorControlTarget,
529 targets: impl IntoIterator<Item = MessageTarget>,
530) {
531 send_message(&(value), targets);
532}
533
534//===================================================================
535// Power Hold
536//===================================================================
537
538#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
539pub struct DisplayPowerHold {
540 pub value: bool,
541}
542
543message_type!(DisplayPowerHold, "Std", "DisplayPowerHold", "ModulParams");
544
545#[derive(Debug)]
546pub struct DisplayPowerHoldSender {
547 value_last: bool,
548}
549
550impl DisplayPowerHoldSender {
551 pub fn send(&mut self, value: bool) {
552 if value != self.value_last {
553 send_message(
554 &(super::std::DisplayPowerHold { value }),
555 [MessageTarget::Broadcast {
556 across_couplings: true,
557 include_self: false,
558 }],
559 );
560 self.value_last = value;
561 }
562 }
563}
564
565//===================================================================
566// Power Setting (0.0 .. 1.0)
567//===================================================================
568
569/// Passing a power setting value to a mechanical component
570/// (e.g. throttle valve in a combustion engine). Value generally
571/// ranges from 0.0 to 1.0, 1.0 corresponds to the maximum normal setting value. Values
572/// above 1.0 are also conceivable if technically possible.
573///
574/// Important: The target component can process this value in a control loop,
575/// but this is by no means necessarily the case!
576#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
577pub struct PowerSetting(pub f32);
578
579message_type!(PowerSetting, "Std", "PowerSetting");
580
581//===================================================================
582// Engine Start Stop
583//===================================================================
584
585/// Communication to the engine whether the starter or engine shutdown
586/// should be active.
587#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
588pub enum EngineStartStop {
589 Stop,
590 None,
591 Start,
592}
593
594message_type!(EngineStartStop, "Std", "EngineStartStop");
595
596//===================================================================
597// Gearbox modes
598//===================================================================
599
600/// Automatikgetriebe: Fahrmodus
601#[derive(Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd)]
602pub enum AutomaticGearboxMode {
603 Reverse,
604 #[default]
605 Neutral,
606 First,
607 FirstSecond,
608 FirstSecondThird,
609 Drive,
610}
611
612message_type!(AutomaticGearboxMode, "Std", "GearBoxMode");
613
614//===================================================================
615// Gearbox mode request
616//===================================================================
617
618/// Automatikgetriebe: Request Fahrmodus
619#[derive(Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd)]
620pub struct AutomaticGearboxModeRequest(pub AutomaticGearboxMode);
621
622message_type!(AutomaticGearboxModeRequest, "Std", "GearBoxModeRequest");
623
624//===================================================================
625// Current Gear
626//===================================================================
627
628/// Automatikgetriebe: Aktueller Gang
629#[derive(Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd)]
630pub struct AutomaticGearboxCurrentGear(pub i8);
631
632message_type!(AutomaticGearboxCurrentGear, "Std", "GearBoxCurrentGear");
633
634//===================================================================
635// Retarder
636//===================================================================
637
638/// Retarder request
639#[derive(Default, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
640pub struct RetarderRequest(pub i8);
641
642message_type!(RetarderRequest, "Std", "RetarderRequest");