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
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use crate::hal::Moistures;
|
use crate::hal::Moistures;
|
||||||
use crate::{config::PlantConfig, hal::HAL, in_time_range};
|
use crate::{config::PlantConfig, hal::HAL, in_time_range};
|
||||||
|
use alloc::string::String;
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -7,12 +8,14 @@ use serde::{Deserialize, Serialize};
|
|||||||
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 70000.; // 70kHz
|
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 70000.; // 70kHz
|
||||||
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 400.; // this is really, really dry, think like cactus levels
|
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 {
|
pub enum MoistureSensorError {
|
||||||
MissingMessage,
|
MissingMessage,
|
||||||
NotExpectedMessage { hz: f32 },
|
NotExpectedMessage { hz: f32 },
|
||||||
ShortCircuit { hz: f32, max: f32 },
|
ShortCircuit { hz: f32, max: f32 },
|
||||||
OpenLoop { hz: f32, min: f32 },
|
OpenLoop { hz: f32, min: f32 },
|
||||||
|
BoardError { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
@@ -46,6 +49,14 @@ impl MoistureSensorState {
|
|||||||
impl MoistureSensorState {}
|
impl MoistureSensorState {}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
|
pub struct SensorTelemetry {
|
||||||
|
pub moisture_pct: Option<f32>,
|
||||||
|
pub raw_hz: Option<f32>,
|
||||||
|
pub error: Option<MoistureSensorError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
pub enum PumpError {
|
pub enum PumpError {
|
||||||
PumpNotWorking {
|
PumpNotWorking {
|
||||||
failed_attempts: usize,
|
failed_attempts: usize,
|
||||||
@@ -215,7 +226,7 @@ impl PlantState {
|
|||||||
pub fn plant_moisture(
|
pub fn plant_moisture(
|
||||||
&self,
|
&self,
|
||||||
) -> (
|
) -> (
|
||||||
Option<u8>,
|
Option<f32>,
|
||||||
(Option<&MoistureSensorError>, Option<&MoistureSensorError>),
|
(Option<&MoistureSensorError>, Option<&MoistureSensorError>),
|
||||||
) {
|
) {
|
||||||
match (
|
match (
|
||||||
@@ -223,13 +234,13 @@ impl PlantState {
|
|||||||
self.sensor_b.moisture_percent(),
|
self.sensor_b.moisture_percent(),
|
||||||
) {
|
) {
|
||||||
(Some(moisture_a), Some(moisture_b)) => {
|
(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), _) => {
|
||||||
(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)) => {
|
||||||
(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())),
|
_ => (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 let Some(moisture_percent) = moisture_percent {
|
||||||
if self.pump_in_timeout(plant_conf, current_time) {
|
if self.pump_in_timeout(plant_conf, current_time) {
|
||||||
false
|
false
|
||||||
} else if moisture_percent < plant_conf.target_moisture {
|
} else if moisture_percent < plant_conf.target_moisture.into() {
|
||||||
in_time_range(
|
in_time_range(
|
||||||
current_time,
|
current_time,
|
||||||
plant_conf.pump_hour_start,
|
plant_conf.pump_hour_start,
|
||||||
@@ -273,19 +284,21 @@ impl PlantState {
|
|||||||
&self,
|
&self,
|
||||||
plant_conf: &PlantConfig,
|
plant_conf: &PlantConfig,
|
||||||
current_time: &DateTime<Tz>,
|
current_time: &DateTime<Tz>,
|
||||||
) -> PlantInfo<'_> {
|
) -> PlantInfo {
|
||||||
|
let (moisture_pct, _) = self.plant_moisture();
|
||||||
PlantInfo {
|
PlantInfo {
|
||||||
sensor_a: &self.sensor_a,
|
moisture_pct,
|
||||||
sensor_b: &self.sensor_b,
|
sensor_a: Self::sensor_to_telemetry(&self.sensor_a),
|
||||||
|
sensor_b: Self::sensor_to_telemetry(&self.sensor_b),
|
||||||
mode: plant_conf.mode,
|
mode: plant_conf.mode,
|
||||||
do_water: self.needs_to_be_watered(plant_conf, current_time),
|
do_water: self.needs_to_be_watered(plant_conf, current_time),
|
||||||
dry: if let Some(moisture_percent) = self.plant_moisture().0 {
|
dry: if let Some(moisture_percent) = moisture_pct {
|
||||||
moisture_percent < plant_conf.target_moisture
|
moisture_percent < plant_conf.target_moisture.into()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
cooldown: self.pump_in_timeout(plant_conf, current_time),
|
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,
|
current_time,
|
||||||
plant_conf.pump_hour_start,
|
plant_conf.pump_hour_start,
|
||||||
plant_conf.pump_hour_end,
|
plant_conf.pump_hour_end,
|
||||||
@@ -315,15 +328,42 @@ impl PlantState {
|
|||||||
last_fertilizer_time: self.last_fertilizer_time,
|
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)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
/// State of a single plant to be tracked
|
/// State of a single plant to be tracked
|
||||||
pub struct PlantInfo<'a> {
|
pub struct PlantInfo {
|
||||||
|
/// combined plant moisture from available sensors
|
||||||
|
moisture_pct: Option<f32>,
|
||||||
/// state of humidity sensor on bank a
|
/// state of humidity sensor on bank a
|
||||||
sensor_a: &'a MoistureSensorState,
|
sensor_a: SensorTelemetry,
|
||||||
/// state of humidity sensor on bank b
|
/// state of humidity sensor on bank b
|
||||||
sensor_b: &'a MoistureSensorState,
|
sensor_b: SensorTelemetry,
|
||||||
/// configured plant watering mode
|
/// configured plant watering mode
|
||||||
mode: PlantWateringMode,
|
mode: PlantWateringMode,
|
||||||
/// the plant needs to be watered
|
/// the plant needs to be watered
|
||||||
|
|||||||
Reference in New Issue
Block a user