summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Kobert <dennis@kobert.dev>2025-04-15 12:11:43 +0200
committerDennis Kobert <dennis@kobert.dev>2025-04-15 12:11:43 +0200
commit05a3d658aacd4cbec0521b94af7be6006d310bd1 (patch)
tree4f972d23d2b319f7cb3f928302e3a312a3a23eb9
parent5de74c2aefd1b44e3f0fb33de18b838f3ab1ed68 (diff)
Add power limit ui
-rw-r--r--energy-monitor/src/main.rs26
-rw-r--r--energy-monitor/src/ui.rs47
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)")