Files
ember-tune-rs/src/agent_integrator/mod.rs
2026-02-27 17:04:47 +01:00

116 lines
4.3 KiB
Rust

//! System Service Integration (Agent Integrator)
//!
//! This module translates the mathematical optimums defined by the Analyst
//! into actionable, real-world Linux/OS service configurations.
//! It generates templates for fan daemons (i8kmon, thinkfan) and handles
//! resolution strategies for overlapping daemons.
use anyhow::Result;
use std::path::Path;
use std::fs;
use crate::agent_analyst::OptimizationMatrix;
pub struct ServiceIntegrator;
impl ServiceIntegrator {
/// Generates and saves an i8kmon configuration based on the balanced profile.
pub fn generate_i8kmon_config(matrix: &OptimizationMatrix, output_path: &Path) -> Result<()> {
let profile = &matrix.balanced;
let mut conf = String::new();
conf.push_str("# Auto-generated by ember-tune Integrator
");
conf.push_str(&format!("# Profile: {}
", profile.name));
for (i, p) in profile.fan_curve.iter().enumerate() {
// i8kmon syntax: set config(state) {left_fan right_fan temp_on temp_off}
// State 0, 1, 2, 3 correspond to BIOS fan states (off, low, high)
let state = match p.pwm_percent {
0..=20 => 0,
21..=50 => 1,
51..=100 => 2,
_ => 2,
};
let off = if i == 0 { "-".to_string() } else { format!("{}", p.temp_off) };
conf.push_str(&format!("set config({}) {{{} {} {} {}}}
", i, state, state, p.temp_on, off));
}
fs::write(output_path, conf)?;
Ok(())
}
/// Generates a thinkfan configuration.
pub fn generate_thinkfan_config(matrix: &OptimizationMatrix, output_path: &Path) -> Result<()> {
let profile = &matrix.balanced;
let mut conf = String::new();
conf.push_str("# Auto-generated by ember-tune Integrator
");
conf.push_str("sensors:
- hwmon: /sys/class/hwmon/hwmon0/temp1_input
");
conf.push_str("levels:
");
for (i, p) in profile.fan_curve.iter().enumerate() {
// thinkfan syntax: - [level, temp_down, temp_up]
let level = match p.pwm_percent {
0..=20 => 0,
21..=40 => 1,
41..=60 => 3,
61..=80 => 5,
_ => 7,
};
let down = if i == 0 { 0.0 } else { p.temp_off };
conf.push_str(&format!(" - [{}, {}, {}]
", level, down, p.temp_on));
}
fs::write(output_path, conf)?;
Ok(())
}
/// Generates a resolution checklist/script for daemons.
pub fn generate_conflict_resolution_script(output_path: &Path) -> Result<()> {
let script = r#"#!/bin/bash
# ember-tune Daemon Neutralization Script
# 1. Mask power-profiles-daemon (Prevent ACPI overrides)
systemctl mask power-profiles-daemon
# 2. Filter TLP (Prevent CPU governor fights while keeping PCIe saving)
sed -i 's/^CPU_SCALING_GOVERNOR_ON_AC=.*/CPU_SCALING_GOVERNOR_ON_AC=""/' /etc/tlp.conf
sed -i 's/^CPU_BOOST_ON_AC=.*/CPU_BOOST_ON_AC=""/' /etc/tlp.conf
systemctl restart tlp
# 3. Thermald Delegate (We provide the trips, it handles the rest)
# (Ensure your custom thermal-conf.xml is in /etc/thermald/)
systemctl restart thermald
"#;
fs::write(output_path, script)?;
Ok(())
}
/// Generates a thermald configuration XML.
pub fn generate_thermald_config(matrix: &OptimizationMatrix, output_path: &Path) -> Result<()> {
let profile = &matrix.balanced;
let mut xml = String::new();
xml.push_str("<?xml version=\"1.0\"?>\n<ThermalConfiguration>\n <Platform>\n <Name>ember-tune Balanced</Name>\n <ProductName>Generic</ProductName>\n <Preference>balanced</Preference>\n <ThermalZones>\n <ThermalZone>\n <Type>cpu</Type>\n <TripPoints>\n");
for (i, p) in profile.fan_curve.iter().enumerate() {
xml.push_str(&format!(" <TripPoint>\n <SensorType>cpu</SensorType>\n <Temperature>{}</Temperature>\n <Type>Passive</Type>\n <ControlId>{}</ControlId>\n </TripPoint>\n", p.temp_on * 1000.0, i));
}
xml.push_str(" </TripPoints>\n </ThermalZone>\n </ThermalZones>\n </Platform>\n</ThermalConfiguration>\n");
fs::write(output_path, xml)?;
Ok(())
}
}