xps 13 3980
This commit is contained in:
302
src/main.rs
Normal file
302
src/main.rs
Normal file
@@ -0,0 +1,302 @@
|
||||
mod mediator;
|
||||
mod sal;
|
||||
mod load;
|
||||
mod orchestrator;
|
||||
mod ui;
|
||||
mod engine;
|
||||
mod cli;
|
||||
|
||||
use miette::{Result, IntoDiagnostic, Diagnostic, Report};
|
||||
use thiserror::Error;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::io;
|
||||
|
||||
use clap::Parser;
|
||||
use tracing::{info, debug, error};
|
||||
|
||||
use crossterm::{
|
||||
event::{self, Event, KeyCode},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
use cli::Cli;
|
||||
use mediator::{TelemetryState, UiCommand, BenchmarkPhase};
|
||||
use sal::traits::{PreflightAuditor, EnvironmentGuard, SensorBus, ActuatorBus, HardwareWatchdog, AuditError};
|
||||
use sal::mock::{MockAuditor, MockGuard, MockSensorBus, MockActuatorBus, MockWatchdog};
|
||||
use sal::dell_xps_9380::DellXps9380Sal;
|
||||
use load::StressNg;
|
||||
use orchestrator::BenchmarkOrchestrator;
|
||||
use ui::dashboard::{draw_dashboard, DashboardState};
|
||||
use engine::OptimizationResult;
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
#[derive(Error, Diagnostic, Debug)]
|
||||
#[error("Multiple pre-flight audit failures occurred.")]
|
||||
struct MultiAuditError {
|
||||
#[related]
|
||||
errors: Vec<AuditError>,
|
||||
}
|
||||
|
||||
fn print_summary_report(result: &OptimizationResult) {
|
||||
println!();
|
||||
let header = format!("{}", " 🔥 ember-tune optimization complete! ".bold().on_red().white());
|
||||
println!("╭──────────────────────────────────────────────────╮");
|
||||
println!("│ {:<48} │", header);
|
||||
println!("├──────────────────────────────────────────────────┤");
|
||||
|
||||
if result.is_partial {
|
||||
println!("│ {} │", " ⚠ WARNING: PARTIAL RESULTS (INTERRUPTED) ".yellow().bold());
|
||||
println!("├──────────────────────────────────────────────────┤");
|
||||
}
|
||||
|
||||
let knee_label = "Silicon Knee:".cyan();
|
||||
println!("│ {:<28} {:>5.1} W / {:>3.0}°C │", knee_label, result.silicon_knee_watts, result.max_temp_c);
|
||||
|
||||
let res_label = "Thermal Resistance (Rθ):".cyan();
|
||||
println!("│ {:<28} {:>8.3} K/W │", res_label, result.thermal_resistance_kw);
|
||||
|
||||
println!("│ │");
|
||||
println!("│ {} │", "Recommended Limits:".bold().green());
|
||||
println!("│ Sustained (PL1): {:>5.1} W │", result.recommended_pl1);
|
||||
println!("│ Burst (PL2): {:>5.1} W │", result.recommended_pl2);
|
||||
|
||||
println!("│ │");
|
||||
println!("│ {} │", "Apply to /etc/throttled.conf:".bold().magenta());
|
||||
println!("│ PL1_Tdp_W: {:<5.1} │", result.recommended_pl1);
|
||||
println!("│ PL2_Tdp_W: {:<5.1} │", result.recommended_pl2);
|
||||
println!("╰──────────────────────────────────────────────────╯");
|
||||
println!();
|
||||
}
|
||||
|
||||
fn setup_logging(verbose: bool) -> tracing_appender::non_blocking::WorkerGuard {
|
||||
let file_appender = tracing_appender::rolling::never("/tmp", "ember-tune.log");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
let level = if verbose { tracing::Level::DEBUG } else { tracing::Level::INFO };
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(level)
|
||||
.with_writer(non_blocking)
|
||||
.with_ansi(false)
|
||||
.init();
|
||||
|
||||
guard
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// 1. Diagnostics & CLI Initialization
|
||||
let args = Cli::parse();
|
||||
let _log_guard = setup_logging(args.verbose);
|
||||
|
||||
// Set panic hook to restore terminal state
|
||||
std::panic::set_hook(Box::new(|panic_info| {
|
||||
let _ = disable_raw_mode();
|
||||
let mut stdout = io::stdout();
|
||||
let _ = execute!(stdout, LeaveAlternateScreen, crossterm::cursor::Show);
|
||||
eprintln!("\n\x1b[1;31mFATAL ERROR: ember-tune Panicked\x1b[0m");
|
||||
eprintln!("----------------------------------------");
|
||||
eprintln!("{}", panic_info);
|
||||
eprintln!("----------------------------------------\n");
|
||||
}));
|
||||
|
||||
info!("ember-tune starting with args: {:?}", args);
|
||||
|
||||
// 2. Pre-flight Audit (Before TUI)
|
||||
let auditor: Arc<dyn PreflightAuditor> = if args.mock {
|
||||
Arc::new(MockAuditor)
|
||||
} else {
|
||||
match DellXps9380Sal::init() {
|
||||
Ok(sal) => Arc::new(sal),
|
||||
Err(e) => return Err(miette::miette!("Failed to initialize Dell SAL: {}", e)),
|
||||
}
|
||||
};
|
||||
|
||||
println!("{}", console::style("─── Pre-flight System Audit ───").bold().cyan());
|
||||
let mut audit_failures = Vec::new();
|
||||
|
||||
for step in auditor.audit() {
|
||||
print!(" Checking {:<40} ", step.description);
|
||||
io::Write::flush(&mut io::stdout()).into_diagnostic()?;
|
||||
|
||||
match step.outcome {
|
||||
Ok(_) => {
|
||||
println!("{}", console::style("[✓]").green());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{}", console::style("[✗]").red());
|
||||
audit_failures.push(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !audit_failures.is_empty() {
|
||||
println!();
|
||||
return Err(Report::new(MultiAuditError { errors: audit_failures }));
|
||||
}
|
||||
|
||||
println!("{}", console::style("✓ All pre-flight audits passed.").green().bold());
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
|
||||
if args.audit_only {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 3. Terminal Setup
|
||||
enable_raw_mode().into_diagnostic()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen).into_diagnostic()?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend).into_diagnostic()?;
|
||||
|
||||
// 4. State & Communication Setup
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let r = running.clone();
|
||||
|
||||
let (telemetry_tx, telemetry_rx) = mpsc::channel::<TelemetryState>();
|
||||
let (command_tx, command_rx) = mpsc::channel::<UiCommand>();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
r.store(false, Ordering::SeqCst);
|
||||
}).expect("Error setting Ctrl-C handler");
|
||||
|
||||
// 5. Spawn Backend Orchestrator
|
||||
let is_mock = args.mock;
|
||||
let b_auditor = auditor.clone();
|
||||
let backend_handle = thread::spawn(move || {
|
||||
let (guard, sensors, actuators, watchdog): (
|
||||
Box<dyn EnvironmentGuard>,
|
||||
Box<dyn SensorBus>,
|
||||
Box<dyn ActuatorBus>,
|
||||
Box<dyn HardwareWatchdog>,
|
||||
) = if is_mock {
|
||||
(
|
||||
Box::new(MockGuard::new()),
|
||||
Box::new(MockSensorBus),
|
||||
Box::new(MockActuatorBus),
|
||||
Box::new(MockWatchdog),
|
||||
)
|
||||
} else {
|
||||
// Re-init or share the SAL
|
||||
let sal = Arc::new(DellXps9380Sal::init().expect("Failed to init Dell SAL in backend"));
|
||||
(
|
||||
Box::new(sal::dell_xps_9380::DellXps9380Guard::new()),
|
||||
Box::new(sal.clone() as Arc<dyn SensorBus>),
|
||||
Box::new(sal.clone() as Arc<dyn ActuatorBus>),
|
||||
Box::new(sal as Arc<dyn HardwareWatchdog>),
|
||||
)
|
||||
};
|
||||
|
||||
let workload = Box::new(StressNg::new());
|
||||
|
||||
let mut orchestrator = BenchmarkOrchestrator::new(
|
||||
Box::new(b_auditor),
|
||||
guard,
|
||||
sensors,
|
||||
actuators,
|
||||
watchdog,
|
||||
workload,
|
||||
telemetry_tx,
|
||||
command_rx,
|
||||
);
|
||||
|
||||
orchestrator.run()
|
||||
});
|
||||
|
||||
// 6. Frontend Event Loop
|
||||
let mut ui_state = DashboardState::new();
|
||||
let mut last_telemetry = TelemetryState {
|
||||
cpu_model: "Loading...".to_string(),
|
||||
total_ram_gb: 0,
|
||||
tick: 0,
|
||||
cpu_temp: 0.0,
|
||||
power_w: 0.0,
|
||||
current_freq: 0.0,
|
||||
fan_rpm: 0,
|
||||
governor: "detecting".to_string(),
|
||||
pl1_limit: 0.0,
|
||||
pl2_limit: 0.0,
|
||||
fan_tier: "auto".to_string(),
|
||||
phase: BenchmarkPhase::Auditing,
|
||||
history_watts: Vec::new(),
|
||||
history_temp: Vec::new(),
|
||||
history_mhz: Vec::new(),
|
||||
log_event: None,
|
||||
metadata: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let tick_rate = Duration::from_millis(100);
|
||||
let mut last_tick = Instant::now();
|
||||
|
||||
while running.load(Ordering::SeqCst) {
|
||||
terminal.draw(|f| {
|
||||
draw_dashboard(f, f.area(), &last_telemetry, &ui_state);
|
||||
}).into_diagnostic()?;
|
||||
|
||||
let timeout = tick_rate
|
||||
.checked_sub(last_tick.elapsed())
|
||||
.unwrap_or(Duration::from_secs(0));
|
||||
|
||||
if event::poll(timeout).into_diagnostic()? {
|
||||
if let Event::Key(key) = event::read().into_diagnostic()? {
|
||||
match key.code {
|
||||
KeyCode::Char('q') | KeyCode::Esc => {
|
||||
let _ = command_tx.send(UiCommand::Abort);
|
||||
running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Ok(new_state) = telemetry_rx.try_recv() {
|
||||
if let Some(log) = &new_state.log_event {
|
||||
ui_state.logs.push(log.clone());
|
||||
debug!("Backend Log: {}", log);
|
||||
} else {
|
||||
ui_state.update(&new_state);
|
||||
last_telemetry = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
if last_tick.elapsed() >= tick_rate {
|
||||
last_tick = Instant::now();
|
||||
}
|
||||
|
||||
if backend_handle.is_finished() {
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Terminal Restoration
|
||||
disable_raw_mode().into_diagnostic()?;
|
||||
execute!(terminal.backend_mut(), LeaveAlternateScreen).into_diagnostic()?;
|
||||
terminal.show_cursor().into_diagnostic()?;
|
||||
|
||||
// 8. Final Report (Post-TUI)
|
||||
match backend_handle.join() {
|
||||
Ok(Ok(result)) => {
|
||||
print_summary_report(&result);
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
if e.to_string() == "ABORTED" {
|
||||
println!("{}", "Benchmark aborted by user. No summary available.".yellow());
|
||||
} else {
|
||||
error!("Orchestrator encountered error: {}", e);
|
||||
eprintln!("{} {}", "Error:".red().bold(), e);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Backend thread panicked!");
|
||||
}
|
||||
}
|
||||
|
||||
info!("ember-tune exited gracefully.");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user