cleanups
This commit is contained in:
284
Software/MainBoard/rust/src/log/mod.rs
Normal file
284
Software/MainBoard/rust/src/log/mod.rs
Normal file
@@ -0,0 +1,284 @@
|
||||
use crate::hal::TIME_ACCESS;
|
||||
use crate::vec;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use bytemuck::{AnyBitPattern, Pod, Zeroable};
|
||||
use deranged::RangedU8;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use esp_hal::Persistable;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
use strum_macros::IntoStaticStr;
|
||||
use unit_enum::UnitEnum;
|
||||
|
||||
const LOG_ARRAY_SIZE: u8 = 220;
|
||||
const MAX_LOG_ARRAY_INDEX: u8 = LOG_ARRAY_SIZE - 1;
|
||||
#[esp_hal::ram(rtc_fast, persistent)]
|
||||
static mut LOG_ARRAY: LogArray = LogArray {
|
||||
buffer: [LogEntryInner {
|
||||
timestamp: 0,
|
||||
message_id: 0,
|
||||
a: 0,
|
||||
b: 0,
|
||||
txt_short: [0; TXT_SHORT_LENGTH],
|
||||
txt_long: [0; TXT_LONG_LENGTH],
|
||||
}; LOG_ARRAY_SIZE as usize],
|
||||
head: 0,
|
||||
};
|
||||
pub static LOG_ACCESS: Mutex<CriticalSectionRawMutex, &'static mut LogArray> =
|
||||
unsafe { Mutex::new(&mut *&raw mut LOG_ARRAY) };
|
||||
|
||||
const TXT_SHORT_LENGTH: usize = 8;
|
||||
const TXT_LONG_LENGTH: usize = 32;
|
||||
|
||||
#[derive(Debug, Clone, Copy, AnyBitPattern)]
|
||||
#[repr(C)]
|
||||
pub struct LogArray {
|
||||
buffer: [LogEntryInner; LOG_ARRAY_SIZE as usize],
|
||||
head: u8,
|
||||
}
|
||||
|
||||
unsafe impl Persistable for LogArray {}
|
||||
unsafe impl Zeroable for LogEntryInner {}
|
||||
|
||||
unsafe impl Pod for LogEntryInner {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct LogEntryInner {
|
||||
pub timestamp: u64,
|
||||
pub message_id: u16,
|
||||
pub a: u32,
|
||||
pub b: u32,
|
||||
pub txt_short: [u8; TXT_SHORT_LENGTH],
|
||||
pub txt_long: [u8; TXT_LONG_LENGTH],
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct LogEntry {
|
||||
pub timestamp: u64,
|
||||
pub message_id: u16,
|
||||
pub a: u32,
|
||||
pub b: u32,
|
||||
pub txt_short: alloc::string::String,
|
||||
pub txt_long: alloc::string::String,
|
||||
}
|
||||
|
||||
impl From<LogEntryInner> for LogEntry {
|
||||
fn from(value: LogEntryInner) -> Self {
|
||||
LogEntry {
|
||||
timestamp: value.timestamp,
|
||||
message_id: value.message_id,
|
||||
a: value.a,
|
||||
b: value.b,
|
||||
txt_short: alloc::string::String::from_utf8_lossy_owned(value.txt_short.to_vec()),
|
||||
txt_long: alloc::string::String::from_utf8_lossy_owned(value.txt_long.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn log(
|
||||
message_key: LogMessage,
|
||||
number_a: u32,
|
||||
number_b: u32,
|
||||
txt_short: &str,
|
||||
txt_long: &str,
|
||||
) {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(message_key, number_a, number_b, txt_short, txt_long)
|
||||
.await
|
||||
}
|
||||
|
||||
impl LogArray {
|
||||
pub fn get(&mut self) -> Vec<LogEntry> {
|
||||
let head: RangedU8<0, MAX_LOG_ARRAY_INDEX> =
|
||||
RangedU8::new(self.head).unwrap_or(RangedU8::new(0).unwrap());
|
||||
|
||||
let mut rv: Vec<LogEntry> = Vec::new();
|
||||
let mut index = head.wrapping_sub(1);
|
||||
for _ in 0..self.buffer.len() {
|
||||
let entry = self.buffer[index.get() as usize];
|
||||
if (entry.message_id as usize) != LogMessage::Empty.ordinal() {
|
||||
rv.push(entry.into());
|
||||
}
|
||||
index = index.wrapping_sub(1);
|
||||
}
|
||||
rv
|
||||
}
|
||||
|
||||
pub async fn log(
|
||||
&mut self,
|
||||
message_key: LogMessage,
|
||||
number_a: u32,
|
||||
number_b: u32,
|
||||
txt_short: &str,
|
||||
txt_long: &str,
|
||||
) {
|
||||
let mut head: RangedU8<0, MAX_LOG_ARRAY_INDEX> =
|
||||
RangedU8::new(self.head).unwrap_or(RangedU8::new(0).unwrap());
|
||||
|
||||
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 = {
|
||||
let guard = TIME_ACCESS.get().await.lock().await;
|
||||
guard.current_time_us()
|
||||
} / 1000;
|
||||
|
||||
let ordinal = message_key.ordinal() as u16;
|
||||
let template: &str = message_key.into();
|
||||
let mut template_string = template.to_string();
|
||||
template_string = template_string.replace("${number_a}", number_a.to_string().as_str());
|
||||
template_string = template_string.replace("${number_b}", number_b.to_string().as_str());
|
||||
template_string = template_string.replace("${txt_long}", txt_long);
|
||||
template_string = template_string.replace("${txt_short}", txt_short);
|
||||
|
||||
info!("{}", template_string);
|
||||
|
||||
let to_modify = &mut self.buffer[head.get() as usize];
|
||||
to_modify.timestamp = time;
|
||||
to_modify.message_id = ordinal;
|
||||
to_modify.a = number_a;
|
||||
to_modify.b = number_b;
|
||||
to_modify
|
||||
.txt_short
|
||||
.clone_from_slice(&txt_short_stack.as_bytes());
|
||||
to_modify
|
||||
.txt_long
|
||||
.clone_from_slice(&txt_long_stack.as_bytes());
|
||||
head = head.wrapping_add(1);
|
||||
self.head = head.get();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
while target.len() < LIMIT {
|
||||
target.push(' ').unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoStaticStr, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)]
|
||||
pub enum LogMessage {
|
||||
#[strum(serialize = "")]
|
||||
Empty,
|
||||
#[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 ${number_a}")]
|
||||
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,
|
||||
#[strum(
|
||||
serialize = "Pump Overcurrent error, pump: ${number_a} tripped overcurrent ${number_b} limit was ${txt_short} @s ${txt_long}"
|
||||
)]
|
||||
PumpOverCurrent,
|
||||
#[strum(
|
||||
serialize = "Pump Open loop error, pump: ${number_a} is low, ${number_b} limit was ${txt_short} @s ${txt_long}"
|
||||
)]
|
||||
PumpOpenLoopCurrent,
|
||||
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
|
||||
PumpMissingSensorCurrent,
|
||||
}
|
||||
|
||||
#[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()
|
||||
}))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user