From 6bf7a04024c1e5c6357c40871b008ca446faa1f5 Mon Sep 17 00:00:00 2001 From: ju6ge Date: Sun, 10 May 2026 14:04:55 +0200 Subject: [PATCH] refactor: PlantInfo structure (consistent layout) - fix: use tagged enum serialization for MoistureSensorError and PumpError - fix: flatten PlantInfo sensors to SensorTelemetry with top-level moisture_pct --- Software/MainBoard/rust/src/plant_state.rs | 70 +++++++++++++++++----- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/Software/MainBoard/rust/src/plant_state.rs b/Software/MainBoard/rust/src/plant_state.rs index 445e5fa..8c2c35f 100644 --- a/Software/MainBoard/rust/src/plant_state.rs +++ b/Software/MainBoard/rust/src/plant_state.rs @@ -1,5 +1,6 @@ use crate::hal::Moistures; use crate::{config::PlantConfig, hal::HAL, in_time_range}; +use alloc::string::String; use chrono::{DateTime, TimeDelta, Utc}; use chrono_tz::Tz; use serde::{Deserialize, Serialize}; @@ -7,12 +8,14 @@ use serde::{Deserialize, Serialize}; const MOIST_SENSOR_MAX_FREQUENCY: f32 = 70000.; // 70kHz const MOIST_SENSOR_MIN_FREQUENCY: f32 = 400.; // this is really, really dry, think like cactus levels -#[derive(Debug, PartialEq, Serialize)] +#[derive(Debug, PartialEq, Clone, Serialize)] +#[serde(tag = "kind")] pub enum MoistureSensorError { MissingMessage, NotExpectedMessage { hz: f32 }, ShortCircuit { hz: f32, max: f32 }, OpenLoop { hz: f32, min: f32 }, + BoardError { message: String }, } #[derive(Debug, PartialEq, Serialize)] @@ -46,6 +49,14 @@ impl MoistureSensorState { impl MoistureSensorState {} #[derive(Debug, PartialEq, Serialize)] +pub struct SensorTelemetry { + pub moisture_pct: Option, + pub raw_hz: Option, + pub error: Option, +} + +#[derive(Debug, PartialEq, Serialize)] +#[serde(tag = "kind")] pub enum PumpError { PumpNotWorking { failed_attempts: usize, @@ -215,7 +226,7 @@ impl PlantState { pub fn plant_moisture( &self, ) -> ( - Option, + Option, (Option<&MoistureSensorError>, Option<&MoistureSensorError>), ) { match ( @@ -223,13 +234,13 @@ impl PlantState { self.sensor_b.moisture_percent(), ) { (Some(moisture_a), Some(moisture_b)) => { - (Some(((moisture_a + moisture_b) / 2.) as u8), (None, None)) + (Some((moisture_a + moisture_b) / 2.), (None, None)) } (Some(moisture_percent), _) => { - (Some(moisture_percent as u8), (None, self.sensor_b.is_err())) + (Some(moisture_percent), (None, self.sensor_b.is_err())) } (_, Some(moisture_percent)) => { - (Some(moisture_percent as u8), (self.sensor_a.is_err(), None)) + (Some(moisture_percent), (self.sensor_a.is_err(), None)) } _ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())), } @@ -247,7 +258,7 @@ impl PlantState { if let Some(moisture_percent) = moisture_percent { if self.pump_in_timeout(plant_conf, current_time) { false - } else if moisture_percent < plant_conf.target_moisture { + } else if moisture_percent < plant_conf.target_moisture.into() { in_time_range( current_time, plant_conf.pump_hour_start, @@ -273,19 +284,21 @@ impl PlantState { &self, plant_conf: &PlantConfig, current_time: &DateTime, - ) -> PlantInfo<'_> { + ) -> PlantInfo { + let (moisture_pct, _) = self.plant_moisture(); PlantInfo { - sensor_a: &self.sensor_a, - sensor_b: &self.sensor_b, + moisture_pct, + sensor_a: Self::sensor_to_telemetry(&self.sensor_a), + sensor_b: Self::sensor_to_telemetry(&self.sensor_b), mode: plant_conf.mode, do_water: self.needs_to_be_watered(plant_conf, current_time), - dry: if let Some(moisture_percent) = self.plant_moisture().0 { - moisture_percent < plant_conf.target_moisture + dry: if let Some(moisture_percent) = moisture_pct { + moisture_percent < plant_conf.target_moisture.into() } else { false }, cooldown: self.pump_in_timeout(plant_conf, current_time), - out_of_work_hour: in_time_range( + out_of_work_hour: !in_time_range( current_time, plant_conf.pump_hour_start, plant_conf.pump_hour_end, @@ -315,15 +328,42 @@ impl PlantState { last_fertilizer_time: self.last_fertilizer_time, } } + + fn sensor_to_telemetry(sensor: &MoistureSensorState) -> SensorTelemetry { + match sensor { + MoistureSensorState::NoMessage => { + SensorTelemetry { + moisture_pct: None, + raw_hz: None, + error: None + } + } + MoistureSensorState::MoistureValue { + hz, + moisture_percent, + } => SensorTelemetry { + moisture_pct: Some(*moisture_percent), + raw_hz: Some(*hz), + error: None, + }, + MoistureSensorState::SensorError(err) => SensorTelemetry { + moisture_pct: None, + raw_hz: None, + error: Some(err.clone()), + }, + } + } } #[derive(Debug, PartialEq, Serialize)] /// State of a single plant to be tracked -pub struct PlantInfo<'a> { +pub struct PlantInfo { + /// combined plant moisture from available sensors + moisture_pct: Option, /// state of humidity sensor on bank a - sensor_a: &'a MoistureSensorState, + sensor_a: SensorTelemetry, /// state of humidity sensor on bank b - sensor_b: &'a MoistureSensorState, + sensor_b: SensorTelemetry, /// configured plant watering mode mode: PlantWateringMode, /// the plant needs to be watered