1use core::cell::RefCell;
2use core::fmt;
3
4pub mod drain;
5
6use critical_section::Mutex;
8
9use crate::comm::messages::enums::Severity;
10
11const LOG_QUEUE_SIZE: usize = 16;
13const MAX_LOG_LEN: usize = 50; #[derive(Clone, Copy)]
19pub struct LogString {
20 buffer: [u8; MAX_LOG_LEN],
21 len: usize,
22}
23
24impl LogString {
25 pub const fn new() -> Self {
26 Self {
27 buffer: [0; MAX_LOG_LEN],
28 len: 0,
29 }
30 }
31
32 pub fn as_str(&self) -> &str {
33 unsafe { core::str::from_utf8_unchecked(&self.buffer[..self.len]) }
35 }
36}
37
38impl fmt::Write for LogString {
39 fn write_str(&mut self, s: &str) -> fmt::Result {
40 let bytes = s.as_bytes();
41 let remaining = MAX_LOG_LEN - self.len;
42 let copy_len = bytes.len().min(remaining);
43
44 self.buffer[self.len..self.len + copy_len].copy_from_slice(&bytes[..copy_len]);
46 self.len += copy_len;
47
48 if bytes.len() > remaining {
49 return Err(fmt::Error); }
51 Ok(())
52 }
53}
54
55#[derive(Clone, Copy)]
57pub struct LogEntry {
58 pub severity: Severity,
59 pub message: LogString,
60}
61
62impl LogEntry {
63 pub const fn empty() -> Self {
64 Self {
65 severity: Severity::Info,
67 message: LogString {
68 buffer: [0; MAX_LOG_LEN],
69 len: 0,
70 },
71 }
72 }
73}
74
75struct LogQueue {
77 storage: [LogEntry; LOG_QUEUE_SIZE],
78 head: usize, tail: usize, full: bool,
81}
82
83impl LogQueue {
84 const fn new() -> Self {
85 Self {
86 storage: [LogEntry::empty(); LOG_QUEUE_SIZE],
87 head: 0,
88 tail: 0,
89 full: false,
90 }
91 }
92
93 fn push(&mut self, entry: LogEntry) {
94 self.storage[self.head] = entry;
95 self.head = (self.head + 1) % LOG_QUEUE_SIZE;
96
97 if self.full {
98 self.tail = (self.tail + 1) % LOG_QUEUE_SIZE;
100 }
101
102 self.full = self.head == self.tail;
103 }
104
105 fn pop(&mut self) -> Option<LogEntry> {
106 if !self.full && self.head == self.tail {
107 return None; }
109
110 let entry = self.storage[self.tail];
111 self.tail = (self.tail + 1) % LOG_QUEUE_SIZE;
112 self.full = false;
113 Some(entry)
114 }
115}
116
117static LOG_QUEUE: Mutex<RefCell<LogQueue>> = Mutex::new(RefCell::new(LogQueue::new()));
120
121pub struct Logger;
124
125impl Logger {
126 pub fn log(severity: Severity, args: fmt::Arguments) {
127 critical_section::with(|cs| {
128 let mut queue = LOG_QUEUE.borrow_ref_mut(cs);
129
130 let mut entry = LogEntry::empty();
131 entry.severity = severity;
132
133 let _ = fmt::Write::write_fmt(&mut entry.message, args);
136
137 queue.push(entry);
138 });
139 }
140
141 pub fn info(args: fmt::Arguments) {
143 Self::log(Severity::Info, args);
144 }
145 pub fn warn(args: fmt::Arguments) {
146 Self::log(Severity::Warning, args);
147 }
148 pub fn error(args: fmt::Arguments) {
149 Self::log(Severity::Error, args);
150 }
151 pub fn debug(args: fmt::Arguments) {
152 Self::log(Severity::Debug, args);
153 }
154
155 pub fn pop() -> Option<LogEntry> {
157 critical_section::with(|cs| LOG_QUEUE.borrow_ref_mut(cs).pop())
158 }
159}
160
161#[macro_export]
169macro_rules! log_info {
170 ($($arg:tt)*) => { $crate::log::Logger::info(format_args!($($arg)*)) };
171}
172#[macro_export]
173macro_rules! log_warn {
174 ($($arg:tt)*) => { $crate::log::Logger::warn(format_args!($($arg)*)) };
175}
176#[macro_export]
177macro_rules! log_error {
178 ($($arg:tt)*) => { $crate::log::Logger::error(format_args!($($arg)*)) };
179}
180#[macro_export]
182macro_rules! log_debug {
183 ($($arg:tt)*) => { $crate::log::Logger::debug(format_args!($($arg)*))};
184}