Migrate serialization from Bincode to Postcard
- Replaced Bincode with Postcard for serialization/deserialization across configs and save operations. - Simplified struct derives by removing `bincode`-specific traits. - Updated `Cargo.toml` and `Cargo.lock` to include `postcard` and dependencies. - Added padding stripping for deserialization and improved error handling. - Adjusted serialization logic in `savegame_manager.rs` and related modules.
This commit is contained in:
50
Software/MainBoard/rust/Cargo.lock
generated
50
Software/MainBoard/rust/Cargo.lock
generated
@@ -83,7 +83,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode_derive",
|
"bincode_derive",
|
||||||
"serde",
|
|
||||||
"unty",
|
"unty",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -225,6 +224,15 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cobs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-default"
|
name = "const-default"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -746,6 +754,12 @@ dependencies = [
|
|||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-io"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-io"
|
name = "embedded-io"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@@ -1884,7 +1898,6 @@ name = "plant-ctrl2"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"canapi",
|
"canapi",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1926,6 +1939,7 @@ dependencies = [
|
|||||||
"option-lock",
|
"option-lock",
|
||||||
"pca9535",
|
"pca9535",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
|
"postcard",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sntpc",
|
"sntpc",
|
||||||
@@ -1971,6 +1985,18 @@ dependencies = [
|
|||||||
"syn 2.0.110",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "postcard"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||||
|
dependencies = [
|
||||||
|
"cobs",
|
||||||
|
"embedded-io 0.4.0",
|
||||||
|
"embedded-io 0.6.1",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -2404,6 +2430,26 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.110",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ embedded-savegame = { version = "0.3.0" }
|
|||||||
# Serialization / codecs
|
# Serialization / codecs
|
||||||
serde = { version = "1.0.228", features = ["derive", "alloc"], default-features = false }
|
serde = { version = "1.0.228", features = ["derive", "alloc"], default-features = false }
|
||||||
serde_json = { version = "1.0.145", default-features = false, features = ["alloc"] }
|
serde_json = { version = "1.0.145", default-features = false, features = ["alloc"] }
|
||||||
bincode = { version = "2.0.1", default-features = false, features = ["derive", "alloc"] }
|
postcard = { version = "1.1.3", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
# Time and time zones
|
# Time and time zones
|
||||||
chrono = { version = "0.4.42", default-features = false, features = ["iana-time-zone", "alloc", "serde"] }
|
chrono = { version = "0.4.42", default-features = false, features = ["iana-time-zone", "alloc", "serde"] }
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use crate::hal::PLANT_COUNT;
|
use crate::hal::PLANT_COUNT;
|
||||||
use crate::plant_state::PlantWateringMode;
|
use crate::plant_state::PlantWateringMode;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use bincode::{Decode, Encode};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct NetworkConfig {
|
pub struct NetworkConfig {
|
||||||
pub ap_ssid: String,
|
pub ap_ssid: String,
|
||||||
@@ -31,7 +30,7 @@ impl Default for NetworkConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct NightLampConfig {
|
pub struct NightLampConfig {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
@@ -54,7 +53,7 @@ impl Default for NightLampConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct TankConfig {
|
pub struct TankConfig {
|
||||||
pub tank_sensor_enabled: bool,
|
pub tank_sensor_enabled: bool,
|
||||||
@@ -79,20 +78,20 @@ impl Default for TankConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||||
pub enum BatteryBoardVersion {
|
pub enum BatteryBoardVersion {
|
||||||
#[default]
|
#[default]
|
||||||
Disabled,
|
Disabled,
|
||||||
WchI2cSlave,
|
WchI2cSlave,
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||||
pub enum BoardVersion {
|
pub enum BoardVersion {
|
||||||
Initial,
|
Initial,
|
||||||
#[default]
|
#[default]
|
||||||
V4,
|
V4,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||||
pub struct BoardHardware {
|
pub struct BoardHardware {
|
||||||
pub board: BoardVersion,
|
pub board: BoardVersion,
|
||||||
pub battery: BatteryBoardVersion,
|
pub battery: BatteryBoardVersion,
|
||||||
@@ -100,7 +99,7 @@ pub struct BoardHardware {
|
|||||||
pub pump_corrosion_protection: bool,
|
pub pump_corrosion_protection: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct PlantControllerConfig {
|
pub struct PlantControllerConfig {
|
||||||
pub hardware: BoardHardware,
|
pub hardware: BoardHardware,
|
||||||
@@ -111,7 +110,7 @@ pub struct PlantControllerConfig {
|
|||||||
pub timezone: Option<String>,
|
pub timezone: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct PlantConfig {
|
pub struct PlantConfig {
|
||||||
pub mode: PlantWateringMode,
|
pub mode: PlantWateringMode,
|
||||||
|
|||||||
@@ -230,16 +230,8 @@ impl<E: fmt::Debug> From<ExpanderError<I2cDeviceError<E>>> for FatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bincode::error::DecodeError> for FatError {
|
impl From<postcard::Error> for FatError {
|
||||||
fn from(value: bincode::error::DecodeError) -> Self {
|
fn from(value: postcard::Error) -> Self {
|
||||||
FatError::Eeprom24x {
|
|
||||||
error: format!("{value:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bincode::error::EncodeError> for FatError {
|
|
||||||
fn from(value: bincode::error::EncodeError) -> Self {
|
|
||||||
FatError::Eeprom24x {
|
FatError::Eeprom24x {
|
||||||
error: format!("{value:?}"),
|
error: format!("{value:?}"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -582,7 +582,15 @@ impl Esp<'_> {
|
|||||||
/// Retries once on flash error.
|
/// Retries once on flash error.
|
||||||
pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> FatResult<()> {
|
pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> FatResult<()> {
|
||||||
let timestamp = self.get_time().to_rfc3339();
|
let timestamp = self.get_time().to_rfc3339();
|
||||||
self.savegame.save(config.as_slice(), ×tamp)
|
self.savegame.save(config.as_slice(), ×tamp)?;
|
||||||
|
|
||||||
|
match self.savegame.load_latest()? {
|
||||||
|
None => bail!("Config save verification failed: no latest save found"),
|
||||||
|
Some(data) => {
|
||||||
|
let _: PlantControllerConfig = serde_json::from_slice(&data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a specific save slot by erasing it on flash.
|
/// Delete a specific save slot by erasing it on flash.
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ use alloc::boxed::Box;
|
|||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bincode::{Decode, Encode};
|
|
||||||
use canapi::SensorSlot;
|
use canapi::SensorSlot;
|
||||||
use chrono::{DateTime, FixedOffset, Utc};
|
use chrono::{DateTime, FixedOffset, Utc};
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
@@ -118,7 +117,7 @@ pub static I2C_DRIVER: OnceLock<
|
|||||||
embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>,
|
embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>,
|
||||||
> = OnceLock::new();
|
> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Encode, Decode)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub enum Sensor {
|
pub enum Sensor {
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::fat_error::FatResult;
|
use crate::fat_error::FatResult;
|
||||||
use crate::hal::Box;
|
use crate::hal::Box;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bincode::{Decode, Encode};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use ds323x::ic::DS3231;
|
use ds323x::ic::DS3231;
|
||||||
use ds323x::interface::I2cInterface;
|
use ds323x::interface::I2cInterface;
|
||||||
@@ -29,7 +28,7 @@ pub trait RTCModuleInteraction {
|
|||||||
fn read(&mut self, offset: u32, data: &mut [u8]) -> FatResult<()>;
|
fn read(&mut self, offset: u32, data: &mut [u8]) -> FatResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug, Default)]
|
||||||
pub struct BackupHeader {
|
pub struct BackupHeader {
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub(crate) crc16: u16,
|
pub(crate) crc16: u16,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use alloc::string::ToString;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use embedded_savegame::storage::{Flash, Storage};
|
use embedded_savegame::storage::{Flash, Storage};
|
||||||
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
||||||
@@ -32,6 +33,19 @@ struct ParsedSave<'a> {
|
|||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn strip_padding(data: &[u8]) -> &[u8] {
|
||||||
|
let mut end = data.len();
|
||||||
|
while end > 0 {
|
||||||
|
let b = data[end - 1];
|
||||||
|
if b == 0x00 || b == 0xFF {
|
||||||
|
end -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&data[..end]
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_save(data: &[u8]) -> FatResult<ParsedSave<'_>> {
|
fn parse_save(data: &[u8]) -> FatResult<ParsedSave<'_>> {
|
||||||
if data.len() < SAVE_HEADER_LEN {
|
if data.len() < SAVE_HEADER_LEN {
|
||||||
return Err(FatError::String {
|
return Err(FatError::String {
|
||||||
@@ -60,7 +74,7 @@ fn parse_save(data: &[u8]) -> FatResult<ParsedSave<'_>> {
|
|||||||
|
|
||||||
Ok(ParsedSave {
|
Ok(ParsedSave {
|
||||||
created_at,
|
created_at,
|
||||||
data: &data[timestamp_end..],
|
data: strip_padding(&data[timestamp_end..]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +254,7 @@ impl SavegameManager {
|
|||||||
saves.push(SaveInfo {
|
saves.push(SaveInfo {
|
||||||
idx,
|
idx,
|
||||||
len: 0,
|
len: 0,
|
||||||
created_at: None,
|
created_at: Some(err.to_string()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use crate::log::{log, LogMessage};
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bincode::config;
|
|
||||||
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
||||||
use canapi::SensorSlot;
|
use canapi::SensorSlot;
|
||||||
use chrono::{DateTime, FixedOffset, Utc};
|
use chrono::{DateTime, FixedOffset, Utc};
|
||||||
@@ -36,7 +35,6 @@ use measurements::{Current, Voltage};
|
|||||||
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
||||||
|
|
||||||
pub const BACKUP_HEADER_MAX_SIZE: usize = 64;
|
pub const BACKUP_HEADER_MAX_SIZE: usize = 64;
|
||||||
const CONFIG: config::Configuration = config::standard();
|
|
||||||
|
|
||||||
const MPPT_CURRENT_SHUNT_OHMS: f64 = 0.05_f64;
|
const MPPT_CURRENT_SHUNT_OHMS: f64 = 0.05_f64;
|
||||||
const TWAI_BAUDRATE: twai::BaudRate = twai::BaudRate::Custom(twai::TimingConfig {
|
const TWAI_BAUDRATE: twai::BaudRate = twai::BaudRate::Custom(twai::TimingConfig {
|
||||||
@@ -543,7 +541,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
}
|
}
|
||||||
async fn backup_config(&mut self, controller_config: &PlantControllerConfig) -> FatResult<()> {
|
async fn backup_config(&mut self, controller_config: &PlantControllerConfig) -> FatResult<()> {
|
||||||
let mut buffer: [u8; 4096 - BACKUP_HEADER_MAX_SIZE] = [0; 4096 - BACKUP_HEADER_MAX_SIZE];
|
let mut buffer: [u8; 4096 - BACKUP_HEADER_MAX_SIZE] = [0; 4096 - BACKUP_HEADER_MAX_SIZE];
|
||||||
let length = bincode::encode_into_slice(controller_config, &mut buffer, CONFIG)?;
|
let length = postcard::to_slice(controller_config, &mut buffer)?.len();
|
||||||
info!("Writing backup config of size {}", length);
|
info!("Writing backup config of size {}", length);
|
||||||
let mut checksum = X25.digest();
|
let mut checksum = X25.digest();
|
||||||
checksum.update(&buffer[..length]);
|
checksum.update(&buffer[..length]);
|
||||||
@@ -556,7 +554,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
size: length as u16,
|
size: length as u16,
|
||||||
};
|
};
|
||||||
info!("Header is {:?}", header);
|
info!("Header is {:?}", header);
|
||||||
bincode::encode_into_slice(&header, &mut header_page_buffer, CONFIG)?;
|
postcard::to_slice(&header, &mut header_page_buffer)?;
|
||||||
info!("Header is serialized");
|
info!("Header is serialized");
|
||||||
self.get_rtc_module().write(0, &header_page_buffer)?;
|
self.get_rtc_module().write(0, &header_page_buffer)?;
|
||||||
info!("Header written");
|
info!("Header written");
|
||||||
@@ -600,7 +598,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
bail!("CRC mismatch in backup data")
|
bail!("CRC mismatch in backup data")
|
||||||
}
|
}
|
||||||
info!("CRC is correct");
|
info!("CRC is correct");
|
||||||
let (decoded, _) = bincode::decode_from_slice(&store[..], CONFIG)?;
|
let decoded = postcard::from_bytes(&store[..])?;
|
||||||
info!("Backup data decoded");
|
info!("Backup data decoded");
|
||||||
Ok(decoded)
|
Ok(decoded)
|
||||||
}
|
}
|
||||||
@@ -609,8 +607,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
|
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
|
||||||
self.get_rtc_module().read(0, &mut header_page_buffer)?;
|
self.get_rtc_module().read(0, &mut header_page_buffer)?;
|
||||||
info!("Read header page");
|
info!("Read header page");
|
||||||
let info: Result<(BackupHeader, usize), bincode::error::DecodeError> =
|
let info = postcard::take_from_bytes::<BackupHeader>(&header_page_buffer[..]);
|
||||||
bincode::decode_from_slice(&header_page_buffer[..], CONFIG);
|
|
||||||
info!("decoding header: {:?}", info);
|
info!("decoding header: {:?}", info);
|
||||||
let (header, _) = info.context("Could not read backup header")?;
|
let (header, _) = info.context("Could not read backup header")?;
|
||||||
Ok(header)
|
Ok(header)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
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 bincode::{Decode, Encode};
|
|
||||||
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};
|
||||||
@@ -73,7 +72,7 @@ impl PumpState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Encode, Decode)]
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum PlantWateringMode {
|
pub enum PlantWateringMode {
|
||||||
Off,
|
Off,
|
||||||
TargetMoisture,
|
TargetMoisture,
|
||||||
|
|||||||
Reference in New Issue
Block a user