From d2b55fdafbdbc6062ed64e9ee4b2ae0eb2dfaeae Mon Sep 17 00:00:00 2001 From: ju6ge Date: Fri, 18 Apr 2025 14:01:51 +0200 Subject: [PATCH] save compiling state during refactor --- rust/src/main.rs | 64 ++++++++++++----------- rust/src/plant_hal.rs | 10 ++-- rust/src/plant_state.rs | 112 ++++++++++++++++++++-------------------- rust/src/util.rs | 4 +- 4 files changed, 98 insertions(+), 92 deletions(-) diff --git a/rust/src/main.rs b/rust/src/main.rs index ae54c25..f2b03da 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -7,7 +7,6 @@ use anyhow::{bail, Result}; use chrono::{DateTime, Datelike, TimeDelta, Timelike, Utc}; use chrono_tz::{Europe::Berlin, Tz}; -use config::PlantWateringMode; use esp_idf_hal::delay::Delay; use esp_idf_sys::{ esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition, @@ -410,50 +409,55 @@ fn safe_main() -> anyhow::Result<()> { let mut plantstate: [PlantState; PLANT_COUNT] = core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board, &config.plants[i])); - let pump_required = plantstate.iter().any(|it| it.needs_to_be_watered(&config.plants[i], &timezone_time)) && !water_frozen; + let pump_required = plantstate + .iter() + .zip(&config.plants) + .any(|(it, conf)| it.needs_to_be_watered(&conf, &timezone_time)) + && !water_frozen; if pump_required { log(log::LogMessage::EnableMain, dry_run as u32, 0, "", ""); if !dry_run { - board.any_pump(true)?; + board.any_pump(true)?; // what does this do? Does it need to be reset? } - for plant in 0..PLANT_COUNT { - let state = &mut plantstate[plant]; - if state.do_water { - let plant_config = &config.plants[plant]; - state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1; - board.store_consecutive_pump_count(plant, state.consecutive_pump_count); - if state.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 { - log( - log::LogMessage::ConsecutivePumpCountLimit, - state.consecutive_pump_count as u32, - plant_config.max_consecutive_pump_count as u32, - &plant.to_string(), - "", - ); - state.not_effective = true; - board.fault(plant, true); - } + for (plant_id, (state, plant_config)) in plantstate.iter().zip(&config.plants).enumerate() { + if state.needs_to_be_watered(&plant_config, &timezone_time) { + let pump_count = board.consecutive_pump_count(plant_id) + 1; + board.store_consecutive_pump_count(plant_id, pump_count); + //TODO(judge) where to put this? + //if state.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 { + // log( + // log::LogMessage::ConsecutivePumpCountLimit, + // state.consecutive_pump_count as u32, + // plant_config.max_consecutive_pump_count as u32, + // &plant.to_string(), + // "", + // ); + // state.not_effective = true; + // board.fault(plant, true); + //} log( log::LogMessage::PumpPlant, - (plant + 1) as u32, + (plant_id + 1) as u32, plant_config.pump_time_s as u32, &dry_run.to_string(), "", ); - board.store_last_pump_time(plant, cur); - board.last_pump_time(plant); - state.active = true; + board.store_last_pump_time(plant_id, cur); + board.last_pump_time(plant_id); + //state.active = true; if !dry_run { - board.pump(plant, true)?; - for _ in 0..plant_config.pump_time_s { - Delay::new_default().delay_ms(1000); - } - board.pump(plant, false)?; + board.pump(plant_id, true)?; + Delay::new_default().delay_ms(1000*plant_config.pump_time_s as u32); + board.pump(plant_id, false)?; } + } else if !state.pump_in_timeout(&plant_config, &timezone_time){ + // plant does not need to be watered and is not in timeout + // -> reset consecutive pump count + board.store_consecutive_pump_count(plant_id, 0); } } } - update_plant_state(&mut plantstate, &mut board, &config); + //update_plant_state(&mut plantstate, &mut board, &config); let is_day = board.is_day(); let state_of_charge = board.state_charge_percent().unwrap_or(0); diff --git a/rust/src/plant_hal.rs b/rust/src/plant_hal.rs index 9a4829e..412391b 100644 --- a/rust/src/plant_hal.rs +++ b/rust/src/plant_hal.rs @@ -603,7 +603,7 @@ impl PlantCtrlBoard<'_> { self.time() } - pub fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result { + pub fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result { let sensor_channel = match sensor { Sensor::A => match plant { 0 => SENSOR_A_1, @@ -629,7 +629,7 @@ impl PlantCtrlBoard<'_> { }, }; - let mut results = [0_u32; REPEAT_MOIST_MEASURE]; + let mut results = [0_f32; REPEAT_MOIST_MEASURE]; for repeat in 0..REPEAT_MOIST_MEASURE { self.signal_counter.counter_pause()?; self.signal_counter.counter_clear()?; @@ -644,7 +644,7 @@ impl PlantCtrlBoard<'_> { .unwrap(); let delay = Delay::new_default(); - let measurement = 100; + let measurement = 100; // TODO what is this scaling factor? what is its purpose? let factor = 1000 as f32 / measurement as f32; //give some time to stabilize @@ -658,7 +658,7 @@ impl PlantCtrlBoard<'_> { .unwrap(); delay.delay_ms(10); let unscaled = self.signal_counter.get_counter_value()? as i32; - let hz = (unscaled as f32 * factor) as u32; + let hz = unscaled as f32 * factor; log( LogMessage::RawMeasure, unscaled as u32, @@ -668,7 +668,7 @@ impl PlantCtrlBoard<'_> { ); results[repeat] = hz; } - results.sort(); + results.sort_by(|a,b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord let mid = results.len() / 2; diff --git a/rust/src/plant_state.rs b/rust/src/plant_state.rs index 80bcf1a..aa2078a 100644 --- a/rust/src/plant_state.rs +++ b/rust/src/plant_state.rs @@ -8,18 +8,19 @@ use crate::{ plant_hal::{self, PLANT_COUNT}, }; -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 +const MOIST_SENSOR_MAX_FREQUENCY: f32 = 5500.; // 60kHz (500Hz margin) +const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really really dry, think like cactus levels +#[derive(Debug, PartialEq, Serialize)] pub enum HumiditySensorError { ShortCircuit { hz: f32, max: f32 }, OpenLoop { hz: f32, min: f32 }, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum HumiditySensorState { Disabled, - HumidityValue { raw_hz: u32, moisture_percent: f32 }, + HumidityValue { raw_hz: f32, moisture_percent: f32 }, SensorError(HumiditySensorError), BoardError(String), } @@ -35,7 +36,7 @@ impl HumiditySensorState { moisture_percent, } = self { - Some(moisture_percent) + Some(*moisture_percent) } else { None } @@ -44,7 +45,7 @@ impl HumiditySensorState { impl HumiditySensorState {} -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize)] pub enum PumpError { PumpNotWorking { failed_attempts: usize, @@ -52,6 +53,7 @@ pub enum PumpError { }, } +#[derive(Debug, Serialize)] pub struct PumpState { consecutive_pump_count: u32, previous_pump: Option>, @@ -74,7 +76,7 @@ pub struct PlantState { fn map_range_moisture(s: f32) -> Result { if s < MOIST_SENSOR_MIN_FREQUENCY { - return Err(HumiditySensorError::OpenCircuit { + return Err(HumiditySensorError::OpenLoop { hz: s, min: MOIST_SENSOR_MIN_FREQUENCY, }); @@ -85,7 +87,7 @@ fn map_range_moisture(s: f32) -> Result { max: MOIST_SENSOR_MAX_FREQUENCY, }); } - let moisture_percent = (s - MOIST_SENSOR_MIN_FREQUENCY) * 100 + let moisture_percent = (s - MOIST_SENSOR_MIN_FREQUENCY) * 100.0 / (MOIST_SENSOR_MAX_FREQUENCY - MOIST_SENSOR_MIN_FREQUENCY); return Ok(moisture_percent); @@ -165,7 +167,7 @@ impl PlantState { PlantWateringMode::TargetMoisture => { let moisture_percent = match ( self.sensor_a.moisture_percent(), - &self.sensor_b.moisture_percent(), + self.sensor_b.moisture_percent(), ) { (Some(moisture_a), Some(moisture_b)) => (moisture_a + moisture_b) / 2., (Some(moisture_percent), _) => moisture_percent, @@ -196,51 +198,51 @@ impl PlantState { } } -fn update_plant_state( - plantstate: &mut [PlantInfo; PLANT_COUNT], - board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, - config: &PlantControllerConfig, -) { - for plant in 0..PLANT_COUNT { - let state = &plantstate[plant]; - let plant_config = &config.plants[plant]; - - let mode = format!("{:?}", plant_config.mode); - - let plant_dto = PlantStateMQTT { - a: &sensor_to_string( - &state.a, - &state.sensor_error_a, - plant_config.mode != PlantWateringMode::OFF, - ), - a_raw: &state.a_raw.unwrap_or(0).to_string(), - b: &sensor_to_string(&state.b, &state.sensor_error_b, plant_config.sensor_b), - b_raw: &state.b_raw.unwrap_or(0).to_string(), - active: state.active, - mode: &mode, - last_pump: &time_to_string_utc(board.last_pump_time(plant)), - next_pump: &time_to_string(state.next_pump), - consecutive_pump_count: state.consecutive_pump_count, - cooldown: state.cooldown, - dry: state.dry, - not_effective: state.not_effective, - out_of_work_hour: state.out_of_work_hour, - pump_error: state.pump_error, - }; - - match serde_json::to_string(&plant_dto) { - Ok(state) => { - let plant_topic = format!("/plant{}", plant + 1); - let _ = board.mqtt_publish(&config, &plant_topic, state.as_bytes()); - //reduce speed as else messages will be dropped - Delay::new_default().delay_ms(200); - } - Err(err) => { - println!("Error publishing lightstate {}", err); - } - }; - } -} +//fn update_plant_state( +// plantstate: &mut [PlantInfo; PLANT_COUNT], +// board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, +// config: &PlantControllerConfig, +//) { +// for plant in 0..PLANT_COUNT { +// let state = &plantstate[plant]; +// let plant_config = &config.plants[plant]; +// +// let mode = format!("{:?}", plant_config.mode); +// +// let plant_dto = PlantStateMQTT { +// a: &sensor_to_string( +// &state.a, +// &state.sensor_error_a, +// plant_config.mode != PlantWateringMode::OFF, +// ), +// a_raw: &state.a_raw.unwrap_or(0).to_string(), +// b: &sensor_to_string(&state.b, &state.sensor_error_b, plant_config.sensor_b), +// b_raw: &state.b_raw.unwrap_or(0).to_string(), +// active: state.active, +// mode: &mode, +// last_pump: &time_to_string_utc(board.last_pump_time(plant)), +// next_pump: &time_to_string(state.next_pump), +// consecutive_pump_count: state.consecutive_pump_count, +// cooldown: state.cooldown, +// dry: state.dry, +// not_effective: state.not_effective, +// out_of_work_hour: state.out_of_work_hour, +// pump_error: state.pump_error, +// }; +// +// match serde_json::to_string(&plant_dto) { +// Ok(state) => { +// let plant_topic = format!("/plant{}", plant + 1); +// let _ = board.mqtt_publish(&config, &plant_topic, state.as_bytes()); +// //reduce speed as else messages will be dropped +// Delay::new_default().delay_ms(200); +// } +// Err(err) => { +// println!("Error publishing lightstate {}", err); +// } +// }; +// } +//} #[derive(Debug, PartialEq, Serialize)] /// State of a single plant to be tracked @@ -250,7 +252,7 @@ pub struct PlantInfo { /// state of humidity sensor on bank b sensor_b: HumiditySensorState, /// configured plant watering mode - mode: config::PlantWateringMode, + mode: PlantWateringMode, /// plant needs to be watered do_water: bool, /// is plant considerd to be dry according to settings diff --git a/rust/src/util.rs b/rust/src/util.rs index a26fee4..b14fa95 100644 --- a/rust/src/util.rs +++ b/rust/src/util.rs @@ -1,10 +1,10 @@ pub trait LimitPrecision { - fn to_precision(self, presision: i32) -> self; + fn to_precision(self, presision: i32) -> Self; } impl LimitPrecision for f32 { - fn to_precision(self, precision: i32) -> self { + fn to_precision(self, precision: i32) -> Self { (self * (10_f32).powi(precision)).round() / (10_f32).powi(precision) } }