dive_deco/common/
deco_model.rs

1use crate::common::deco::{DecoCalculationError, DecoRuntime};
2use crate::common::global_types::{CeilingType, MbarPressure};
3use crate::common::ox_tox::OxTox;
4use crate::common::{AscentRatePerMinute, Cns, Gas, Otu};
5use crate::common::{Depth, Time};
6use alloc::string::String;
7use alloc::vec;
8use alloc::vec::Vec;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, PartialEq)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct ConfigValidationErr {
15    pub field: String,
16    pub reason: String,
17}
18
19impl ConfigValidationErr {
20    pub fn new(field: &str, reason: &str) -> Self {
21        Self {
22            field: String::from(field),
23            reason: String::from(reason),
24        }
25    }
26}
27
28pub trait DecoModelConfig {
29    fn validate(&self) -> Result<(), ConfigValidationErr>;
30    fn surface_pressure(&self) -> MbarPressure;
31    fn deco_ascent_rate(&self) -> AscentRatePerMinute;
32    fn ceiling_type(&self) -> CeilingType;
33    fn round_ceiling(&self) -> bool;
34}
35
36#[derive(Debug, Clone)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38pub struct DiveState {
39    pub depth: Depth,
40    pub time: Time,
41    pub gas: Gas,
42    pub ox_tox: OxTox,
43}
44
45pub trait DecoModel {
46    type ConfigType: DecoModelConfig;
47
48    // default
49    fn default() -> Self;
50
51    /// model init
52    fn new(config: Self::ConfigType) -> Self;
53
54    /// get model config
55    fn config(&self) -> Self::ConfigType;
56
57    /// get model dive state
58    fn dive_state(&self) -> DiveState;
59
60    /// record (depth: meters, time: seconds)
61    fn record(&mut self, depth: Depth, time: Time, gas: &Gas);
62
63    /// record linear ascent / descent record given travel time
64    fn record_travel(&mut self, target_depth: Depth, time: Time, gas: &Gas);
65
66    /// register linear ascent / descent record given rate
67    fn record_travel_with_rate(
68        &mut self,
69        target_depth: Depth,
70        rate: AscentRatePerMinute,
71        gas: &Gas,
72    );
73
74    /// current non decompression limit (NDL)
75    fn ndl(&self) -> Time;
76
77    /// current decompression ceiling in meters
78    fn ceiling(&self) -> Depth;
79
80    /// deco stages, TTL
81    fn deco(&self, gas_mixes: Vec<Gas>) -> Result<DecoRuntime, DecoCalculationError>;
82
83    /// is in deco check
84    fn in_deco(&self) -> bool {
85        let ceiling_type = self.config().ceiling_type();
86        match ceiling_type {
87            CeilingType::Actual => self.ceiling() > Depth::zero(),
88            CeilingType::Adaptive => {
89                let current_gas = self.dive_state().gas;
90                let runtime = self.deco(vec![current_gas]).unwrap();
91                let deco_stages = runtime.deco_stages;
92                deco_stages.len() > 1
93            }
94        }
95    }
96
97    /// central nervous system oxygen toxicity
98    fn cns(&self) -> Cns {
99        self.dive_state().ox_tox.cns()
100    }
101
102    /// pulmonary oxygen toxicity
103    fn otu(&self) -> Otu {
104        self.dive_state().ox_tox.otu()
105    }
106}