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 ember_tune_rs::cli::Cli; use ember_tune_rs::mediator::{TelemetryState, UiCommand, BenchmarkPhase}; use ember_tune_rs::sal::traits::{AuditError, PlatformSal}; use ember_tune_rs::sal::mock::MockSal; use ember_tune_rs::sal::heuristic::engine::HeuristicEngine; use ember_tune_rs::sal::heuristic::discovery::SystemFactSheet; use ember_tune_rs::load::{StressNg}; use ember_tune_rs::orchestrator::BenchmarkOrchestrator; use ember_tune_rs::ui::dashboard::{draw_dashboard, DashboardState}; use ember_tune_rs::engine::OptimizationResult; use owo_colors::OwoColorize; #[derive(Error, Diagnostic, Debug)] #[error("Multiple pre-flight audit failures occurred.")] struct MultiAuditError { #[related] errors: Vec, } 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 these to your system:".bold().magenta()); for (id, path) in &result.config_paths { println!("│ {:<10}: {:<34} │", id, path.display()); } println!("╰──────────────────────────────────────────────────╯"); println!(); } fn setup_logging(verbose: bool) -> tracing_appender::non_blocking::WorkerGuard { let file_appender = tracing_appender::rolling::never("/var/log", "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); let ctx = ember_tune_rs::sal::traits::EnvironmentCtx::production(); // 2. Platform Detection & Audit let (sal_box, facts): (Box, SystemFactSheet) = if args.mock { (Box::new(MockSal::new()), SystemFactSheet::default()) } else { HeuristicEngine::detect_and_build(ctx)? }; let sal: Arc = sal_box.into(); println!("{}", console::style("─── Pre-flight System Audit ───").bold().cyan()); let mut audit_failures = Vec::new(); for step in sal.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 })); } if args.audit_only { println!("{}", console::style("āœ“ All pre-flight audits passed.").green().bold()); return Ok(()); } // 3. Terminal Setup enable_raw_mode().into_diagnostic()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen).into_diagnostic()?; let backend_stdout = io::stdout(); let backend_term = CrosstermBackend::new(backend_stdout); let mut terminal = Terminal::new(backend_term).into_diagnostic()?; // 4. State & Communication Setup let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); let (telemetry_tx, telemetry_rx) = mpsc::channel::(); let (command_tx, command_rx) = mpsc::channel::(); let c_tx = command_tx.clone(); ctrlc::set_handler(move || { let _ = c_tx.send(UiCommand::Abort); r.store(false, Ordering::SeqCst); }).expect("Error setting Ctrl-C handler"); // 5. Spawn Backend Orchestrator let sal_backend = sal.clone(); let facts_backend = facts.clone(); let config_out = args.config_out.clone(); let backend_handle = thread::spawn(move || { let workload = Box::new(StressNg::new()); let mut orchestrator = BenchmarkOrchestrator::new( sal_backend, facts_backend, workload, telemetry_tx, command_rx, config_out, ); 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, fans: Vec::new(), governor: "detecting".to_string(), pl1_limit: 0.0, pl2_limit: 0.0, fan_tier: "auto".to_string(), is_throttling: false, phase: BenchmarkPhase::Auditing, history_watts: Vec::new(), history_temp: Vec::new(), history_mhz: Vec::new(), log_event: None, metadata: std::collections::HashMap::new(), is_emergency: false, emergency_reason: None, }; 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.add_log(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() { break; } } // 7. Terminal Restoration let _ = disable_raw_mode(); let _ = execute!(terminal.backend_mut(), LeaveAlternateScreen); let _ = terminal.show_cursor(); // 8. Final Report & Hardware Restoration let join_res = backend_handle.join(); // Explicit hardware restoration info!("Restoring hardware state..."); if let Err(e) = sal.restore() { error!("Failed to restore hardware state: {}", e); } match join_res { Ok(Ok(result)) => { print_summary_report(&result); } Ok(Err(e)) => { let err_str = e.to_string(); if err_str == "ABORTED" { println!("{}", "Benchmark aborted by user.".yellow()); } else if err_str.contains("EMERGENCY_ABORT") { println!(); println!("{}", " 🚨 EMERGENCY ABORT TRIGGERED ".bold().on_red().white()); println!("Reason: {}", err_str.replace("EMERGENCY_ABORT: ", "").red().bold()); println!("{}", "Hardware state has been restored to safe defaults.".yellow()); println!(); } else { error!("Orchestrator encountered error: {}", e); eprintln!("{} {}", "Error:".red().bold(), e); } } Err(_) => { error!("Backend thread panicked!"); } } info!("ember-tune exited gracefully."); Ok(()) }