This commit is contained in:
Herbert Wolverson 2024-03-20 15:13:34 -05:00
parent 6c7c8d94c9
commit aed4e042e7
8 changed files with 79 additions and 41 deletions

View File

@ -1,15 +1,14 @@
//! Provides a sysinfo link for CPU and RAM tracking
use crate::ui_base::SHOULD_EXIT;
use std::sync::atomic::Ordering;
use once_cell::sync::Lazy;
use std::sync::atomic::Ordering;
use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize};
const MAX_CPUS_COUNTED: usize = 128;
/// Stores overall CPU usage
pub static CPU_USAGE: Lazy<[AtomicU32; MAX_CPUS_COUNTED]> =
Lazy::new(build_empty_cpu_list);
pub static CPU_USAGE: Lazy<[AtomicU32; MAX_CPUS_COUNTED]> = Lazy::new(build_empty_cpu_list);
/// Total number of CPUs detected
pub static NUM_CPUS: AtomicUsize = AtomicUsize::new(0);
@ -50,8 +49,8 @@ pub async fn gather_sysinfo() {
fn build_empty_cpu_list() -> [AtomicU32; MAX_CPUS_COUNTED] {
let mut temp = Vec::with_capacity(MAX_CPUS_COUNTED);
for _ in 0..MAX_CPUS_COUNTED {
temp.push(AtomicU32::new(0));
temp.push(AtomicU32::new(0));
}
temp.try_into().expect("This should never happen, sizes are constant.")
}
temp.try_into()
.expect("This should never happen, sizes are constant.")
}

View File

@ -1,10 +1,10 @@
//! Handles the communication loop with lqosd.
use std::sync::atomic::Ordering;
use lqos_bus::{BusClient, BusRequest, BusResponse};
use anyhow::{bail, Result};
use tokio::sync::mpsc::{Receiver, Sender};
use crate::ui_base::SHOULD_EXIT;
use anyhow::{bail, Result};
use lqos_bus::{BusClient, BusRequest, BusResponse};
use std::sync::atomic::Ordering;
use tokio::sync::mpsc::{Receiver, Sender};
pub mod cpu_ram;
pub mod throughput;
@ -22,7 +22,7 @@ pub async fn bus_loop() -> Sender<BusCommand> {
let (tx, rx) = tokio::sync::mpsc::channel::<BusCommand>(100);
tokio::spawn(cpu_ram::gather_sysinfo());
tokio::spawn(main_loop_wrapper(rx));
tokio::spawn(main_loop_wrapper(rx));
tx
}
@ -57,7 +57,7 @@ async fn main_loop(mut rx: Receiver<BusCommand>) -> Result<()> {
}
}
}
// Perform actual bus collection
let mut commands: Vec<BusRequest> = Vec::new();
@ -68,7 +68,7 @@ async fn main_loop(mut rx: Receiver<BusCommand>) -> Result<()> {
// Send the requests and process replies
for response in bus_client.request(commands).await? {
match response {
BusResponse::CurrentThroughput{..} => throughput::throughput(&response).await,
BusResponse::CurrentThroughput { .. } => throughput::throughput(&response).await,
_ => {}
}
}
@ -82,4 +82,4 @@ async fn main_loop(mut rx: Receiver<BusCommand>) -> Result<()> {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
Ok(())
}
}

View File

@ -1,10 +1,12 @@
use std::sync::Mutex;
use lqos_bus::BusResponse;
use once_cell::sync::Lazy;
use std::sync::Mutex;
pub static THROUGHPUT_RING: Lazy<Mutex<ThroughputRingbuffer>> = Lazy::new(|| Mutex::new(ThroughputRingbuffer::default()));
pub static THROUGHPUT_RING: Lazy<Mutex<ThroughputRingbuffer>> =
Lazy::new(|| Mutex::new(ThroughputRingbuffer::default()));
const RINGBUFFER_SIZE: usize = 80;
pub static CURRENT_THROUGHPUT: Lazy<Mutex<CurrentThroughput>> = Lazy::new(|| Mutex::new(CurrentThroughput::default()));
pub static CURRENT_THROUGHPUT: Lazy<Mutex<CurrentThroughput>> =
Lazy::new(|| Mutex::new(CurrentThroughput::default()));
#[derive(Default, Copy, Clone)]
pub struct CurrentThroughput {
@ -109,6 +111,6 @@ pub async fn throughput(response: &BusResponse) {
let mut current = CURRENT_THROUGHPUT.lock().unwrap();
current.bits_per_second = *bits_per_second;
current.packets_per_second = *packets_per_second;
current.shaped_bits_per_second = *shaped_bits_per_second;
current.shaped_bits_per_second = *shaped_bits_per_second;
}
}

View File

@ -1,6 +1,6 @@
mod ui_base;
mod top_level_ui;
mod bus;
mod top_level_ui;
mod ui_base;
use anyhow::Result;
use ui_base::UiBase;
pub mod widgets;
@ -12,9 +12,9 @@ async fn main() -> Result<()> {
let bus_commander = bus::bus_loop().await;
// Initialize the UI
let mut ui = UiBase::new(bus_commander.clone())?;
let mut ui = UiBase::new(bus_commander.clone())?;
ui.event_loop().await?;
// Return OK
Ok(())
}
}

View File

@ -5,29 +5,38 @@
//! It's designed to be the manager from which specific UI
//! components are managed.
use crate::{bus::BusCommand, widgets::*};
use lqos_bus::BusClient;
use ratatui::prelude::*;
use std::io::Stdout;
use crate::widgets::*;
use tokio::sync::mpsc::Sender;
pub struct TopUi {
show_cpus: bool,
show_throughput_sparkline: bool,
main_widget: MainWidget,
}
impl TopUi {
/// Create a new TopUi instance. This will initialize the UI framework.
pub fn new() -> Self {
TopUi {
TopUi {
show_cpus: true,
show_throughput_sparkline: true,
main_widget: MainWidget::Hosts,
}
}
pub fn handle_keypress(&mut self, key: char) {
pub fn handle_keypress(&mut self, key: char, commander: Sender<BusCommand>) {
// Handle Mode Switches
match key {
'c' => self.show_cpus = !self.show_cpus,
'n' => self.show_throughput_sparkline = !self.show_throughput_sparkline,
'n' => {
self.show_throughput_sparkline = !self.show_throughput_sparkline;
commander.send(BusCommand::CollectTotalThroughput(
self.show_throughput_sparkline,
));
}
_ => {}
}
}
@ -48,7 +57,7 @@ impl TopUi {
let cpu_region = if self.show_cpus {
constraints.push(Constraint::Length(1));
next_region += 1;
next_region-1
next_region - 1
} else {
next_region
};
@ -56,7 +65,7 @@ impl TopUi {
let network_spark_region = if self.show_throughput_sparkline {
constraints.push(Constraint::Length(10));
next_region += 1;
next_region-1
next_region - 1
} else {
next_region
};
@ -67,10 +76,7 @@ impl TopUi {
}
constraints.push(Constraint::Fill(1));
let main_layout = Layout::new(
Direction::Vertical,
constraints
).split(frame.size());
let main_layout = Layout::new(Direction::Vertical, constraints).split(frame.size());
// Add Widgets
if self.show_cpus {
@ -81,5 +87,8 @@ impl TopUi {
let render = nspark.render();
frame.render_widget(render, main_layout[network_spark_region]);
}
// And finally the main panel
frame.render_widget(self.main_widget.render(), main_layout[next_region]);
}
}

View File

@ -10,7 +10,10 @@ use crossterm::{
ExecutableCommand,
};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::{io::stdout, sync::atomic::{AtomicBool, Ordering}};
use std::{
io::stdout,
sync::atomic::{AtomicBool, Ordering},
};
use tokio::{sync::mpsc::Sender, task::yield_now};
pub static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
@ -66,7 +69,7 @@ impl UiBase {
_ => None,
};
if let Some(c) = char {
self.ui.handle_keypress(c);
self.ui.handle_keypress(c, self.bus_commander.clone());
}
}
}

View File

@ -1,12 +1,20 @@
use std::sync::atomic::Ordering;
use ratatui::{style::{Color, Style}, text::Span, widgets::{Block, Borders, Widget}};
use ratatui::{
style::{Color, Style},
text::Span,
widgets::{Block, Borders, Widget},
};
/// Used to display the CPU usage and RAM usage
pub fn cpu_display() -> impl Widget {
use crate::bus::cpu_ram::*;
let num_cpus = NUM_CPUS.load(Ordering::Relaxed);
let cpu_usage = CPU_USAGE.iter().take(num_cpus).map(|x| x.load(Ordering::Relaxed)).collect::<Vec<_>>();
let cpu_usage = CPU_USAGE
.iter()
.take(num_cpus)
.map(|x| x.load(Ordering::Relaxed))
.collect::<Vec<_>>();
let total_ram = TOTAL_RAM.load(Ordering::Relaxed);
let used_ram = RAM_USED.load(Ordering::Relaxed);
@ -22,7 +30,10 @@ pub fn cpu_display() -> impl Widget {
let mut span_buf = vec![
Span::styled(" [ RAM: ", Style::default().fg(Color::Green)),
Span::styled(format!("{:.0}% ", ram_percent), Style::default().fg(ram_color)),
Span::styled(
format!("{:.0}% ", ram_percent),
Style::default().fg(ram_color),
),
Span::styled("CPU: ", Style::default().fg(Color::Green)),
];
for cpu in cpu_usage {
@ -33,9 +44,12 @@ pub fn cpu_display() -> impl Widget {
} else {
Color::Red
};
span_buf.push(Span::styled(format!("{}% ", cpu), Style::default().fg(color)));
span_buf.push(Span::styled(
format!("{}% ", cpu),
Style::default().fg(color),
));
}
span_buf.push(Span::styled(" ] ", Style::default().fg(Color::Green)));
Block::new().borders(Borders::TOP).title(span_buf)
}
}

View File

@ -1,4 +1,15 @@
mod cpu;
pub use cpu::cpu_display;
mod network_sparkline;
pub use network_sparkline::*;
pub use network_sparkline::*;
use ratatui::widgets::Widget;
pub enum MainWidget {
Hosts,
}
impl MainWidget {
pub fn render(&self) -> impl Widget + '_ {
ratatui::widgets::Block::new()
}
}