diff options
| author | Dennis Kobert <dennis@kobert.dev> | 2025-04-15 12:11:43 +0200 |
|---|---|---|
| committer | Dennis Kobert <dennis@kobert.dev> | 2025-04-15 12:11:43 +0200 |
| commit | 05a3d658aacd4cbec0521b94af7be6006d310bd1 (patch) | |
| tree | 4f972d23d2b319f7cb3f928302e3a312a3a23eb9 | |
| parent | 5de74c2aefd1b44e3f0fb33de18b838f3ab1ed68 (diff) | |
Add power limit ui
| -rw-r--r-- | energy-monitor/src/main.rs | 26 | ||||
| -rw-r--r-- | energy-monitor/src/ui.rs | 47 |
2 files changed, 57 insertions, 16 deletions
diff --git a/energy-monitor/src/main.rs b/energy-monitor/src/main.rs index b07bfc2..e608d72 100644 --- a/energy-monitor/src/main.rs +++ b/energy-monitor/src/main.rs @@ -24,10 +24,12 @@ use process::ProcessData; const SOCKET_PATH: &str = "/tmp/pm-sched"; const UPDATE_INTERVAL: Duration = Duration::from_secs(1); const MAX_HISTORY_SIZE: usize = 60; // Keep a minute's worth of data (at 1 sec update interval) +const POWER_LIMIT_STEP: f64 = 5.0; // 5W increment/decrement step +const DEFAULT_POWER_LIMIT: f64 = 50.0; // Default 50W limit enum AppTab { ProcessList, - DetailedView, + DetailedView(Option<Pid>), } struct App { @@ -40,6 +42,8 @@ struct App { color_index: usize, last_update: Instant, should_quit: bool, + power_limit: f64, + power_limit_history: Vec<(f64, f64)>, } impl App { @@ -70,6 +74,8 @@ impl App { color_index: 0, last_update: Instant::now(), should_quit: false, + power_limit: DEFAULT_POWER_LIMIT, + power_limit_history: vec![], }; // Start with the first process selected @@ -147,10 +153,22 @@ impl App { fn on_tab(&mut self) { self.selected_tab = match self.selected_tab { - AppTab::ProcessList => AppTab::DetailedView, - AppTab::DetailedView => AppTab::ProcessList, + AppTab::ProcessList => AppTab::DetailedView( + self.table_state + .selected() + .and_then(|i| self.process_data.processes.get(i).copied()), + ), + AppTab::DetailedView(_) => AppTab::ProcessList, }; } + + fn increase_power_limit(&mut self) { + self.power_limit += POWER_LIMIT_STEP; + } + + fn decrease_power_limit(&mut self) { + self.power_limit = (self.power_limit - POWER_LIMIT_STEP).max(0.0); + } } fn main() -> Result<(), Box<dyn Error>> { @@ -189,6 +207,8 @@ fn main() -> Result<(), Box<dyn Error>> { KeyCode::Down | KeyCode::Char('j') => app.on_down(), KeyCode::Char(' ') => app.on_toggle_select(), KeyCode::Tab => app.on_tab(), + KeyCode::Char('+') | KeyCode::Char('=') => app.increase_power_limit(), + KeyCode::Char('-') | KeyCode::Char('_') => app.decrease_power_limit(), _ => {} } } diff --git a/energy-monitor/src/ui.rs b/energy-monitor/src/ui.rs index 3a94e53..b35c619 100644 --- a/energy-monitor/src/ui.rs +++ b/energy-monitor/src/ui.rs @@ -39,7 +39,7 @@ pub fn draw(f: &mut Frame, app: &mut App) { ) .select(match app.selected_tab { AppTab::ProcessList => 0, - AppTab::DetailedView => 1, + AppTab::DetailedView(_) => 1, }) .style(Style::default().fg(Color::White)) .highlight_style(Style::default().fg(Color::Yellow)); @@ -48,7 +48,7 @@ pub fn draw(f: &mut Frame, app: &mut App) { // Draw content based on selected tab match app.selected_tab { AppTab::ProcessList => draw_process_list(f, app, chunks[1]), - AppTab::DetailedView => draw_detailed_view(f, app, chunks[1]), + AppTab::DetailedView(pid) => draw_detailed_view(f, app, chunks[1], pid), } // Draw footer with instructions @@ -61,6 +61,8 @@ pub fn draw(f: &mut Frame, app: &mut App) { Span::raw(" Select | "), Span::styled("Tab", Style::default().fg(Color::Yellow)), Span::raw(" Change Tab"), + Span::styled("+/-", Style::default().fg(Color::Yellow)), + Span::raw(" Adjust Power Limit"), ]; let footer = @@ -128,13 +130,8 @@ fn draw_process_list(f: &mut Frame, app: &mut App, area: Rect) { draw_energy_graph(f, app, chunks[1]); } -fn draw_detailed_view(f: &mut Frame, app: &mut App, area: Rect) { - let selected_pid = app - .table_state - .selected() - .and_then(|i| app.process_data.processes.get(i)); - - if let Some(pid) = selected_pid { +fn draw_detailed_view(f: &mut Frame, app: &mut App, area: Rect, selected_pid: Option<i32>) { + if let Some(pid) = selected_pid.as_ref() { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Length(5), Constraint::Min(0)]) @@ -192,14 +189,15 @@ fn draw_detailed_view(f: &mut Frame, app: &mut App, area: Rect) { let dataset = Dataset::default() .name(process.name.clone()) - .marker(symbols::Marker::Dot) + .marker(symbols::Marker::HalfBlock) .style( Style::default().fg(app .process_colors - .get(&pid) + .get(pid) .copied() .unwrap_or(Color::Yellow)), ) + .graph_type(ratatui::widgets::GraphType::Line) .data(&process.history); let chart = Chart::new(vec![dataset]) @@ -290,6 +288,7 @@ fn draw_energy_graph(f: &mut Frame, app: &mut App, area: Rect) { .name("Total System Energy") .marker(symbols::Marker::HalfBlock) .style(Style::default().fg(Color::White)) + .graph_type(ratatui::widgets::GraphType::Line) .data(&app.process_data.total_energy_history), ); } @@ -309,6 +308,7 @@ fn draw_energy_graph(f: &mut Frame, app: &mut App, area: Rect) { .copied() .unwrap_or(Color::White)), ) + .graph_type(ratatui::widgets::GraphType::Line) .data(&process.history), ); } @@ -330,6 +330,20 @@ fn draw_energy_graph(f: &mut Frame, app: &mut App, area: Rect) { .map(|(t, _)| *t) .unwrap_or(start_time + 60.0); + // Add power limit line as a horizontal line across the chart + if app.power_limit > 0.0 { + let limit_data = vec![(start_time, app.power_limit), (end_time, app.power_limit)]; + app.power_limit_history = limit_data; + datasets.push( + Dataset::default() + .name(format!("Power Limit ({:.1}W)", app.power_limit)) + .marker(symbols::Marker::Bar) + .style(Style::default().fg(Color::Red)) + .graph_type(ratatui::widgets::GraphType::Line) + .data(&app.power_limit_history), + ); + } + let max_total = app .process_data .total_energy_history @@ -344,11 +358,18 @@ fn draw_energy_graph(f: &mut Frame, app: &mut App, area: Rect) { .flat_map(|process| process.history.iter().map(|(_, e)| *e)) .fold(0.0f64, f64::max); - let max_energy = f64::max(max_total, max_processes); + // Ensure the y-axis upper bound includes the power limit, if set + let mut max_energy = f64::max(max_total, max_processes); + if app.power_limit > 0.0 { + max_energy = f64::max(max_energy, app.power_limit); + } // Create chart widget let chart = Chart::new(datasets) - .block(Block::default().borders(Borders::ALL).title("Energy Usage")) + .block(Block::default().borders(Borders::ALL).title(format!( + "Energy Usage (Power Limit: {:.1}W)", + app.power_limit + ))) .x_axis( Axis::default() .title("Time (s)") |
