BB-System — Dokumentation für Einsteiger
=========================================

Diese Dokumentation erklärt das BB-System (BackBone-System) in lotus-extra
Schritt für Schritt. Sie richtet sich an Personen, die neu in LOTUS-Scripting,
Rust oder beides sind.

Das BB-System wird bereits in Fahrzeugscripts eingesetzt, u. a. nd313 (Bus),
berlin-ubahn-d-f (U-Bahn) und v6e (historisches Straßenbahn-Cockpit). Das
Bus-Projekt nd313 zeigt das empfohlene Vollmuster mit BBVehicle, bb_modules!
und VehicleInterface.


Inhaltsverzeichnis
------------------

  1. Was ist das BB-System?
  2. Voraussetzungen: LOTUS, Rust und lotus-extra
  3. Grundidee: Konfiguration und Zustand trennen
  4. Der Ablauf eines Simulationsticks
  5. Fahrzeug-Gerüst: BBVehicle, bb_modules! und VehicleInterface
  6. Referenzbeispiel: nd313-Bus
  7. Kern-Typen und Traits (basic.rs)
  8. Änderungserkennung mit BBSimple und BBF32
  9. Eingaben, Ausgaben und Reset
 10. Module im Überblick
 11. Beispiel: Stromversorgung Schritt für Schritt
 12. Beispiel: Taster und Schalter im Cockpit
 13. VDV-Dashboard für Busse
 14. Pneumatik und Türen
 15. Eigene Module schreiben
 16. Häufige Fehler
 17. Glossar


================================================================================
1. Was ist das BB-System?
================================================================================

LOTUS simuliert Fahrzeuge frame für frame (Ticks). In jedem Tick liest das
Script Eingaben (Tasten, Schalter, Sensoren), berechnet den neuen Zustand
und schreibt Ausgaben (Variablen, Kräfte, Sounds, Nachrichten).

Das BB-System ist eine Bibliothek in lotus-extra, die diese Arbeit strukturiert.
Statt alles in einer großen Funktion zu schreiben, zerlegt man das Fahrzeug in
Module — zum Beispiel Stromversorgung, Beleuchtung, Türen, Lenkung.

Jedes Modul besteht aus zwei Teilen:

  • Konfiguration (z. B. PowerSupply, Button, Steering)
    — Was existiert? Welche Variablennamen? Welche Sounds?
    — Wird einmal definiert und ändert sich während der Fahrt nicht.

  • Laufzeit-Zustand (z. B. BBPowerSupply, BBButton, BBSteering)
    — Aktuelle Spannung, Schalterstellung, Lenkwinkel …
    — Wird in jedem Tick aktualisiert.

Der Name „BackBone“ (Rückgrat) bedeutet: Der BB*-Zustand ist das Rückgrat,
an dem alle Module pro Tick arbeiten.


================================================================================
2. Voraussetzungen: LOTUS, Rust und lotus-extra
================================================================================

LOTUS-Scripting
  Fahrzeug-Scripts werden in Rust geschrieben, zu WebAssembly kompiliert und
  von LOTUS ausgeführt. Die API stellt lotus-script bereit (Variablen, Input,
  Nachrichten, Physik).

Rust-Grundlagen, die Sie brauchen
  • struct — Datenstrukturen definieren
  • impl — Methoden und Trait-Implementierungen
  • mut — veränderbare Referenz (&mut)
  • Vec — dynamische Liste
  • Option — optionaler Wert (Some/None)
  • Trait — gemeinsames Verhalten (wie ein Interface)

lotus-extra
  Erweiterungspaket mit Hilfsfunktionen. Das BB-System liegt unter:

    src/bb_system/

  In Ihrem Fahrzeug-Script binden Sie lotus-extra als Abhängigkeit ein und
  importieren Module, z. B.:

    use lotus_extra::bb_system::{BBVehicle, VehicleInterface, ...};
    use lotus_extra::bb_system::power::{PowerSupply, BBPowerSupply, ...};


================================================================================
3. Grundidee: Konfiguration und Zustand trennen
================================================================================

Beispiel Stromversorgung:

  Konfiguration (PowerSupply):
    — Liste der Batterien
    — Liste der elektrischen Busse (Verteiler)
    — Mindestspannung pro Bus

  Zustand (BBPowerSupply):
    — Aktuelle Batteriespannung
    — Ist das Hauptrelais ein?
    — Liegt genug Spannung an?

Das Modul PowerSupply kennt die Regeln: „Wenn Relais an, dann Spannung =
maximale Batterie-Spannung.“ Der Zustand BBPowerSupply speichert nur die
aktuellen Werte.

Vorteile:
  • Übersicht: Jedes System hat eine klare Datei/Struktur.
  • Wiederverwendung: Derselbe PowerSupply-Typ für verschiedene Busse.
  • Testbarkeit: Logik ist von LOTUS-API-Aufrufen gekapselt.
  • Kein Async nötig: Alles läuft synchron pro Tick (deterministisch).


================================================================================
4. Der Ablauf eines Simulationsticks
================================================================================

Empfohlener Ablauf mit BBVehicle (siehe Abschnitt 5):

  ┌─────────────────────────────────────────────────────────────┐
  │  EINMAL BEIM START (init)                                   │
  │    modules.init(&mut backbone);                             │
  │    modules.after_init(&mut backbone);   // optional           │
  └─────────────────────────────────────────────────────────────┘
                              │
                              ▼
  ┌─────────────────────────────────────────────────────────────┐
  │  JEDEN TICK                                                 │
  │                                                             │
  │  1. backbone.reset_inputs()                                 │
  │     — refreshed-Flags auf Eingabe-Seite löschen             │
  │                                                             │
  │  2. modules.tick_interface(&mut backbone)                   │
  │     — fahrzeugspezifische Verdrahtung (interface.rs)        │
  │     — Querschnittslogik: Zündschloss → Relais, Bremspedal   │
  │       → Pneumatik, Spannung → Anzeigen …                    │
  │                                                             │
  │  3. backbone.reset_outputs()                                │
  │     — refreshed-Flags auf Ausgabe-Seite löschen             │
  │                                                             │
  │  4. modules.tick(&mut backbone)                             │
  │     — jedes BB-Modul berechnet seinen Zustand               │
  │                                                             │
  │  5. extras.tick_extra()              // optional            │
  │     — z. B. Rattling, nicht-BB-Logik                        │
  └─────────────────────────────────────────────────────────────┘

  Bei Nachrichten (on_message):
    modules.on_message → handle_message/on_action → interface_on_message

Warum zwei Reset-Phasen?
  Vor tick_interface werden Eingabe-Flags gelöscht, damit call_on_changed und
  forward_on_changed nur echte Änderungen seit dem letzten Tick sehen. Vor
  modules.tick werden Ausgabe-Flags gelöscht, damit Modul-interne Logik sauber
  arbeitet.

Einfacheres Muster (ohne Gerüst)
  Für kleine Scripts oder einzelne Module kann man init/tick/reset manuell
  aufrufen. Das Projekt v6e nutzt bb_system/cockpit-Widgets ohne Reset-Zyklus;
  komplexere Fahrzeuge sollten den vollständigen Zyklus verwenden.


================================================================================
5. Fahrzeug-Gerüst: BBVehicle, bb_modules! und VehicleInterface
================================================================================

Für vollständige Fahrzeugscripts stellt lotus-extra ein Gerüst bereit:

  BBVehicle<M, B, X>
    — Implementiert lotus_script::Script
    — M = Modules (Konfiguration aller BB-Module)
    — B = Backbone (Laufzeitzustand)
    — X = Extras (optional, z. B. Rattling), Default: ()

  VehicleInterface<B>
    — after_init     — einmal nach init
    — tick_interface — Verdrahtung zwischen Modulen (pro Tick, vor modules.tick)
    — interface_on_message — fahrzeugspezifische Nachrichten-Handler

  TickExtra
    — tick_extra() — optionale Zusatzlogik am Tick-Ende

  bb_modules!
    — Generiert ModuleInit, ModuleTick, ModuleOnAction, ModuleOnMessage
    — Generiert BackBoneResetInputOutput für die genannten Backbone-Felder
    — VehicleInterface muss weiterhin manuell implementiert werden

Typische Dateistruktur im Fahrzeugprojekt:

  src/lib.rs       — Modules, Backbone, bb_modules!, script!(MyScript)
  src/interface.rs — impl VehicleInterface<Backbone> for Modules
  src/cockpit.rs   — fahrzeugspezifisches Cockpit (optional)
  src/traction.rs  — fahrzeugspezifischer Antrieb (optional)

bb_modules!-Beispiel:

  bb_modules! {
      Modules => Backbone {
          tick {
              pneumatics => pneumatics;
              cockpit => cockpit;
              axles[1] => axle;       // indexiertes Modul
          }
          init {
              pneumatics => pneumatics;
              cockpit => cockpit;
          }
          on_action {
              cockpit => cockpit;
          }
          on_message {
              traction => piston_traction_transfer;
              axles[1] => axle;
          }
          reset {
              cockpit, powersupply, traction, outside_lights, doors,
          }
      }
  }

Einstiegspunkt:

  type MyScript = BBVehicle<Modules, Backbone, Nd313Extras>;
  script!(MyScript);

Alternativen
  berlin-ubahn-d-f implementiert denselben Tick-Ablauf manuell in impl Script,
  ohne BBVehicle — funktional gleichwertig, etwas mehr Boilerplate.


================================================================================
6. Referenzbeispiel: nd313-Bus
================================================================================

Das Fahrzeugscript nd313 (Mercedes-Benz O 530) ist das Referenzprojekt für
das volle BB-Muster. Quellcode: Scripts/nd313.

Aufbau
------

  lib.rs
    — struct Modules mit allen Modul-Konfigurationen
    — struct Backbone mit allen BB-Zuständen
    — bb_modules! für init/tick/on_message/reset
    — type MyScript = BBVehicle<Modules, Backbone, Nd313Extras>

  interface.rs
    — impl VehicleInterface<Backbone> for Modules
    — tick_interface ruft Hilfsmethoden auf (powersupply_input, doors_input, …)

  cockpit.rs
    — CockpitNd313 mit VdvDashboard-Builder

  traction.rs
    — fahrzeugspezifischer Antrieb (PistonTraction)

Modules (Auszug)
----------------

  pub struct Modules {
      axles: Vec<Axle>,
      powersupply: PowerSupply,
      pneumatics: RoadVehiclePneumatics,
      traction: Traction,
      throttle_brake_control: ThrottleBrakeControl,
      outside_lights: OutsideLights,
      doors: Doors,
      cockpit: CockpitNd313,
      steering: Steering,
  }

Backbone (Auszug)
-----------------

  pub struct Backbone {
      pub pneumatics: BBRoadVehiclePneumatics,
      pub powersupply: BBPowerSupply,
      pub cockpit: BBCockpitNd313,
      pub doors: BBDoors,
      pub axle: BBAxle,
      pub piston_traction_transfer: BBPistonTractionTransfer,
      // fahrzeugeigener Querschnittszustand (kein eigenes Modul):
      pub retarder_request: BBSimple<i8>,
      // ...
  }

Neben den BB-Zuständen der Module kann der Backbone auch Felder tragen, die
nur in der Interface-Schicht gebraucht werden.

Verdrahtung (interface.rs, Auszug)
----------------------------------

  impl VehicleInterface<Backbone> for Modules {
      fn tick_interface(&mut self, backbone: &mut Backbone) {
          self.powersupply_input(backbone);
          self.pneumatics_input(backbone);
          self.traction_input(backbone);
          self.steering_input(backbone);
          self.outsidelights_input(backbone);
          self.cockpit_input(backbone);
          self.doors_input(backbone);
      }
  }

  // Zündschloss → Hauptrelais:
  fn powersupply_input(&self, backbone: &mut Backbone) {
      backbone.cockpit.vdv_dashboard.ignition_switch.state()
          .call_on_changed(|state| {
              backbone.powersupply.set_main_relay(0, state >= IgnitionSwitchStep::Step1);
              backbone.powersupply.set_main_relay(1, state >= IgnitionSwitchStep::Step2);
          });
  }

  // Bremspedal → Pneumatik:
  fn pneumatics_input(&mut self, backbone: &mut Backbone) {
      backbone.pneumatics.n_engine_rpm = backbone.piston_traction_transfer.rpm;
      backbone.pneumatics.target_air_brake = backbone.throttle_brake_control.brake_value();
      backbone.pneumatics.target_stop_brake = backbone.doors.stop_brake().get_state();
  }

  // Bremspedal → Bremslichter, Blinker → Außenbeleuchtung:
  fn outsidelights_input(&mut self, backbone: &mut Backbone) {
      let brake = (backbone.throttle_brake_control.brake_value() > 0.02).if_else(1.0, 0.0);
      backbone.outside_lights.set_bulb_brightness(BULB_INDEX_BRAKE, brake * voltage);
      // ...
  }

  // Spannung und Pneumatik → VDV-Dashboard, Türen → Anzeigen:
  fn cockpit_input(&mut self, backbone: &mut Backbone) {
      backbone.cockpit.vdv_dashboard.voltage =
          backbone.powersupply.get_bus(0).unwrap().voltage();
      backbone.cockpit.vdv_dashboard.pneumatics = backbone.pneumatics;
      backbone.doors.door_closed(0).call_on_changed(|closed| {
          backbone.cockpit.vdv_dashboard.il_doors_target[0].set(!closed);
      });
  }

Was man hier lernt
  • BB-Module sind generisch und wiederverwendbar.
  • Die fahrzeugspezifische Logik liegt in interface.rs (Schaltplan).
  • call_on_changed und forward_on_changed verbinden Module ohne deren
    Konfiguration zu ändern.
  • Die Tick-Reihenfolge im bb_modules!-Block bestimmt die Berechnungsreihenfolge
    der Module (z. B. pneumatics vor cockpit).


================================================================================
7. Kern-Typen und Traits (basic.rs)
================================================================================

Traits sind „Verträge“: Wer das Trait implementiert, muss bestimmte Funktionen
bereitstellen.

ModuleInit<T>
  fn init(&self, backbone: &mut T)
  — Einmalige Initialisierung des Zustands T.
  — Beispiel: Batterie auf volle Spannung setzen, Variablen auf 0.

ModuleTick<T>
  fn tick(&self, backbone: &mut T)
  — Wird jeden Tick aufgerufen. Enthält die Hauptlogik.

ModuleTickInput<T, I>
  fn tick(&self, backbone: &mut T, input: I)
  — Wie ModuleTick, aber mit zusätzlichem Eingabewert pro Aufruf.
  — Beispiel: IndicatorLight bekommt (leuchtet_an, spannung).

ModuleOnAction<T>
  fn on_action(&self, backbone: &mut T, action: &ActionEvent) -> bool
  — Reagiert auf LOTUS-Action-Events (Taster, Schalter im Cockpit).

ModuleOnMessage<T>
  fn on_message(&self, backbone: &mut T, msg: &Message) -> bool
  — Reagiert auf LOTUS-Nachrichten. Rückgabe true = Nachricht verarbeitet.

BackBone<T>
  Für Werte mit Änderungserkennung (BBSimple, BBF32):
    get_state()           — aktueller Wert
    changed()             — hat sich dieser Tick geändert?
    set(state)            — Wert setzen und als geändert markieren
    set_if_different(...) — nur setzen wenn wirklich anders
    forward_on_changed    — Wert bei Änderung an anderes BackBone weitergeben
    call_on_changed       — Callback nur bei Änderung ausführen

BackBoneReset / BackBoneResetInputOutput
  reset() bzw. reset(reset_type) — refreshed-Flags zurücksetzen.
  Input vs. Output: Eingabe- und Ausgabe-Seite getrennt resetten.

Hilfreich: Vec<M> und Option<M>
  Wenn M ein Modul-Trait implementiert, funktioniert das automatisch für
  Vec<M> (alle Elemente nacheinander) und Option<M> (nur wenn Some).


================================================================================
8. Änderungserkennung mit BBSimple und BBF32
================================================================================

BBSimple<T>
  Wrappt einen Wert vom Typ T (z. B. bool, i32, Enum) plus ein Flag refreshed.

  let mut schalter = BBSimple::new(false);
  schalter.set(true);       // refreshed = true
  if schalter.changed() {  // true in diesem Tick
      // Reaktion auslösen
  }
  schalter.reset();         // refreshed = false

  Für bool gibt es Logik-Operatoren, die nur rechnen wenn sich Operanden
  geändert haben:
    a.and(&b, &mut ergebnis);
    a.or(&b, &mut ergebnis);

BBF32
  Wie BBSimple für f32, aber set_if_different vergleicht mit Epsilon
  (Toleranz für Gleitkomma-Ungenauigkeiten).

  BBF32::new(0.0, 0.01)  — Startwert 0, Epsilon 0.01

Warum das wichtig ist
  Ohne Änderungserkennung würde man jeden Tick Sounds abspielen, Variablen
  schreiben oder Nachrichten senden — auch wenn sich nichts geändert hat.
  Das BB-System vermeidet das gezielt.


================================================================================
9. Eingaben, Ausgaben und Reset
================================================================================

BackBoneResetType::Input
  Setzt refreshed auf Eingabe-Feldern zurück (Schalter, Taster, Sollwerte).

BackBoneResetType::Output
  Setzt refreshed auf Ausgabe-Feldern zurück (berechnete Lampenhelligkeit,
  gesendete Zustände).

BBModuleInputOutput<I, O>
  Kombiniert input: BBSimple<I> und output: BBSimple<O> in einer Struktur.

Typisches Muster mit BBVehicle:
  — Am Tick-Anfang: backbone.reset_inputs()
  — Nach tick_interface: backbone.reset_outputs()
  — Module berechnen in modules.tick()
  — bb_modules! listet die Backbone-Felder auf, die reset implementieren


================================================================================
10. Module im Überblick
================================================================================

basic
  Grundlage: Traits, BBSimple, BBF32, BBModuleInputOutput, Vec/Option-Helfer.

vehicle
  BBVehicle, VehicleInterface, TickExtra — Fahrzeug-Gerüst (vehicle.rs).

macros
  bb_modules! — generiert Trait-Impls und Reset (macros.rs).

input
  Input::new("Tastenname") — liest key_just_pressed/key_just_released und
  schreibt in BBSimple<bool>.

power
  Battery       — Batterie mit Schalter und relativer Spannung
  ElectricBus   — Verteiler mit Hauptrelais und Mindestspannungs-Relais
  PowerSupply   — Verknüpft mehrere Batterien und Busse

electric_power
  ElectricUnit  — Spannungs-Unit mit Quellen-Graph, Batterie, Limiter
  ElectricPower — Verknüpft mehrere Units (absolute Spannung in Volt)
  Alternative zu power.rs mit flexibleren PowerSignal-Nachrichten und
  Upstream-Quellen; Builder über add_unit_get_index.

pneumatics
  Physikalische Pneumatik-Simulation für Schienenfahrzeuge (SI-Einheiten).
  Tanks, Verbindungen, Ventile, Kompressoren, Kupplung zwischen Fahrzeugen.
  Konstanten: P_STD = 101300 Pa, RT_AIR = 287 * 293.15.

doors
  PneumaticDoor, DoorUnit, DoorRelease, StopBrakeController, Doors.
  Steuert Türdruck, Freigabe, Haltestellenbremse.

cockpit
  Button, StepSwitch, IndicatorLight, Drehschalter, Tachometer-Nadeln u. a.
  Reagiert auf InputEvents und schreibt LOTUS-Variablen.

cockpit_enhanced
  Erweiterte Bus-Cockpit-Elemente: Zündschloss, Blinkerhebel,
  Außenlicht-Drehschalter, pneumatische Feststellbremse.

lights
  Bulb (mit optionalem exponentiellem Ein-/Ausblenden),
  IndicatorLights (Blinker links/rechts/Warnblinken),
  OutsideLights (Fern-/Abblendlicht).

date_time
  SimpleTimer, Timer, CallTimer, QueueTimer — zeitgesteuerte Zustände.
  SimpleBlinker — Blinkrelais für Warnleuchten.

piston_traction
  PistonTraction — Anlasser, Nachrichten an Motor/Getriebe.
  ThrottleBrakeControl — Fahr- und Bremspedal für Busse mit Verbrenner.

road_vehicle
  Steering — Lenkrad, Mauslenkung, Tastatur.
  Axle — Radsatzgeschwindigkeit, Antriebskraft, Kneeling.
  RoadVehiclePneumatics — vereinfachte Bus-Pneumatik (Behälter, Bremszylinder).

vdv_dashboard / vdv_display
  VdvDashboard — komplettes VDV-Cockpit per Builder (.add_...).
  VdvDisplay — zeichnet Anzeige (Druck, Diagnose, Footer) auf Texturen.
  Erfordert Implementierung von VdvDashboardPneumatics am Pneumatik-Zustand.


================================================================================
11. Beispiel: Stromversorgung Schritt für Schritt
================================================================================

Schritt 1 — Konfiguration anlegen (typischerweise in Modules::default):

  let power = PowerSupply::new(
      vec![Battery::new(true)],           // eine Batterie, sendet Spannungs-Msg
      vec![ElectricBus::new(vec![0], 0.5)], // Bus 0, Batterie 0, min. 50 %
  );

Schritt 2 — Zustand im Backbone:

  pub struct Backbone {
      pub powersupply: BBPowerSupply,
      // ...
  }

Schritt 3 — Beim Fahrzeugstart (automatisch via BBVehicle.init):

  // bb_modules! { init { powersupply => powersupply; } }

Schritt 4 — Verdrahtung in interface.rs:

  backbone.powersupply.set_main_relay(0, zündung_an);

Schritt 5 — Berechnung in modules.tick (automatisch via bb_modules!):

  // powersupply.tick(&mut backbone.powersupply);

  let u = backbone.powersupply.bus_voltage(0);
  let bus_ok = backbone.powersupply.bus_active(0).get_state();

Die Batterie liefert rel_voltage (0.0–1.0). Der Bus kombiniert Batterien und
schaltet bei ausreichender Spannung min_voltage_relay. Bei Relais-Wechsel
werden LOTUS PowerSignalState-Nachrichten gesendet.


================================================================================
11b. Beispiel: ElectricPower (absolute Spannung, Quellen-Graph)
================================================================================

Schritt 1 — Konfiguration anlegen:

  use lotus_extra::bb_system::electric_power::{
      ElectricBatteryProperties, ElectricLimiter, ElectricPower, ElectricUnit,
      SendPowerSignalMessage, BBElectricPower,
  };
  use lotus_script::message::MessageTarget;

  let mut electric_power = ElectricPower::default();

  let generator = electric_power.add_unit_get_index(
      ElectricUnit::new(vec![]).with_external_power(750.0),
  );

  let bus = electric_power.add_unit_get_index(
      ElectricUnit::new(vec![generator])
          .with_battery(ElectricBatteryProperties::new(24.0))
          .with_limiter(ElectricLimiter::new(20.0, 200.0).with_auto_reset(true)),
  );

Schritt 2 — Zustand im Backbone:

  pub struct Backbone {
      pub electric_power: BBElectricPower,
  }

Schritt 3 — bb_modules! mit tick, init und reset registrieren.

Schritt 4 — Verdrahtung in interface.rs:

  backbone.electric_power.set_switch(bus, zündung_an);

Schritt 5 — Auslesen nach tick:

  let u = backbone.electric_power.unit_voltage(bus);
  let ok = backbone.electric_power.unit_active(bus).get_state();

Units mit niedrigerem Index sollten vor ihren Abnehmern stehen, damit Quellen
im selben Tick bereits aktualisiert sind.


================================================================================
12. Beispiel: Taster und Schalter im Cockpit
================================================================================

Ein einfacher Ein/Aus-Schalter (aus vdv_dashboard.rs, Hilfsfunktion):

  standard_switch("Var_Schaltername", InputEvent::new("Action_Name", 0))

  — ButtonBehaviour::OnOff
  — Schreibt Positions-Variable, spielt Standard-Sounds

Indikatorleuchte:

  IndicatorLight::new(IndicatorLightType::Bulb((30.0, 30.0)), Some("Lm_Name"))

  — Bulb: träge Glühlampe (exponentielles Ein/Aus)
  — LED: schaltet hart ab unter Mindestspannung
  — tick bekommt (soll_leuchten, spannung)

Im nd313-Cockpit wird das VdvDashboard per Builder zusammengesetzt:

  VdvDashboard::default()
      .add_std_ignition_key()
      .add_std_ignition_switch()
      .add_automatic_gear_box_mode_switch_group(...)
      .add_std_retarder_switch()
      // ...

Spannung und Pneumatik werden in interface.rs vor dem Dashboard-Tick gesetzt:

  backbone.cockpit.vdv_dashboard.voltage = backbone.powersupply.get_bus(0).unwrap().voltage();
  backbone.cockpit.vdv_dashboard.pneumatics = backbone.pneumatics;


================================================================================
13. VDV-Dashboard für Busse
================================================================================

VdvDashboard ist ein fertiges Cockpit-Gerüst für VDV-konforme Busse.

Aufbau per Builder:

  let dashboard = VdvDashboard::default()
      .set_cockpit_index(0)
      .add_ignition_switch(...)
      .add_indicator_switch(...)
      .add_display(VdvDisplay::new(...))
      ...;

Zustand (in Backbone, z. B. in BBCockpitNd313):

  pub struct BBCockpitNd313 {
      pub vdv_dashboard: BBVdvDashboard<BBRoadVehiclePneumatics>,
      pub parking_brake: BBPneumaticHandbrakeLever,
  }

  dashboard.init(&mut bb_cockpit.vdv_dashboard);
  // Spannung/Pneumatik in tick_interface setzen, dann:
  // cockpit.tick(&mut bb_cockpit) via bb_modules!

VdvDashboardPneumatics
  Ihr Pneumatik-Zustand (z. B. BBRoadVehiclePneumatics) muss dieses Trait
  implementieren, damit die Anzeige Druckbehälter und Bremszylinder zeigen kann:
    pressure_low_warning, p_reservoir, p_brake_cylinder, a_transfervalve

Nachrichten
  VdvDashboard implementiert ModuleOnMessage — Taster und Schalter reagieren
  auf ActionEvents und LOTUS-Messages (Getriebemodus, Motor, …).


================================================================================
14. Pneumatik und Türen
================================================================================

Zwei Ebenen:

  pneumatics::PneumaticSystem
    — Generische Simulation mit Tanks und Rohrleitungen (Schienenfahrzeuge,
      Kupplung zwischen Wagen). Verwendet in berlin-ubahn-d-f.

  road_vehicle::RoadVehiclePneumatics
    — Vereinfachtes Modell für Busse (Behälter 1–4, Bremszylinder,
      Federspeicher, Kneeling). Verwendet in nd313.

  doors::Doors
    — Türantriebe mit Druckmodell, Freigabe-Taster, Haltestellenbremse.

Türen und Bremsen teilen oft den gleichen Druck — deshalb hängen viele
BB-Felder zusammen und werden in interface.rs und modules.tick aktualisiert.


================================================================================
15. Eigene Module schreiben
================================================================================

Muster:

  1. Konfigurations-struct (Clone) mit allen festen Parametern
  2. BB*-struct (Default) mit Laufzeit-Feldern, ggf. BBSimple/BBF32
  3. impl ModuleInit<BBMeins> — Startwerte
  4. impl ModuleTick<BBMeins> — Logik pro Tick
  5. Optional: ModuleTickInput, ModuleOnMessage, ModuleOnAction
  6. impl BackBoneResetInputOutput für BBMeins — reset aufteilen
  7. In bb_modules! eintragen und in interface.rs verdrahten

Beispiel-Skelett:

  pub struct MyModule { index: usize }

  #[derive(Default)]
  pub struct BBMyModule {
      pub input: BBSimple<bool>,
      pub output: BBSimple<f32>,
  }

  impl ModuleInit<BBMyModule> for MyModule {
      fn init(&self, bb: &mut BBMyModule) {
          bb.output.set(0.0);
      }
  }

  impl ModuleTick<BBMyModule> for MyModule {
      fn tick(&self, bb: &mut BBMyModule) {
          if bb.input.changed() {
              bb.output.set(if bb.input.get_state() { 1.0 } else { 0.0 });
          }
      }
  }

  impl BackBoneResetInputOutput for BBMyModule {
      fn reset(&mut self, t: BackBoneResetType) {
          match t {
              BackBoneResetType::Input => self.input.reset(),
              BackBoneResetType::Output => self.output.reset(),
          }
      }
  }


================================================================================
16. Häufige Fehler
================================================================================

  • reset_inputs/reset_outputs vergessen
    → changed() bleibt dauerhaft true, Logik feuert jeden Tick.

  • Spannung/Druck nicht vor dashboard.tick in tick_interface setzen
    → Anzeigen und Lampen reagieren nicht.

  • Verdrahtung in modules.tick statt tick_interface
    → Reihenfolge-Probleme; Interface-Schicht ist der richtige Ort.

  • Modul-Konfiguration in BB-Zustand mischen
    → Schwer wartbar; immer trennen.

  • set() statt set_if_different bei Gleitkomma
    → Unnötige Updates; BBF32 oder set_if_different nutzen.

  • Index-Fehler bei Bus/Batterie/Achse
    → Panic mit „not found“; Indizes bei Konfiguration dokumentieren.

  • ModuleOnMessage/OnAction nicht in bb_modules! eingetragen
    → Schalter reagieren nicht auf LOTUS-Actions/Nachrichten.

  • VehicleInterface vergessen
    → Module laufen, aber nichts ist untereinander verbunden.


================================================================================
17. Glossar
================================================================================

  BackBone (BB)     Laufzeit-Zustand eines Moduls (Präfix BB*)
  Modul             Konfiguration + Trait-Implementierung (Logik)
  Tick              Ein Simulationsschritt (ein Frame)
  refreshed         Markierung: Wert hat sich diesen Tick geändert
  Trait             Rust-Schnittstelle für gemeinsames Verhalten
  LOTUS-Variable    set_var/get_var — Anbindung an 3D-Modell und UI
  Message           Nachricht zwischen Script-Modulen oder Fahrzeugen
  VDV               Verband Deutscher Verkehrsunternehmen — Bus-Normen
  P_STD             Atmosphärendruck in Pascal (101300)
  Builder           Methoden-Kette (.add_...().add_...) zum Konfigurieren
  BBVehicle         Standard-Gerüst für Fahrzeugscripts (vehicle.rs)
  bb_modules!       Macro für Modul-Orchestrierung (macros.rs)
  VehicleInterface  Trait für fahrzeugspezifische Verdrahtung (interface.rs)
  tick_interface    Verdrahtungsphase vor modules.tick

================================================================================

Weiterführend: readme_en.txt (Kurzreferenz auf Englisch für Fortgeschrittene)

Quellcode: src/bb_system/ — jedes Modul in einer eigenen .rs-Datei, Einstieg
über mod.rs.

Referenz-Fahrzeugscript: Scripts/nd313 (volles BB-Muster).
