PlantCtrl/rust/src/tank.rs

147 lines
4.8 KiB
Rust

use crate::config::{self, PlantControllerConfig, TankConfig};
const OPEN_TANK_VOLTAGE: f32 = 3.0;
#[derive(Debug, PartialEq, Default)]
/// State data for water tank
///
/// TODO unify with TankStateMQTT
pub struct TankInfo {
/// is there enough water in the tank
enough_water: bool,
/// warning that water needs to be refilled soon
warn_level: bool,
/// estimation how many ml are still in tank
left_ml: u32,
/// if there is was an issue with the water level sensor
/// TODO merge with left_ml as Result<u32, error_type>
sensor_error: bool,
/// raw water sensor value
raw: u16,
}
pub enum TankError {
SensorDisabled,
SensorMissing(f32),
SensorValueError { value: f32, min: f32, max: f32 },
}
pub enum TankState {
TankSensorPresent(u16),
TankSensorDisabled,
}
fn raw_volatge_to_divider_percent(raw_value_mv: u16) -> Result<f32, TankError> {
if raw_value_mv > OPEN_TANK_VOLTAGE {
return Err(TankError::SensorMissing(raw_value_mv));
}
let r2 = raw_value_mv * 50.0 / (3.3 - raw_value_mv);
let mut percent = r2 / 190_f32 * 100_f32;
percent = percent.clamp(0.0, 100.0);
// TODO(judge) move this to a sensible place
//log(
//LogMessage::SensorTankRaw,
//raw_value_mv as u32,
//percent as u32,
//"",
//"",
//);
Ok(percent)
}
fn raw_voltage_to_tank_fill_percent(
raw_value_mv: u16,
config: &TankConfig,
) -> Result<f32, TankError> {
let divider_percent = raw_volatge_to_divider_percent(raw_value_mv)?;
if s < config.tank_empty_percent || s > config.tank_full_percent {
return Err(TankError::SensorValueError {
value: divider_percent,
min: config.tank_empty_percent,
max: config.tank_full_percent,
});
}
Ok((divider_percent - config.tank_empty_percent) * 100
/ (config.tank_full_percent - config.tank_empty_percent))
}
impl TankState {
pub fn left_ml(&self, config: &TankConfig) -> Result<f32, TankError> {
match self {
TankState::TankSensorDisabled => Err(TankError::SensorDisabled),
TankState::TankSensorPresent(raw_value_mv) => {
let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config)?;
//TODO(judge) move logging to more sensible place
//println!(
//"Tank sensor returned mv {} as {}% leaving {} ml useable",
//rv.raw, percent as u8, rv.left_ml
//);
Ok(config.tank_useable_ml as f32 * tank_fill_percent / 100.)
}
}
}
pub fn enough_water(&self, config: &TankConfig) -> Result<bool, TankError> {
match self {
TankState::TankSensorDisabled => Err(TankError::SensorDisabled),
TankState::TankSensorPresent(raw_value_mv) => {
let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config)?;
if tank_fill_percent > config.tank_empty_percent {
//TODO(judge) move logging to more sensible place
//println!(
//"Enough water, current percent is {}, minimum empty level is {}",
//percent as u8, config.tank.tank_empty_percent
//);
Ok(true)
} else {
Ok(false)
}
},
}
}
pub fn warn_level(&self, config: &TankConfig) -> Result<bool, TankError> {
match self {
TankState::TankSensorDisabled => Err(TankError::SensorDisabled),
TankState::TankSensorPresent(raw_value_mv) => {
let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config)?;
if tank_fill_percent < config.tank_warn_percent {
//TODO(judge) move logging to more sensible place
//println!(
//"Low water, current percent is {}, minimum warn level is {}",
//percent as u8, config.tank.tank_warn_percent
//);
// TODO(judge) move board fault setting
// board.general_fault(true);
Ok(true)
} else {
Ok(false)
}
},
}
}
}
#[derive(Serialize)]
pub struct TankStateMQTT {
enough_water: bool,
warn_level: bool,
left_ml: u32,
sensor_error: bool,
raw: u16,
water_frozen: String,
}
pub fn determine_tank_state(
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
config: &PlantControllerConfig,
) -> TankState {
if config.tank.tank_sensor_enabled {
let raw_sensor_value_mv = board.tank_sensor_voltage();
TankState::TankSensorPresent(raw_sensor_value_mv)
} else {
TankState::TankSensorDisabled
}
}