get most stuff working again, by upgrading to newer esp-hal version
this involved adding a lot of code from the develop branch step by step there are still some bugs, but at least i can get into the web interface and configure stuff again \o/ … measuring and pumping is working as well
This commit is contained in:
108
rust/src/log/interceptor.rs
Normal file
108
rust/src/log/interceptor.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex as BlockingMutex;
|
||||
use log::{LevelFilter, Log, Metadata, Record};
|
||||
|
||||
const MAX_LIVE_LOG_ENTRIES: usize = 64;
|
||||
|
||||
struct LiveLogBuffer {
|
||||
entries: Vec<(u64, String)>,
|
||||
next_seq: u64,
|
||||
}
|
||||
|
||||
impl LiveLogBuffer {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
entries: Vec::new(),
|
||||
next_seq: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, text: String) {
|
||||
if self.entries.len() >= MAX_LIVE_LOG_ENTRIES {
|
||||
self.entries.remove(0);
|
||||
}
|
||||
self.entries.push((self.next_seq, text));
|
||||
self.next_seq += 1;
|
||||
}
|
||||
|
||||
fn get_after(&self, after: Option<u64>) -> (Vec<(u64, String)>, bool, u64) {
|
||||
let next_seq = self.next_seq;
|
||||
match after {
|
||||
None => (self.entries.clone(), false, next_seq),
|
||||
Some(after_seq) => {
|
||||
let result: Vec<_> = self.entries
|
||||
.iter()
|
||||
.filter(|(seq, _)| *seq > after_seq)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// Dropped if there are entries that should exist (seq > after_seq) but
|
||||
// the oldest retained entry has a higher seq than after_seq + 1.
|
||||
let dropped = if next_seq > after_seq.saturating_add(1) {
|
||||
if let Some((oldest_seq, _)) = self.entries.first() {
|
||||
*oldest_seq > after_seq.saturating_add(1)
|
||||
} else {
|
||||
// Buffer empty but entries were written — all dropped
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
(result, dropped, next_seq)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InterceptorLogger {
|
||||
live_log: BlockingMutex<CriticalSectionRawMutex, core::cell::RefCell<LiveLogBuffer>>,
|
||||
}
|
||||
|
||||
impl InterceptorLogger {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
live_log: BlockingMutex::new(core::cell::RefCell::new(LiveLogBuffer::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns (entries_after, dropped, next_seq).
|
||||
/// Pass `after = None` to retrieve the entire current buffer.
|
||||
/// Pass `after = Some(seq)` to retrieve only entries with seq > that value.
|
||||
pub fn get_live_logs(&self, after: Option<u64>) -> (Vec<(u64, String)>, bool, u64) {
|
||||
self.live_log.lock(|buf| buf.borrow().get_after(after))
|
||||
}
|
||||
|
||||
pub fn init(&'static self) {
|
||||
match log::set_logger(self).map(|()| log::set_max_level(LevelFilter::Info)) {
|
||||
Ok(()) => {}
|
||||
Err(_e) => {
|
||||
esp_println::println!("ERROR: Logger already set");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Log for InterceptorLogger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.level() <= log::Level::Info
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
let message = alloc::format!("{}: {}", record.level(), record.args());
|
||||
|
||||
// Print to serial
|
||||
esp_println::println!("{}", message);
|
||||
|
||||
// Store in live log ring buffer
|
||||
self.live_log.lock(|buf| {
|
||||
buf.borrow_mut().push(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
use crate::hal::TIME_ACCESS;
|
||||
use crate::vec;
|
||||
use crate::BOARD_ACCESS;
|
||||
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::channel::Channel;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use esp_hal::Persistable;
|
||||
use log::info;
|
||||
use log::{info, warn};
|
||||
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)]
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut LOG_ARRAY: LogArray = LogArray {
|
||||
buffer: [LogEntryInner {
|
||||
timestamp: 0,
|
||||
@@ -26,8 +27,45 @@ static mut LOG_ARRAY: LogArray = LogArray {
|
||||
}; LOG_ARRAY_SIZE as usize],
|
||||
head: 0,
|
||||
};
|
||||
|
||||
// this is the only reference created for LOG_ARRAY and the only way to access it
|
||||
#[allow(static_mut_refs)]
|
||||
pub static LOG_ACCESS: Mutex<CriticalSectionRawMutex, &'static mut LogArray> =
|
||||
unsafe { Mutex::new(&mut *&raw mut LOG_ARRAY) };
|
||||
unsafe { Mutex::new(&mut LOG_ARRAY) };
|
||||
|
||||
mod interceptor;
|
||||
|
||||
pub use interceptor::InterceptorLogger;
|
||||
|
||||
pub static INTERCEPTOR: InterceptorLogger = InterceptorLogger::new();
|
||||
|
||||
pub struct LogRequest {
|
||||
pub message_key: LogMessage,
|
||||
pub number_a: u32,
|
||||
pub number_b: u32,
|
||||
pub txt_short: heapless::String<TXT_SHORT_LENGTH>,
|
||||
pub txt_long: heapless::String<TXT_LONG_LENGTH>,
|
||||
}
|
||||
|
||||
static LOG_CHANNEL: Channel<CriticalSectionRawMutex, LogRequest, 16> = Channel::new();
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn log_task() {
|
||||
loop {
|
||||
let request = LOG_CHANNEL.receive().await;
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
request.message_key,
|
||||
request.number_a,
|
||||
request.number_b,
|
||||
request.txt_short.as_str(),
|
||||
request.txt_long.as_str(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
const TXT_SHORT_LENGTH: usize = 8;
|
||||
const TXT_LONG_LENGTH: usize = 32;
|
||||
@@ -77,10 +115,31 @@ impl From<LogEntryInner> for LogEntry {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
match LOG_CHANNEL.try_send(LogRequest {
|
||||
message_key,
|
||||
number_a,
|
||||
number_b,
|
||||
txt_short: txt_short_stack,
|
||||
txt_long: txt_long_stack,
|
||||
}) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
warn!("Log channel full, dropping log entry");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
RangedU8::new(self.head).unwrap_or(RangedU8::new_saturating(0));
|
||||
|
||||
let mut rv: Vec<LogEntry> = Vec::new();
|
||||
let mut index = head.wrapping_sub(1);
|
||||
@@ -103,17 +162,11 @@ impl LogArray {
|
||||
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);
|
||||
RangedU8::new(self.head).unwrap_or(RangedU8::new_saturating(0));
|
||||
|
||||
let time = {
|
||||
let guard = TIME_ACCESS.get().await.lock().await;
|
||||
guard.current_time_us()
|
||||
let mut guard = BOARD_ACCESS.get().await.lock().await;
|
||||
guard.board_hal.get_esp().rtc.current_time_us()
|
||||
} / 1000;
|
||||
|
||||
let ordinal = message_key.ordinal() as u16;
|
||||
@@ -124,19 +177,15 @@ impl LogArray {
|
||||
template_string = template_string.replace("${txt_long}", txt_long);
|
||||
template_string = template_string.replace("${txt_short}", txt_short);
|
||||
|
||||
info!("{}", template_string);
|
||||
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());
|
||||
to_modify.txt_short.clone_from_slice(txt_short.as_bytes());
|
||||
to_modify.txt_long.clone_from_slice(txt_long.as_bytes());
|
||||
head = head.wrapping_add(1);
|
||||
self.head = head.get();
|
||||
}
|
||||
@@ -148,18 +197,37 @@ fn limit_length<const LIMIT: usize>(input: &str, target: &mut heapless::String<L
|
||||
Ok(_) => {} //continue adding chars
|
||||
Err(_) => {
|
||||
//clear space for two asci chars
|
||||
info!("pushing char {char} to limit {LIMIT} current value {target} input {input}");
|
||||
while target.len() + 2 >= LIMIT {
|
||||
target.pop().unwrap();
|
||||
target.pop();
|
||||
}
|
||||
//add .. to shortened strings
|
||||
target.push('.').unwrap();
|
||||
target.push('.').unwrap();
|
||||
return;
|
||||
match target.push('.') {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
warn!(
|
||||
"Error pushin . to limit {LIMIT} current value {target} input {input}"
|
||||
)
|
||||
}
|
||||
}
|
||||
match target.push('.') {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
warn!(
|
||||
"Error pushin . to limit {LIMIT} current value {target} input {input}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while target.len() < LIMIT {
|
||||
target.push(' ').unwrap();
|
||||
match target.push(' ') {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
warn!("Error pushing space to limit {LIMIT} current value {target} input {input}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +311,20 @@ pub enum LogMessage {
|
||||
PumpOpenLoopCurrent,
|
||||
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
|
||||
PumpMissingSensorCurrent,
|
||||
#[strum(
|
||||
serialize = "Fertilizer applied for ${number_a}s on plant ${number_b} (last application ${txt_short} minutes ago)"
|
||||
)]
|
||||
FertilizerApplied,
|
||||
#[strum(serialize = "MPPT Current sensor could not be reached")]
|
||||
MPPTError,
|
||||
#[strum(
|
||||
serialize = "Trace: a: ${number_a} b: ${number_b} txt_s ${txt_short} long ${txt_long}"
|
||||
)]
|
||||
Trace,
|
||||
#[strum(serialize = "Parsing error reading message")]
|
||||
UnknownMessage,
|
||||
#[strum(serialize = "Going to deep sleep for ${number_a} minutes")]
|
||||
DeepSleep,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -261,9 +343,9 @@ impl From<&LogMessage> for MessageTranslation {
|
||||
}
|
||||
|
||||
impl LogMessage {
|
||||
pub fn to_log_localisation_config() -> Vec<MessageTranslation> {
|
||||
pub fn log_localisation_config() -> Vec<MessageTranslation> {
|
||||
Vec::from_iter((0..LogMessage::len()).map(|i| {
|
||||
let msg_type = LogMessage::from_ordinal(i).unwrap();
|
||||
let msg_type = LogMessage::from_ordinal(i).unwrap_or(LogMessage::UnknownMessage);
|
||||
(&msg_type).into()
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user