use crate::imports::*;
use crate::track::PathTpc;
#[serde_api]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, HistoryVec)]
#[cfg_attr(feature = "pyo3", pyclass(module = "altrios", subclass, eq))]
pub struct InitTrainState {
pub time: TrackedState<si::Time>,
pub offset: TrackedState<si::Length>,
pub speed: TrackedState<si::Velocity>,
}
#[pyo3_api]
impl InitTrainState {
#[new]
#[pyo3(signature = (
time_seconds=None,
offset_meters=None,
speed_meters_per_second=None,
))]
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),
)
}
}
impl Init for InitTrainState {}
impl SerdeAPI for InitTrainState {}
impl Default for InitTrainState {
fn default() -> Self {
Self {
time: TrackedState::new(si::Time::ZERO),
offset: TrackedState::new(f64::NAN * uc::M),
speed: TrackedState::new(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: TrackedState::new(
time.unwrap_or(*base.time.get_fresh(|| format_dbg!()).unwrap()),
),
offset: TrackedState::new(
offset.unwrap_or(*base.offset.get_fresh(|| format_dbg!()).unwrap()),
),
speed: TrackedState::new(
speed.unwrap_or(*base.speed.get_fresh(|| format_dbg!()).unwrap()),
),
}
}
}
#[serde_api]
#[derive(
Debug, Clone, Serialize, Deserialize, HistoryVec, PartialEq, StateMethods, SetCumulative,
)]
#[cfg_attr(feature = "pyo3", pyclass(module = "altrios", subclass, eq))]
pub struct TrainState {
pub time: TrackedState<si::Time>,
pub i: TrackedState<usize>,
pub offset: TrackedState<si::Length>,
pub offset_back: TrackedState<si::Length>,
pub total_dist: TrackedState<si::Length>,
pub link_idx_front: TrackedState<u32>,
pub link_idx_back: TrackedState<u32>,
pub offset_in_link: TrackedState<si::Length>,
pub speed: TrackedState<si::Velocity>,
pub speed_limit: TrackedState<si::Velocity>,
pub speed_target: TrackedState<si::Velocity>,
pub dt: TrackedState<si::Time>,
pub length: TrackedState<si::Length>,
pub mass_static: TrackedState<si::Mass>,
pub mass_rot: TrackedState<si::Mass>,
pub mass_freight: TrackedState<si::Mass>,
pub weight_static: TrackedState<si::Force>,
pub res_rolling: TrackedState<si::Force>,
pub res_bearing: TrackedState<si::Force>,
pub res_davis_b: TrackedState<si::Force>,
pub res_aero: TrackedState<si::Force>,
pub res_grade: TrackedState<si::Force>,
pub res_curve: TrackedState<si::Force>,
pub grade_front: TrackedState<si::Ratio>,
pub grade_back: TrackedState<si::Ratio>,
pub elev_front: TrackedState<si::Length>,
pub elev_back: TrackedState<si::Length>,
pub pwr_res: TrackedState<si::Power>,
pub pwr_accel: TrackedState<si::Power>,
pub pwr_whl_out: TrackedState<si::Power>,
pub energy_whl_out: TrackedState<si::Energy>,
pub energy_whl_out_pos: TrackedState<si::Energy>,
pub energy_whl_out_neg: TrackedState<si::Energy>,
}
impl Init for TrainState {}
impl SerdeAPI for TrainState {}
impl Default for TrainState {
fn default() -> Self {
Self {
time: Default::default(),
i: Default::default(),
offset: Default::default(),
offset_back: Default::default(),
total_dist: Default::default(),
link_idx_front: Default::default(),
link_idx_back: Default::default(),
offset_in_link: Default::default(),
speed: Default::default(),
speed_limit: Default::default(),
dt: TrackedState::new(uc::S),
length: Default::default(),
mass_static: Default::default(),
mass_rot: Default::default(),
mass_freight: Default::default(),
elev_front: Default::default(),
elev_back: Default::default(),
energy_whl_out: Default::default(),
grade_front: Default::default(),
grade_back: 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 Mass for TrainState {
fn mass(&self) -> anyhow::Result<Option<si::Mass>> {
self.derived_mass()
}
fn set_mass(
&mut self,
_new_mass: Option<si::Mass>,
_side_effect: MassSideEffect,
) -> anyhow::Result<()> {
bail!("`set_mass` is not enabled for `TrainState`")
}
fn derived_mass(&self) -> anyhow::Result<Option<si::Mass>> {
Ok(Some(*self.mass_static.get_unchecked(|| format_dbg!())?))
}
fn expunge_mass_fields(&mut self) {}
}
impl TrainState {
#[allow(clippy::too_many_arguments)]
pub fn new(
length: si::Length,
mass_static: si::Mass,
mass_rot: 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
.get_fresh(|| format_dbg!())
.unwrap()
.max(length);
Self {
time: init_train_state.time,
i: Default::default(),
offset: TrackedState::new(offset),
offset_back: TrackedState::new(offset - length),
total_dist: TrackedState::new(si::Length::ZERO),
speed: init_train_state.speed.clone(),
speed_limit: init_train_state.speed,
length: TrackedState::new(length),
mass_static: TrackedState::new(mass_static),
mass_rot: TrackedState::new(mass_rot),
mass_freight: TrackedState::new(mass_freight),
..Self::default()
}
}
pub fn res_net(&self) -> anyhow::Result<si::Force> {
Ok(*self.res_rolling.get_fresh(|| format_dbg!())?
+ *self.res_bearing.get_fresh(|| format_dbg!())?
+ *self.res_davis_b.get_fresh(|| format_dbg!())?
+ *self.res_aero.get_fresh(|| format_dbg!())?
+ *self.res_grade.get_fresh(|| format_dbg!())?
+ *self.res_curve.get_fresh(|| format_dbg!())?)
}
pub fn mass_compound(&self) -> anyhow::Result<si::Mass> {
Ok(self
.mass()
.with_context(|| format_dbg!())? .with_context(|| format!("{}\nExpected `Some`", format_dbg!()))? + *self.mass_rot.get_unchecked(|| format_dbg!())?)
}
}
impl Valid for TrainState {
fn valid() -> Self {
Self {
length: TrackedState::new(2000.0 * uc::M),
offset: TrackedState::new(2000.0 * uc::M),
offset_back: TrackedState::new(si::Length::ZERO),
mass_static: TrackedState::new(6000.0 * uc::TON),
mass_rot: TrackedState::new(200.0 * uc::TON),
dt: TrackedState::new(uc::S),
..Self::default()
}
}
}
impl ObjState for TrainState {
fn validate(&self) -> ValidationResults {
let mut errors = ValidationErrors::new();
if let Err(err) = self.mass_static.get_fresh(|| format_dbg!()) {
errors.push(err);
return errors.make_err();
}
if let Err(err) = self.length.get_fresh(|| format_dbg!()) {
errors.push(err);
return errors.make_err();
}
si_chk_num_gtz_fin(
&mut errors,
self.mass_static.get_fresh(|| format_dbg!()).unwrap(),
"Mass static",
);
si_chk_num_gtz_fin(
&mut errors,
self.length.get_fresh(|| format_dbg!()).unwrap(),
"Length",
);
errors.make_err()
}
}
pub fn set_link_and_offset(state: &mut TrainState, path_tpc: &PathTpc) -> anyhow::Result<()> {
let offset = *state.offset.get_stale(|| format_dbg!())?;
let idx_curr_link = path_tpc
.link_points()
.iter()
.position(|&lp| lp.offset > offset)
.unwrap_or_else(|| path_tpc.link_points().len())
- 1;
let link_point = path_tpc
.link_points()
.get(idx_curr_link)
.with_context(|| format_dbg!())?;
state
.link_idx_front
.update(link_point.link_idx.idx() as u32, || format_dbg!())?;
state.offset_in_link.update(
*state.offset.get_stale(|| format_dbg!())? - link_point.offset,
|| format_dbg!(),
)?;
let offset_back = *state.offset_back.get_fresh(|| format_dbg!())?;
let idx_back_link = path_tpc
.link_points()
.iter()
.position(|&lp| lp.offset > offset_back)
.unwrap_or_else(|| path_tpc.link_points().len())
- 1;
state.link_idx_back.update(
path_tpc
.link_points()
.get(idx_back_link)
.with_context(|| format_dbg!())?
.link_idx
.idx() as u32,
|| format_dbg!(),
)?;
Ok(())
}