use crate::imports::*;
use crate::track::PathTpc;
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, HistoryVec)]
#[altrios_api(
#[new]
fn __new__(
time_seconds: Option<f64>,
offset_meters: Option<f64>,
speed_meters_per_second: Option<f64>,
) -> Self {
Self::new(
time_seconds.map(|x| x * uc::S),
offset_meters.map(|x| x * uc::M),
speed_meters_per_second.map(|x| x * uc::MPS),
)
}
)]
pub struct InitTrainState {
pub time: si::Time,
pub offset: si::Length,
pub speed: si::Velocity,
}
impl Default for InitTrainState {
fn default() -> Self {
Self {
time: si::Time::ZERO,
offset: f64::NAN * uc::M,
speed: si::Velocity::ZERO,
}
}
}
impl InitTrainState {
pub fn new(
time: Option<si::Time>,
offset: Option<si::Length>,
speed: Option<si::Velocity>,
) -> Self {
let base = InitTrainState::default();
Self {
time: time.unwrap_or(base.time),
offset: offset.unwrap_or(base.offset),
speed: speed.unwrap_or(base.speed),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, HistoryVec, PartialEq)]
#[altrios_api(
#[new]
#[allow(clippy::too_many_arguments)]
fn __new__(
length_meters: f64,
mass_static_kilograms: f64,
mass_adj_kilograms: f64,
mass_freight_kilograms: f64,
init_train_state: Option<InitTrainState>,
) -> Self {
Self::new(
length_meters * uc::M,
mass_static_kilograms * uc::KG,
mass_adj_kilograms * uc::KG,
mass_freight_kilograms * uc::KG,
init_train_state,
)
}
)]
pub struct TrainState {
pub time: si::Time,
pub i: usize,
pub offset: si::Length,
pub offset_back: si::Length,
pub total_dist: si::Length,
pub link_idx_front: u32,
pub offset_in_link: si::Length,
pub speed: si::Velocity,
pub speed_limit: si::Velocity,
pub speed_target: si::Velocity,
pub dt: si::Time,
pub length: si::Length,
pub mass_static: si::Mass,
pub mass_adj: si::Mass,
pub mass_freight: si::Mass,
pub weight_static: si::Force,
pub res_rolling: si::Force,
pub res_bearing: si::Force,
pub res_davis_b: si::Force,
pub res_aero: si::Force,
pub res_grade: si::Force,
pub res_curve: si::Force,
pub grade_front: si::Ratio,
pub elev_front: si::Length,
pub pwr_res: si::Power,
pub pwr_accel: si::Power,
pub pwr_whl_out: si::Power,
pub energy_whl_out: si::Energy,
pub energy_whl_out_pos: si::Energy,
pub energy_whl_out_neg: si::Energy,
}
impl Default for TrainState {
fn default() -> Self {
Self {
time: Default::default(),
i: 1,
offset: Default::default(),
offset_back: Default::default(),
total_dist: si::Length::ZERO,
link_idx_front: Default::default(),
offset_in_link: Default::default(),
speed: Default::default(),
speed_limit: Default::default(),
dt: uc::S,
length: Default::default(),
mass_static: Default::default(),
mass_adj: Default::default(),
mass_freight: Default::default(),
elev_front: Default::default(),
energy_whl_out: Default::default(),
grade_front: Default::default(),
speed_target: Default::default(),
weight_static: Default::default(),
res_rolling: Default::default(),
res_bearing: Default::default(),
res_davis_b: Default::default(),
res_aero: Default::default(),
res_grade: Default::default(),
res_curve: Default::default(),
pwr_res: Default::default(),
pwr_accel: Default::default(),
pwr_whl_out: Default::default(),
energy_whl_out_pos: Default::default(),
energy_whl_out_neg: Default::default(),
}
}
}
impl TrainState {
#[allow(clippy::too_many_arguments)]
pub fn new(
length: si::Length,
mass_static: si::Mass,
mass_adj: si::Mass,
mass_freight: si::Mass,
init_train_state: Option<InitTrainState>,
) -> Self {
let init_train_state = init_train_state.unwrap_or_default();
let offset = init_train_state.offset.max(length);
Self {
time: init_train_state.time,
i: 1,
offset,
offset_back: offset - length,
total_dist: si::Length::ZERO,
speed: init_train_state.speed,
speed_limit: init_train_state.speed,
length,
mass_static,
mass_adj,
mass_freight,
..Self::default()
}
}
pub fn res_net(&self) -> si::Force {
self.res_rolling
+ self.res_bearing
+ self.res_davis_b
+ self.res_aero
+ self.res_grade
+ self.res_curve
}
}
impl Valid for TrainState {
fn valid() -> Self {
Self {
length: 2000.0 * uc::M,
offset: 2000.0 * uc::M,
offset_back: si::Length::ZERO,
mass_static: 6000.0 * uc::TON,
mass_adj: 6200.0 * uc::TON,
dt: uc::S,
..Self::default()
}
}
}
impl ObjState for TrainState {
fn validate(&self) -> ValidationResults {
let mut errors = ValidationErrors::new();
si_chk_num_gtz_fin(&mut errors, &self.mass_static, "Mass static");
si_chk_num_gtz_fin(&mut errors, &self.length, "Length");
errors.make_err()
}
}
pub fn set_link_and_offset(state: &mut TrainState, path_tpc: &PathTpc) -> anyhow::Result<()> {
let idx_curr_link = path_tpc
.link_points()
.iter()
.position(|&lp| lp.offset >= state.offset)
.unwrap_or_else(|| path_tpc.link_points().len())
- 1;
state.link_idx_front = path_tpc
.link_points()
.get(idx_curr_link)
.with_context(|| format_dbg!())?
.link_idx
.idx() as u32;
state.offset_in_link = state.offset
- path_tpc
.link_points()
.get(idx_curr_link)
.with_context(|| format_dbg!())?
.offset;
Ok(())
}