use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use super::parser::{GaudiMetricsData, parse_hlsmi_output};
pub struct MetricsStore {
data_buffer: Arc<Mutex<VecDeque<String>>>,
last_data: Arc<Mutex<Option<GaudiMetricsData>>>,
}
impl MetricsStore {
pub fn new(capacity: usize) -> Self {
Self {
data_buffer: Arc::new(Mutex::new(VecDeque::with_capacity(capacity))),
last_data: Arc::new(Mutex::new(None)),
}
}
#[cfg(test)]
pub fn add_section(&self, section: String, capacity: usize) {
let mut buffer = self.data_buffer.lock().unwrap();
if buffer.len() >= capacity {
buffer.pop_front(); }
buffer.push_back(section);
}
pub fn get_buffer(&self) -> Arc<Mutex<VecDeque<String>>> {
self.data_buffer.clone()
}
pub fn get_latest_data(&self) -> Result<GaudiMetricsData, Box<dyn std::error::Error>> {
let latest_section = {
let buffer = self.data_buffer.lock().unwrap();
buffer.back().cloned()
};
if let Some(section) = latest_section {
if let Ok(data) = parse_hlsmi_output(§ion) {
let mut last_data = self.last_data.lock().unwrap();
*last_data = Some(data.clone());
return Ok(data);
}
}
if let Some(cached) = self.last_data.lock().unwrap().clone() {
return Ok(cached);
}
Err("No hl-smi data available".into())
}
pub fn clear(&self) {
let mut buffer = self.data_buffer.lock().unwrap();
buffer.clear();
let mut last_data = self.last_data.lock().unwrap();
*last_data = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buffer_overflow_protection() {
let capacity = 5;
let store = MetricsStore::new(capacity);
for i in 0..10 {
let section = format!(
"0, UUID-{i}, HL-325L, 131072 MiB, 672 MiB, 130400 MiB, 226 W, 850 W, 36 C, 0 %"
);
store.add_section(section, capacity);
}
let buffer = store.data_buffer.lock().unwrap();
assert_eq!(buffer.len(), capacity);
assert!(buffer.back().unwrap().contains("UUID-9"));
assert!(buffer.front().unwrap().contains("UUID-5"));
}
#[test]
fn test_concurrent_buffer_access() {
use std::thread;
use std::time::Duration;
let capacity = 100;
let store = Arc::new(MetricsStore::new(capacity));
let mut handles = vec![];
for i in 0..5 {
let store_clone = store.clone();
let handle = thread::spawn(move || {
for j in 0..20 {
let section = format!(
"0, UUID-{i}-{j}, HL-325L, 131072 MiB, 672 MiB, 130400 MiB, 226 W, 850 W, 36 C, 0 %"
);
store_clone.add_section(section, capacity);
thread::sleep(Duration::from_micros(100));
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let buffer = store.data_buffer.lock().unwrap();
assert_eq!(buffer.len(), capacity); }
#[test]
fn test_get_latest_data() {
let store = MetricsStore::new(10);
let section = "0, 01P4-HL3090A0-18-U4V193-22-07-00, HL-325L, 1.22.1-97ec1a4, 131072 MiB, 672 MiB, 130400 MiB, 226 W, 850 W, 36 C, 0 %\n\
1, 01P4-HL3090A0-18-U4V298-03-04-04, HL-325L, 1.22.1-97ec1a4, 131072 MiB, 672 MiB, 130400 MiB, 230 W, 850 W, 39 C, 0 %";
store.add_section(section.to_string(), 10);
let result = store.get_latest_data();
assert!(result.is_ok());
let data = result.unwrap();
assert_eq!(data.devices.len(), 2);
assert_eq!(data.devices[0].index, 0);
assert_eq!(data.devices[0].driver_version, "1.22.1");
assert_eq!(data.devices[1].index, 1);
}
}