WIP introduce plant_state module

This commit is contained in:
2025-03-27 22:28:41 +01:00
parent d9c3d4e13c
commit cf31ce8d43
2 changed files with 138 additions and 85 deletions

130
rust/src/plant_state.rs Normal file
View File

@@ -0,0 +1,130 @@
use chrono::{DateTime, Utc};
use chrono_tz::Tz;
use serde::Serialize;
use crate::{config, plant_hal};
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5500; // 60kHz (500Hz margin)
const MOIST_SENSOR_MIN_FREQUENCY: u32 = 150; // this is really really dry, think like cactus levels
pub enum HumiditySensorError{
ShortCircuit{hz: f32, max: f32},
OpenLoop{hz: f32, min: f32}
}
pub enum HumiditySensorState {
Disabled,
HumidityValue{raw_hz: u32, moisture_percent: f32},
SensorError(HumiditySensorError),
BoardError(String)
}
impl HumiditySensorState {
}
pub enum PumpError {}
pub struct PumpState {
consecutive_pump_count: u32,
previous_pump: Option<DateTime<Utc>>
}
pub enum PlantError{}
pub struct PlantState {
sensor_a: HumiditySensorState,
sensor_b: HumiditySensorState,
pump: PumpState,
}
fn map_range_moisture(s: f32) -> Result<f32, HumiditySensorError> {
if s < MOIST_SENSOR_MIN_FREQUENCY {
return Err(HumiditySensorError::OpenCircuit { hz: s, min: FROM.0 });
}
if s > MOIST_SENSOR_MAX_FREQUENCY {
return Err(HumiditySensorError::ShortCircuit { hz: s, max: FROM.1 });
}
let moisture_percent = (s - MOIST_SENSOR_MIN_FREQUENCY) * 100 / (MOIST_SENSOR_MAX_FREQUENCY - MOIST_SENSOR_MIN_FREQUENCY);
return Ok(moisture_percent);
}
impl PlantState {
pub fn read_hardware_state(
plant_id: usize,
board: &mut plant_hal::PlantCtrlBoard,
config: &config::PlantConfig
) -> Self {
let sensor_a = if config.sensor_a {
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::A) {
Ok(raw) => {
match map_range_moisture(raw) {
Ok(moisture_percent) => HumiditySensorState::HumidityValue { raw_hz: raw, moisture_percent },
Err(err) => HumiditySensorState::SensorError(err),
}
},
Err(err) => HumiditySensorState::BoardError(err.to_string()),
}
} else {
HumiditySensorState::Disabled
};
let sensor_b = if config.sensor_b {
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::B) {
Ok(raw) => {
match map_range_moisture(raw) {
Ok(moisture_percent) => HumiditySensorState::HumidityValue { raw_hz: raw, moisture_percent },
Err(err) => HumiditySensorState::SensorError(err),
}
},
Err(err) => HumiditySensorState::BoardError(err.to_string()),
}
} else {
HumiditySensorState::Disabled
};
let previous_pump = board.last_pump_time(plant_id);
let consecutive_pump_count = board.consecutive_pump_count(plant_id);
Self {
sensor_a,
sensor_b,
pump: PumpState { consecutive_pump_count , previous_pump}
}
}
}
#[derive(Debug, PartialEq, Default, Serialize)]
/// State of a single plant to be tracked
pub struct PlantInfo {
/// state of humidity sensor on bank a
a: HumiditySensorState,
/// raw measured frequency value for sensor on bank a in hertz
a_raw: Option<u32>,
/// state of humidity sensor on bank b
b: HumiditySensorState,
/// raw measured frequency value for sensor on bank b in hertz
b_raw: Option<u32>,
/// configured plant watering mode
mode: config::Mode,
/// how often has the logic determined that plant should have been irrigated but wasn't
consecutive_pump_count: u32,
/// plant needs to be watered
do_water: bool,
/// is plant considerd to be dry according to settings
dry: bool,
/// is pump currently running
active: bool,
/// TODO: convert this to an Option<PumpErorr> enum for every case that can happen
pump_error: bool,
/// if pump count has increased higher than configured limit
not_effective: bool,
/// plant irrigation cooldown is active
cooldown: bool,
/// we want to irrigate but tank is empty
no_water: bool,
/// pump should not be watered at this time of day
out_of_work_hour: bool,
/// last time when pump was active
last_pump: Option<DateTime<Tz>>,
/// next time when pump should activate
next_pump: Option<DateTime<Tz>>,
}