BB System — Advanced Reference
==============================

The BB (BackBone) system is a synchronous, tick-based component framework in
lotus-extra for LOTUS vehicle scripts. It separates immutable configuration
(Module) from mutable per-tick state (BB*) and uses explicit change detection
to avoid redundant work and side effects.

The framework is used in production vehicle scripts (e.g. nd313, berlin-ubahn-d-f,
v6e). nd313 demonstrates the full recommended pattern with BBVehicle, bb_modules!
and VehicleInterface.


Architecture
------------

  Module (config)          BB* (runtime state)
  ----------------         -------------------
  PowerSupply              BBPowerSupply
  Battery                  BBBattery
  VdvDashboard<P>          BBVdvDashboard<P>
  ...

Each Module implements one or more traits from basic.rs:

  ModuleInit<T>              — one-time setup (init)
  ModuleTick<T>              — per-frame update (tick)
  ModuleTickInput<T, I>      — tick with external input
  ModuleOnAction<T>          — LOTUS action events (buttons, switches)
  ModuleOnMessage<T>         — LOTUS message handling

Vec<M> and Option<M> implement these traits automatically when M does.


Vehicle Scaffold (vehicle.rs, macros.rs)
----------------------------------------

For full vehicle scripts, lotus-extra provides:

  BBVehicle<M, B, X>     — implements lotus_script::Script
  VehicleInterface<B>    — vehicle-specific wiring between modules
  TickExtra              — optional per-tick logic outside BB modules (e.g. rattling)
  bb_modules!            — generates ModuleInit/Tick/OnAction/OnMessage and
                           BackBoneResetInputOutput from explicit mappings

Typical vehicle script layout:

  lib.rs        — Modules (config), Backbone (state), bb_modules!, script!(...)
  interface.rs  — impl VehicleInterface<Backbone> for Modules
  cockpit.rs    — vehicle-specific cockpit assembly (optional)
  traction.rs   — vehicle-specific traction logic (optional)

BBVehicle tick cycle (recommended):

  1. backbone.reset_inputs()
  2. modules.tick_interface(&mut backbone)   — wiring: read cross-module data,
                                               set BB inputs, forward_on_changed
  3. backbone.reset_outputs()
  4. modules.tick(&mut backbone)             — run each module's logic
  5. extras.tick_extra()                     — optional non-BB per-tick work

Message handling (on_message):

  1. modules.on_message(...)
  2. handle_message(...) → modules.on_action(...)
  3. modules.interface_on_message(...)

init:

  1. modules.init(&mut backbone)
  2. modules.after_init(&mut backbone)


Change Detection
----------------

BBSimple<T> and BBF32 wrap values with a refreshed flag:

  set() / set_if_different()  → refreshed = true
  changed()                     → was updated this tick?
  reset()                       → refreshed = false

Use forward_on_changed, call_on_changed, and boolean and/or/nand/nor only
when operands may have changed. BBF32 uses epsilon-based set_if_different.

BackBoneResetInputOutput distinguishes Input vs Output resets. Input-side
fields (switches, relays, targets) reset on Input; computed fields (lights,
indicator brightness) reset on Output.

The two reset calls bracket tick_interface and module ticks so that change
flags from the previous tick are cleared before wiring, and output-side flags
are cleared before modules run their main logic.


bb_modules! Macro
-----------------

Generates trait impls for a Modules struct and reset for Backbone fields.

  bb_modules! {
      Modules => Backbone {
          tick {
              pneumatics => pneumatics;
              cockpit => cockpit;
              axles[1] => axle;          // indexed module → backbone field
          }
          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,
          }
      }
  }

VehicleInterface is NOT generated by the macro — implement it manually in
interface.rs for cross-module wiring.


Reference Example: nd313 (condensed)
------------------------------------

Full script: Scripts/nd313 (Mercedes-Benz O 530 ND313 bus).

Entry point:

  type MyScript = BBVehicle<Modules, Backbone, Nd313Extras>;

  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,
  }

  #[derive(Default)]
  pub struct Backbone {
      pub pneumatics: BBRoadVehiclePneumatics,
      pub powersupply: BBPowerSupply,
      pub cockpit: BBCockpitNd313,
      pub doors: BBDoors,
      pub axle: BBAxle,
      // vehicle-specific cross-cutting state:
      pub retarder_request: BBSimple<i8>,
      // ...
  }

  script!(MyScript);

Wiring in interface.rs (VehicleInterface::tick_interface):

  fn tick_interface(&mut self, backbone: &mut Backbone) {
      self.powersupply_input(backbone);   // ignition → main relays
      self.pneumatics_input(backbone);    // brake pedal → air brake target
      self.traction_input(backbone);      // starter, gearbox mode, retarder
      self.outsidelights_input(backbone); // cockpit switches → bulb brightness
      self.cockpit_input(backbone);       // voltage, pneumatics → dashboard
      self.doors_input(backbone);         // stop brake, door buttons
  }

  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);
          });
  }

The Backbone may contain fields not owned by a single module (e.g. retarder_request)
for cross-cutting state used only in the interface layer.


Modules (src/bb_system/)
------------------------

  basic            Core traits, BBSimple, BBF32, BBModuleInputOutput
  vehicle          BBVehicle, VehicleInterface, TickExtra
  macros           bb_modules! macro
  input            Key → BBSimple<bool>
  power            Battery, ElectricBus, PowerSupply
  electric_power   ElectricUnit graph, absolute voltage, limiter, PowerSignal messages
  pneumatics       Tank/connection/compressor simulation (SI units, train coupling)
  doors            Pneumatic doors, releases, stop-brake controller
  cockpit          Button, StepSwitch, IndicatorLight, rotary controls
  cockpit_enhanced IgnitionSwitch, IndicatorSwitch, ModernOutsideLightSwitch, handbrake
  lights           Bulb, IndicatorLights, OutsideLights
  date_time        SimpleTimer, Timer, CallTimer, QueueTimer, SimpleBlinker
  piston_traction  Starter relay, ThrottleBrakeControl (bus/engine messages)
  road_vehicle     Steering, Axle, RoadVehiclePneumatics (implements VdvDashboardPneumatics)
  vdv_dashboard    Full VDV cockpit assembly (builder pattern)
  vdv_display      Dashboard texture drawing (pressure, diagnostics, footer)


Conventions
-----------

  • Module structs are Clone and hold indices, var names, sounds — no runtime state.
  • BB* structs are Default or manually constructed; live in the vehicle script.
  • Builder pattern: VdvDashboard::default().add_...(self).add_...(other)
  • Generic hook: VdvDashboardPneumatics on the pneumatics BB type for dashboard data.
  • Messages: modules send via lotus_script::message; read via ModuleOnMessage or
    handle_message (crate::messages) in on_message / BBVehicle.
  • Pneumatics constants: P_STD = 101_300 Pa, RT_AIR = R * T.
  • Adoption levels: full scaffold (nd313), manual Script with same tick phases
    (berlin-ubahn-d-f), or cockpit widgets only without reset cycle (v6e).


Minimal Example (single module, no scaffold)
--------------------------------------------

  use lotus_extra::bb_system::{
      basic::{ModuleInit, ModuleTick, BackBoneResetInputOutput},
      power::{Battery, ElectricBus, PowerSupply, BBPowerSupply},
  };

  let power = PowerSupply::new(
      vec![Battery::new(true)],
      vec![ElectricBus::new(vec![0], 0.5)],
  );
  let mut bb_power = BBPowerSupply::default();

  power.init(&mut bb_power);

  // Each tick (manual loop):
  bb_power.set_main_relay(0, true);
  power.tick(&mut bb_power);
  let voltage = bb_power.bus_voltage(0);
  bb_power.reset_inputs();
  bb_power.reset_outputs();


Minimal Example (ElectricPower, absolute voltage)
-------------------------------------------------

  use lotus_extra::bb_system::electric_power::{
      ElectricBatteryProperties, ElectricLimiter, ElectricPower, ElectricUnit,
      BBElectricPower,
  };

  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)),
  );
  let mut bb_electric = BBElectricPower::default();

  electric_power.init(&mut bb_electric);
  bb_electric.set_switch(bus, true);
  electric_power.tick(&mut bb_electric);
  let voltage = bb_electric.unit_voltage(bus);
  bb_electric.reset_inputs();
  bb_electric.reset_outputs();


Minimal Example (full vehicle scaffold)
---------------------------------------

  use lotus_extra::{bb_modules, bb_system::{BBVehicle, TickExtra, VehicleInterface}};

  type MyScript = BBVehicle<Modules, Backbone>;

  pub struct Modules { /* module configs */ }
  #[derive(Default)] pub struct Backbone { /* BB states */ }

  bb_modules! { Modules => Backbone { tick { ... } init { ... } reset { ... } } }

  impl VehicleInterface<Backbone> for Modules {
      fn tick_interface(&mut self, backbone: &mut Backbone) { /* wiring */ }
      fn interface_on_message(&mut self, _: &mut Backbone, _: &Message) {}
  }

  script!(MyScript);

See dokumentation_de.txt for a full beginner-oriented guide in German.
