use serde::{Deserialize, Serialize};
use crate::consist::locomotive::loco_sim::PowerTrace;
use crate::consist::Consist;
use crate::consist::LocoTrait;
use crate::imports::*;
#[serde_api]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[cfg_attr(feature = "pyo3", pyclass(module = "altrios", subclass, eq))]
pub struct ConsistSimulation {
pub loco_con: Consist,
pub power_trace: PowerTrace,
}
#[pyo3_api]
impl ConsistSimulation {
#[new]
#[pyo3(signature = (consist, power_trace, save_interval=None))]
fn __new__(consist: Consist, power_trace: PowerTrace, save_interval: Option<usize>) -> Self {
Self::new(consist, power_trace, save_interval)
}
#[pyo3(name = "walk")]
fn walk_py(&mut self) -> anyhow::Result<()> {
self.walk()
}
#[pyo3(name = "step")]
fn step_py(&mut self) -> anyhow::Result<()> {
self.step(|| format_dbg!())
}
#[pyo3(name = "set_save_interval")]
#[pyo3(signature = (save_interval=None))]
fn set_save_interval_py(&mut self, save_interval: Option<usize>) {
self.set_save_interval(save_interval);
}
#[pyo3(name = "get_save_interval")]
fn get_save_interval_py(&self) -> anyhow::Result<Option<usize>> {
Ok(self.loco_con.get_save_interval())
}
#[pyo3(name = "trim_failed_steps")]
fn trim_failed_steps_py(&mut self) -> anyhow::Result<()> {
self.trim_failed_steps()?;
Ok(())
}
}
impl ConsistSimulation {
pub fn new(consist: Consist, power_trace: PowerTrace, save_interval: Option<usize>) -> Self {
let mut consist_sim = Self {
loco_con: consist,
power_trace,
};
consist_sim.loco_con.set_save_interval(save_interval);
consist_sim
}
pub fn trim_failed_steps(&mut self) -> anyhow::Result<()> {
if *self.loco_con.state.i.get_fresh(|| format_dbg!())? <= 1 {
bail!("`walk` method has not proceeded past first time step.")
}
self.power_trace.trim(
None,
Some(*self.loco_con.state.i.get_fresh(|| format_dbg!())?),
)?;
Ok(())
}
pub fn set_save_interval(&mut self, save_interval: Option<usize>) {
self.loco_con.set_save_interval(save_interval);
}
pub fn solve_step(&mut self) -> anyhow::Result<()> {
self.loco_con
.state
.pwr_cat_lim
.mark_fresh(|| format_dbg!())?;
self.loco_con
.set_pwr_aux(Some(true))
.with_context(|| format_dbg!())?;
let train_mass = self.power_trace.train_mass;
let i = *self.loco_con.state.i.get_fresh(|| format_dbg!())?;
let train_speed = if !self.power_trace.train_speed.is_empty() {
Some(self.power_trace.train_speed[i])
} else {
None
};
let dt = self.power_trace.dt_at_i(i).with_context(|| format_dbg!())?;
self.loco_con
.set_curr_pwr_max_out(None, None, train_mass, train_speed, dt)
.with_context(|| format_dbg!())?;
self.solve_energy_consumption(
self.power_trace.pwr[*self.loco_con.state.i.get_fresh(|| format_dbg!())?],
train_mass,
train_speed,
dt,
)
.with_context(|| format_dbg!())?;
self.set_cumulative(dt, || format_dbg!())?;
Ok(())
}
pub fn walk(&mut self) -> anyhow::Result<()> {
self.save_state(|| format_dbg!())?;
loop {
if *self.loco_con.state.i.get_fresh(|| format_dbg!())? > self.power_trace.len() - 2 {
break;
}
self.step(|| format_dbg!())?;
}
Ok(())
}
pub fn solve_energy_consumption(
&mut self,
pwr_out_req: si::Power,
train_mass: Option<si::Mass>,
train_speed: Option<si::Velocity>,
dt: si::Time,
) -> anyhow::Result<()> {
self.loco_con.solve_energy_consumption(
pwr_out_req,
train_mass,
train_speed,
dt,
Some(true),
)?;
Ok(())
}
}
impl StateMethods for ConsistSimulation {}
impl SetCumulative for ConsistSimulation {
fn set_cumulative<F: Fn() -> String>(&mut self, dt: si::Time, loc: F) -> anyhow::Result<()> {
self.loco_con
.set_cumulative(dt, || format!("{}\n{}", loc(), format_dbg!()))?;
Ok(())
}
}
impl CheckAndResetState for ConsistSimulation {
fn check_and_reset<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()> {
self.loco_con
.check_and_reset(|| format!("{}\n{}", loc(), format_dbg!()))?;
Ok(())
}
}
impl Step for ConsistSimulation {
fn step<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()> {
self.check_and_reset(|| format!("{}\n{}", loc(), format_dbg!()))?;
self.loco_con
.step(|| format!("{}\n{}", loc(), format_dbg!()))?;
let i = *self
.loco_con
.state
.i
.get_fresh(|| format!("{}\n{}", loc(), format_dbg!()))?;
self.solve_step()
.with_context(|| format!("{}\ntime step: {}", loc(), i))?;
self.save_state(|| format!("{}\n{}", loc(), format_dbg!()))?;
Ok(())
}
}
impl SaveState for ConsistSimulation {
fn save_state<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()> {
self.loco_con
.save_state(|| format!("{}\n{}", loc(), format_dbg!()))
}
}
impl Init for ConsistSimulation {
fn init(&mut self) -> Result<(), Error> {
self.loco_con.init()?;
self.power_trace.init()?;
Ok(())
}
}
impl SerdeAPI for ConsistSimulation {}
impl Default for ConsistSimulation {
fn default() -> Self {
let mut consist_sim = Self::new(Consist::default(), PowerTrace::default(), Some(1));
consist_sim.init().unwrap();
consist_sim
}
}
#[cfg(test)]
mod tests {
use super::{Consist, ConsistSimulation};
use crate::consist::locomotive::loco_sim::PowerTrace;
#[test]
fn test_consist_sim() {
let consist = Consist::default();
let pt = PowerTrace::default();
let mut consist_sim = ConsistSimulation::new(consist, pt, None);
consist_sim.walk().unwrap();
}
}