221 lines
7.2 KiB
Rust
221 lines
7.2 KiB
Rust
use serde::Serialize;
|
|
use std::{collections::HashMap, sync::Mutex};
|
|
use strum::EnumIter;
|
|
use strum_macros::IntoStaticStr;
|
|
|
|
use esp_idf_svc::systime::EspSystemTime;
|
|
use once_cell::sync::Lazy;
|
|
use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
|
|
use text_template::Template;
|
|
use unit_enum::UnitEnum;
|
|
|
|
const TXT_SHORT_LENGTH: usize = 8;
|
|
const TXT_LONG_LENGTH: usize = 32;
|
|
|
|
const BUFFER_SIZE: usize = 220;
|
|
|
|
#[link_section = ".rtc.data"]
|
|
static mut BUFFER: ConstGenericRingBuffer<LogEntry, BUFFER_SIZE> =
|
|
ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
|
|
#[allow(static_mut_refs)]
|
|
static BUFFER_ACCESS: Lazy<Mutex<&mut ConstGenericRingBuffer<LogEntry, BUFFER_SIZE>>> =
|
|
Lazy::new(|| unsafe { Mutex::new(&mut BUFFER) });
|
|
|
|
#[derive(Serialize, Debug, Clone)]
|
|
pub struct LogEntry {
|
|
pub timestamp: u64,
|
|
pub message_id: u16,
|
|
pub a: u32,
|
|
pub b: u32,
|
|
pub txt_short: heapless::String<TXT_SHORT_LENGTH>,
|
|
pub txt_long: heapless::String<TXT_LONG_LENGTH>,
|
|
}
|
|
|
|
pub fn init() {
|
|
unsafe {
|
|
BUFFER = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
|
|
};
|
|
let mut access = BUFFER_ACCESS.lock().unwrap();
|
|
access.drain().for_each(|_| {});
|
|
}
|
|
|
|
fn limit_length<const LIMIT: usize>(input: &str, target: &mut heapless::String<LIMIT>) {
|
|
for char in input.chars() {
|
|
match target.push(char) {
|
|
Ok(_) => {} //continue adding chars
|
|
Err(_) => {
|
|
//clear space for two asci chars
|
|
while target.len() + 2 >= LIMIT {
|
|
target.pop().unwrap();
|
|
}
|
|
//add .. to shortened strings
|
|
target.push('.').unwrap();
|
|
target.push('.').unwrap();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_log() -> Vec<LogEntry> {
|
|
let buffer = BUFFER_ACCESS.lock().unwrap();
|
|
let mut read_copy = Vec::new();
|
|
for entry in buffer.iter() {
|
|
let copy = entry.clone();
|
|
read_copy.push(copy);
|
|
}
|
|
drop(buffer);
|
|
return read_copy;
|
|
}
|
|
|
|
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {
|
|
let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
|
|
let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
|
|
|
|
limit_length(txt_short, &mut txt_short_stack);
|
|
limit_length(txt_long, &mut txt_long_stack);
|
|
|
|
let time = EspSystemTime {}.now().as_millis() as u64;
|
|
|
|
let ordinal = message_key.ordinal() as u16;
|
|
let template_string: &str = message_key.into();
|
|
|
|
let mut values: HashMap<&str, &str> = HashMap::new();
|
|
let number_a_str = number_a.to_string();
|
|
let number_b_str = number_b.to_string();
|
|
|
|
values.insert("number_a", &number_a_str);
|
|
values.insert("number_b", &number_b_str);
|
|
values.insert("txt_short", txt_short);
|
|
values.insert("txt_long", txt_long);
|
|
|
|
let template = Template::from(template_string);
|
|
let serial_entry = template.fill_in(&values);
|
|
|
|
println!("{serial_entry}");
|
|
|
|
let entry = LogEntry {
|
|
timestamp: time,
|
|
message_id: ordinal,
|
|
a: number_a,
|
|
b: number_b,
|
|
txt_short: txt_short_stack,
|
|
txt_long: txt_long_stack,
|
|
};
|
|
|
|
let mut buffer = BUFFER_ACCESS.lock().unwrap();
|
|
buffer.push(entry);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn within_limit() {
|
|
let test = "12345678";
|
|
|
|
let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
|
|
let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
|
|
limit_length(test, &mut txt_short_stack);
|
|
limit_length(test, &mut txt_long_stack);
|
|
|
|
assert_eq!(txt_short_stack.as_str(), test);
|
|
assert_eq!(txt_long_stack.as_str(), test);
|
|
}
|
|
}
|
|
|
|
#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)]
|
|
pub enum LogMessage {
|
|
#[strum(
|
|
serialize = "Reset due to ${txt_long} requires rtc clear ${number_a} and force config mode ${number_b}"
|
|
)]
|
|
ResetReason,
|
|
#[strum(serialize = "Current restart to conf mode ${number_a}")]
|
|
RestartToConfig,
|
|
#[strum(serialize = "Current low voltage detection is ${number_a}")]
|
|
LowVoltage,
|
|
#[strum(serialize = "Error communicating with battery!! ${txt_long}")]
|
|
BatteryCommunicationError,
|
|
#[strum(serialize = "Tank water level cricial! Refill tank!")]
|
|
TankWaterLevelLow,
|
|
#[strum(serialize = "Tank sensor hardware error: ${txt_long}")]
|
|
TankSensorBoardError,
|
|
#[strum(serialize = "Tank sensor not present, raw voltage measured = ${number_a} mV")]
|
|
TankSensorMissing,
|
|
#[strum(
|
|
serialize = "Tank sensor value out of range, min = ${number_a}%, max = ${number_b}%, value = ${text_short}%"
|
|
)]
|
|
TankSensorValueRangeError,
|
|
#[strum(
|
|
serialize = "raw measure unscaled ${number_a} hz ${number_b}, plant ${txt_short} sensor ${txt_long}"
|
|
)]
|
|
RawMeasure,
|
|
#[strum(serialize = "IP info: ${txt_long}")]
|
|
WifiInfo,
|
|
#[strum(serialize = "Plant:${txt_short} a:${number_a} b:${number_b}")]
|
|
TestSensor,
|
|
#[strum(serialize = "Stay alive topic is ${txt_long}")]
|
|
StayAlive,
|
|
#[strum(serialize = "Connecting mqtt ${txt_short} with id ${txt_long}")]
|
|
MqttInfo,
|
|
#[strum(serialize = "Received stay alive with value ${txt_short}")]
|
|
MqttStayAliveRec,
|
|
#[strum(serialize = "Unknown topic recieved ${txt_long}")]
|
|
UnknownTopic,
|
|
#[strum(serialize = "Partition state is ${txt_long}")]
|
|
PartitionState,
|
|
#[strum(serialize = "Mounted Filesystem free ${number_a} total ${number_b} use ${txt_short}")]
|
|
FilesystemMount,
|
|
#[strum(
|
|
serialize = "Mounting Filesystem, this will format the first time and needs quite some time!"
|
|
)]
|
|
MountingFilesystem,
|
|
#[strum(serialize = "Year inplausible, force config mode")]
|
|
YearInplausibleForceConfig,
|
|
#[strum(serialize = "Going to config mode, due to request from prior run")]
|
|
ConfigModeSoftwareOverride,
|
|
#[strum(serialize = "Going to config mode, due to request via config mode button")]
|
|
ConfigModeButtonOverride,
|
|
#[strum(serialize = "Going to normal mode")]
|
|
NormalRun,
|
|
#[strum(serialize = "Missing normal config, entering config mode ${txt_long}")]
|
|
ConfigModeMissingConfig,
|
|
#[strum(serialize = "startup state wifi ${number_a} sntp ${number_b} mqtt ${txt_short}")]
|
|
StartupInfo,
|
|
#[strum(
|
|
serialize = "Trying to pump for ${number_b}s with pump ${number_a} now dryrun: ${txt_short}"
|
|
)]
|
|
PumpPlant,
|
|
#[strum(serialize = "Enable main power dryrun: ${number_a}")]
|
|
EnableMain,
|
|
#[strum(
|
|
serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}"
|
|
)]
|
|
ConsecutivePumpCountLimit,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct MessageTranslation {
|
|
msg_type: LogMessage,
|
|
message: &'static str,
|
|
}
|
|
|
|
impl From<&LogMessage> for MessageTranslation {
|
|
fn from(value: &LogMessage) -> Self {
|
|
Self {
|
|
msg_type: value.clone(),
|
|
message: value.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LogMessage {
|
|
pub fn to_log_localisation_config() -> Vec<MessageTranslation> {
|
|
Vec::from_iter((0..LogMessage::len()).map(|i| {
|
|
let msg_type = LogMessage::from_ordinal(i).unwrap();
|
|
(&msg_type).into()
|
|
}))
|
|
}
|
|
}
|