use crate::{
device_info::{ChargingStatus, MobileDeviceInfo},
thermal_power::{ThermalPowerConfig, ThermalPowerManager},
MobileConfig,
};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::time::{Duration, Instant};
use trustformers_core::error::{CoreError, Result};
use trustformers_core::TrustformersError;
pub struct MobileBatteryManager {
config: BatteryConfig,
battery_monitor: BatteryMonitor,
power_predictor: PowerPredictor,
adaptive_scheduler: AdaptiveInferenceScheduler,
battery_optimizer: BatteryOptimizer,
usage_analytics: BatteryUsageAnalytics,
thermal_power_manager: Option<ThermalPowerManager>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatteryConfig {
pub enable_monitoring: bool,
pub monitoring_interval_ms: u64,
pub enable_prediction: bool,
pub prediction_window_minutes: u32,
pub enable_adaptive_quality: bool,
pub quality_strategy: QualityAdaptationStrategy,
pub battery_thresholds: BatteryThresholds,
pub power_limits: PowerUsageLimits,
pub enable_analytics: bool,
pub max_history_size: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatteryThresholds {
pub critical_percent: u8,
pub low_percent: u8,
pub medium_percent: u8,
pub high_percent: u8,
pub time_thresholds: TimeThresholds,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeThresholds {
pub critical_minutes: u32,
pub low_minutes: u32,
pub medium_minutes: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PowerUsageLimits {
pub max_power_on_battery_mw: f32,
pub max_power_when_charging_mw: f32,
pub max_background_power_mw: f32,
pub battery_level_budgets: HashMap<BatteryLevel, f32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BatteryLevel {
Critical,
Low,
Medium,
High,
Full,
Charging,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum QualityAdaptationStrategy {
None,
Linear,
Exponential,
Stepped,
Predictive,
UserPreference,
}
struct BatteryMonitor {
current_level: Option<u8>,
charging_status: ChargingStatus,
voltage: Option<f32>,
current_ma: Option<f32>,
temperature_celsius: Option<f32>,
capacity_mah: Option<u32>,
cycle_count: Option<u32>,
health_percent: Option<u8>,
battery_history: VecDeque<BatteryReading>,
last_update: Instant,
update_interval: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatteryReading {
#[serde(skip, default = "Instant::now")]
pub timestamp: Instant,
pub level_percent: Option<u8>,
pub charging_status: ChargingStatus,
pub voltage: Option<f32>,
pub current_ma: Option<f32>,
pub temperature_celsius: Option<f32>,
pub power_consumption_mw: Option<f32>,
pub estimated_time_remaining_minutes: Option<u32>,
}
struct PowerPredictor {
usage_patterns: Vec<UsagePattern>,
prediction_models: HashMap<String, PredictionModel>,
historical_data: VecDeque<PowerDataPoint>,
accuracy_metrics: PredictionAccuracyMetrics,
}
#[derive(Debug, Clone)]
struct UsagePattern {
time_of_day: u8, day_of_week: u8, app_context: String, inference_frequency: f32, average_power_mw: f32, duration_minutes: u32, }
#[derive(Debug, Clone)]
struct PredictionModel {
model_type: ModelType,
parameters: Vec<f32>,
accuracy: f32,
last_updated: Instant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ModelType {
Linear,
Exponential,
MovingAverage,
ARIMA,
}
#[derive(Debug, Clone)]
struct PowerDataPoint {
timestamp: Instant,
power_mw: f32,
battery_level: u8,
inference_count: u32,
context: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PredictionAccuracyMetrics {
pub mean_absolute_error: f32,
pub root_mean_square_error: f32,
pub mean_absolute_percentage_error: f32,
pub prediction_confidence: f32,
}
struct AdaptiveInferenceScheduler {
inference_queue: VecDeque<AdaptiveInferenceRequest>,
quality_levels: QualityLevelConfig,
current_quality_level: QualityLevel,
adaptation_history: VecDeque<QualityAdaptation>,
}
#[derive(Debug, Clone)]
struct AdaptiveInferenceRequest {
id: String,
priority: InferencePriority,
quality_requirements: QualityRequirements,
power_budget_mw: Option<f32>,
deadline: Option<Instant>,
adaptable_quality: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum InferencePriority {
Background,
Normal,
High,
Critical,
RealTime,
}
#[derive(Debug, Clone)]
struct QualityRequirements {
min_quality: f32,
target_quality: f32,
max_latency_ms: Option<u32>,
accuracy_tolerance: f32,
}
#[derive(Debug, Clone)]
struct QualityLevelConfig {
levels: Vec<QualityLevel>,
current_index: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum QualityLevel {
Minimal, Low, Medium, High, Maximum, }
#[derive(Debug, Clone)]
struct QualityAdaptation {
timestamp: Instant,
from_level: QualityLevel,
to_level: QualityLevel,
reason: AdaptationReason,
battery_level: u8,
power_consumption: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AdaptationReason {
BatteryLevel,
PowerLimit,
ThermalThrottling,
UserPreference,
PredictiveOptimization,
}
struct BatteryOptimizer {
optimization_strategies: Vec<OptimizationStrategy>,
optimization_history: VecDeque<OptimizationAction>,
effectiveness_metrics: OptimizationEffectiveness,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OptimizationStrategy {
ReduceFrequency,
ReducePrecision,
UseSmallerModel,
BatchInferences,
DeferInferences,
OffloadToEdge,
}
#[derive(Debug, Clone)]
struct OptimizationAction {
timestamp: Instant,
strategy: OptimizationStrategy,
battery_level_before: u8,
battery_level_after: u8,
power_savings_mw: f32,
quality_impact: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationEffectiveness {
pub total_power_saved_mwh: f32,
pub battery_life_extension_minutes: u32,
pub average_quality_impact: f32,
pub successful_optimizations: usize,
pub failed_optimizations: usize,
}
pub struct BatteryUsageAnalytics {
usage_sessions: VecDeque<UsageSession>,
daily_summaries: HashMap<String, DailySummary>, weekly_patterns: WeeklyUsagePattern,
optimization_recommendations: Vec<OptimizationRecommendation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsageSession {
#[serde(skip, default = "Instant::now")]
pub start_time: Instant,
#[serde(skip, default = "Instant::now")]
pub end_time: Instant,
pub start_battery_level: u8,
pub end_battery_level: u8,
pub total_inferences: u32,
pub average_power_mw: f32,
pub peak_power_mw: f32,
pub thermal_throttling_events: u32,
pub quality_adaptations: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DailySummary {
pub date: String,
pub total_usage_time_minutes: u32,
pub total_inferences: u32,
pub average_battery_drain_per_hour: f32,
pub peak_power_consumption_mw: f32,
pub thermal_events: u32,
pub charging_sessions: u32,
pub efficiency_score: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WeeklyUsagePattern {
pub peak_usage_hours: Vec<u8>,
pub average_daily_usage_minutes: f32,
pub most_power_intensive_day: String,
pub battery_health_trend: f32,
pub optimization_opportunities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationRecommendation {
pub recommendation_type: String,
pub description: String,
pub estimated_power_savings_percent: f32,
pub estimated_quality_impact_percent: f32,
pub implementation_difficulty: DifficultyLevel,
pub confidence_score: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DifficultyLevel {
Easy,
Medium,
Hard,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatteryStats {
pub current_level_percent: Option<u8>,
pub charging_status: ChargingStatus,
pub estimated_time_remaining_minutes: Option<u32>,
pub current_power_consumption_mw: Option<f32>,
pub average_power_consumption_mw: f32,
pub peak_power_consumption_mw: f32,
pub battery_time_saved_minutes: u32,
pub current_quality_level: QualityLevel,
pub recent_quality_adaptations: u32,
pub battery_health_percent: Option<u8>,
pub optimization_effectiveness: OptimizationEffectiveness,
}
impl Default for BatteryConfig {
fn default() -> Self {
Self {
enable_monitoring: true,
monitoring_interval_ms: 5000, enable_prediction: true,
prediction_window_minutes: 60, enable_adaptive_quality: true,
quality_strategy: QualityAdaptationStrategy::Predictive,
battery_thresholds: BatteryThresholds::default(),
power_limits: PowerUsageLimits::default(),
enable_analytics: true,
max_history_size: 1000,
}
}
}
impl Default for BatteryThresholds {
fn default() -> Self {
Self {
critical_percent: 15,
low_percent: 30,
medium_percent: 50,
high_percent: 80,
time_thresholds: TimeThresholds {
critical_minutes: 30,
low_minutes: 60,
medium_minutes: 120,
},
}
}
}
impl Default for PowerUsageLimits {
fn default() -> Self {
let mut battery_budgets = HashMap::new();
battery_budgets.insert(BatteryLevel::Critical, 1000.0); battery_budgets.insert(BatteryLevel::Low, 2000.0); battery_budgets.insert(BatteryLevel::Medium, 3000.0); battery_budgets.insert(BatteryLevel::High, 4000.0); battery_budgets.insert(BatteryLevel::Charging, 6000.0);
Self {
max_power_on_battery_mw: 4000.0, max_power_when_charging_mw: 8000.0, max_background_power_mw: 1500.0, battery_level_budgets: battery_budgets,
}
}
}
impl MobileBatteryManager {
pub fn new(config: BatteryConfig, device_info: &MobileDeviceInfo) -> Result<Self> {
let battery_monitor = BatteryMonitor::new(
Duration::from_millis(config.monitoring_interval_ms),
config.max_history_size,
);
let power_predictor = PowerPredictor::new(config.prediction_window_minutes);
let adaptive_scheduler = AdaptiveInferenceScheduler::new(&config);
let battery_optimizer = BatteryOptimizer::new();
let usage_analytics = BatteryUsageAnalytics::new(config.max_history_size);
let thermal_power_manager = if config.enable_monitoring {
let thermal_config = ThermalPowerConfig::default();
Some(ThermalPowerManager::new(thermal_config, device_info)?)
} else {
None
};
Ok(Self {
config,
battery_monitor,
power_predictor,
adaptive_scheduler,
battery_optimizer,
usage_analytics,
thermal_power_manager,
})
}
pub fn start(&mut self) -> Result<()> {
self.battery_monitor.start()?;
if let Some(ref mut thermal_manager) = self.thermal_power_manager {
thermal_manager.start_monitoring()?;
}
tracing::info!("Battery management started");
Ok(())
}
pub fn stop(&mut self) {
self.battery_monitor.stop();
if let Some(ref mut thermal_manager) = self.thermal_power_manager {
thermal_manager.stop_monitoring();
}
tracing::info!("Battery management stopped");
}
pub fn update(&mut self, mobile_config: &mut MobileConfig) -> Result<bool> {
let mut config_changed = false;
self.battery_monitor.update()?;
if let Some(ref mut thermal_manager) = self.thermal_power_manager {
if thermal_manager.update(mobile_config)? {
config_changed = true;
}
}
if self.config.enable_prediction {
self.power_predictor.update(&self.battery_monitor)?;
}
if self.should_apply_battery_optimizations()?
&& self.apply_battery_optimizations(mobile_config)?
{
config_changed = true;
}
if self.config.enable_adaptive_quality {
self.update_adaptive_quality(mobile_config)?;
}
if self.config.enable_analytics {
self.usage_analytics.update(&self.battery_monitor, mobile_config);
}
Ok(config_changed)
}
pub fn get_stats(&self) -> BatteryStats {
BatteryStats {
current_level_percent: self.battery_monitor.current_level,
charging_status: self.battery_monitor.charging_status,
estimated_time_remaining_minutes: self.estimate_time_remaining(),
current_power_consumption_mw: self.get_current_power_consumption(),
average_power_consumption_mw: self.calculate_average_power_consumption(),
peak_power_consumption_mw: self.get_peak_power_consumption(),
battery_time_saved_minutes: self.calculate_battery_time_saved(),
current_quality_level: self.adaptive_scheduler.current_quality_level,
recent_quality_adaptations: self.count_recent_quality_adaptations(),
battery_health_percent: self.battery_monitor.health_percent,
optimization_effectiveness: self.battery_optimizer.effectiveness_metrics.clone(),
}
}
pub fn predict_power_consumption(&self, duration_minutes: u32) -> Result<PowerPrediction> {
if !self.config.enable_prediction {
return Err(TrustformersError::config_error(
"Power prediction not enabled",
"predict_power_consumption",
)
.into());
}
self.power_predictor.predict_consumption(duration_minutes)
}
pub fn get_current_reading(&self) -> Result<BatteryReading> {
self.battery_monitor
.battery_history
.back()
.cloned()
.ok_or_else(|| TrustformersError::runtime_error("No battery reading available".into()))
.map_err(|e| e.into())
}
pub fn get_optimization_recommendations(&self) -> &[OptimizationRecommendation] {
&self.usage_analytics.optimization_recommendations
}
pub fn get_usage_analytics(&self) -> BatteryUsageAnalytics {
self.usage_analytics.clone()
}
pub fn create_battery_optimized_config(
base_config: &MobileConfig,
battery_level: u8,
charging: bool,
) -> MobileConfig {
let mut optimized = base_config.clone();
let battery_category = Self::categorize_battery_level(battery_level, charging);
match battery_category {
BatteryLevel::Critical => {
optimized.memory_optimization = crate::MemoryOptimization::Maximum;
optimized.num_threads = 1;
optimized.enable_batching = false;
optimized.backend = crate::MobileBackend::CPU;
if let Some(ref mut quant) = optimized.quantization {
quant.scheme = crate::MobileQuantizationScheme::Int4;
quant.dynamic = true;
}
},
BatteryLevel::Low => {
optimized.memory_optimization = crate::MemoryOptimization::Balanced;
optimized.num_threads = (optimized.num_threads / 2).max(1);
optimized.max_batch_size = (optimized.max_batch_size / 2).max(1);
},
BatteryLevel::Medium => {
optimized.num_threads = (optimized.num_threads * 3 / 4).max(1);
},
BatteryLevel::High | BatteryLevel::Full | BatteryLevel::Charging => {
},
}
optimized
}
fn should_apply_battery_optimizations(&self) -> Result<bool> {
let battery_level = self.battery_monitor.current_level.unwrap_or(100);
let is_charging = matches!(
self.battery_monitor.charging_status,
ChargingStatus::Charging
);
Ok(battery_level < self.config.battery_thresholds.medium_percent && !is_charging)
}
fn apply_battery_optimizations(&mut self, config: &mut MobileConfig) -> Result<bool> {
let battery_level = self.battery_monitor.current_level.unwrap_or(100);
let is_charging = matches!(
self.battery_monitor.charging_status,
ChargingStatus::Charging
);
if is_charging {
return Ok(false); }
let mut changed = false;
if battery_level < self.config.battery_thresholds.critical_percent {
let strategies = vec![
OptimizationStrategy::ReduceFrequency,
OptimizationStrategy::ReducePrecision,
OptimizationStrategy::DeferInferences,
];
for strategy in strategies {
if self.battery_optimizer.apply_strategy(strategy, config)? {
changed = true;
}
}
} else if battery_level < self.config.battery_thresholds.low_percent {
let strategies = vec![
OptimizationStrategy::BatchInferences,
OptimizationStrategy::ReducePrecision,
];
for strategy in strategies {
if self.battery_optimizer.apply_strategy(strategy, config)? {
changed = true;
}
}
}
Ok(changed)
}
fn update_adaptive_quality(&mut self, _config: &mut MobileConfig) -> Result<()> {
let battery_level = self.battery_monitor.current_level.unwrap_or(100);
let battery_category = Self::categorize_battery_level(
battery_level,
matches!(
self.battery_monitor.charging_status,
ChargingStatus::Charging
),
);
let target_quality = match battery_category {
BatteryLevel::Critical => QualityLevel::Minimal,
BatteryLevel::Low => QualityLevel::Low,
BatteryLevel::Medium => QualityLevel::Medium,
BatteryLevel::High => QualityLevel::High,
BatteryLevel::Full => QualityLevel::Maximum,
BatteryLevel::Charging => QualityLevel::Maximum,
};
if target_quality != self.adaptive_scheduler.current_quality_level {
self.adaptive_scheduler.adapt_quality(
target_quality,
AdaptationReason::BatteryLevel,
battery_level,
);
}
Ok(())
}
fn categorize_battery_level(level: u8, charging: bool) -> BatteryLevel {
if charging {
BatteryLevel::Charging
} else if level < 15 {
BatteryLevel::Critical
} else if level < 30 {
BatteryLevel::Low
} else if level < 60 {
BatteryLevel::Medium
} else {
BatteryLevel::High
}
}
fn estimate_time_remaining(&self) -> Option<u32> {
Some(120) }
fn get_current_power_consumption(&self) -> Option<f32> {
self.battery_monitor
.battery_history
.back()
.and_then(|reading| reading.power_consumption_mw)
}
fn calculate_average_power_consumption(&self) -> f32 {
if self.battery_monitor.battery_history.is_empty() {
return 0.0;
}
let sum: f32 = self
.battery_monitor
.battery_history
.iter()
.filter_map(|reading| reading.power_consumption_mw)
.sum();
let count = self
.battery_monitor
.battery_history
.iter()
.filter(|reading| reading.power_consumption_mw.is_some())
.count();
if count > 0 {
sum / count as f32
} else {
0.0
}
}
fn get_peak_power_consumption(&self) -> f32 {
self.battery_monitor
.battery_history
.iter()
.filter_map(|reading| reading.power_consumption_mw)
.fold(0.0, f32::max)
}
pub fn get_current_battery_level(&self) -> f32 {
match self.battery_monitor.current_level {
Some(level) => level as f32 / 100.0,
None => {
match self.battery_monitor.charging_status {
ChargingStatus::Charging => 0.85, ChargingStatus::NotCharging => 0.75, ChargingStatus::Discharging => 0.65, ChargingStatus::Full => 1.0, ChargingStatus::Unknown => 0.5, }
},
}
}
fn calculate_battery_time_saved(&self) -> u32 {
self.battery_optimizer.effectiveness_metrics.battery_life_extension_minutes
}
fn count_recent_quality_adaptations(&self) -> u32 {
let one_hour_ago = Instant::now() - Duration::from_secs(3600);
self.adaptive_scheduler
.adaptation_history
.iter()
.filter(|adaptation| adaptation.timestamp > one_hour_ago)
.count() as u32
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PowerPrediction {
pub predicted_consumption_mw: f32,
pub confidence_interval: (f32, f32),
pub accuracy_metrics: PredictionAccuracyMetrics,
pub factors: Vec<PredictionFactor>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PredictionFactor {
pub factor_name: String,
pub impact_weight: f32,
pub description: String,
}
impl BatteryMonitor {
fn new(update_interval: Duration, max_history: usize) -> Self {
Self {
current_level: None,
charging_status: ChargingStatus::Unknown,
voltage: None,
current_ma: None,
temperature_celsius: None,
capacity_mah: None,
cycle_count: None,
health_percent: None,
battery_history: VecDeque::with_capacity(max_history),
last_update: Instant::now(),
update_interval,
}
}
fn start(&mut self) -> Result<()> {
self.last_update = Instant::now();
Ok(())
}
fn stop(&mut self) {
}
fn update(&mut self) -> Result<()> {
if self.last_update.elapsed() >= self.update_interval {
let reading = self.read_battery_info()?;
self.battery_history.push_back(reading);
while self.battery_history.len() > self.battery_history.capacity() {
self.battery_history.pop_front();
}
self.last_update = Instant::now();
}
Ok(())
}
fn read_battery_info(&mut self) -> Result<BatteryReading> {
#[cfg(target_os = "android")]
{
self.read_android_battery_info()
}
#[cfg(target_os = "ios")]
{
self.read_ios_battery_info()
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
let level = Some(75u8);
self.current_level = level;
self.charging_status = ChargingStatus::Discharging;
Ok(BatteryReading {
timestamp: Instant::now(),
level_percent: level,
charging_status: ChargingStatus::Discharging,
voltage: Some(3.8),
current_ma: Some(-1500.0), temperature_celsius: Some(30.0),
power_consumption_mw: Some(2500.0),
estimated_time_remaining_minutes: Some(180),
})
}
}
#[cfg(target_os = "android")]
fn read_android_battery_info(&mut self) -> Result<BatteryReading> {
Ok(BatteryReading {
timestamp: Instant::now(),
level_percent: Some(80),
charging_status: ChargingStatus::Discharging,
voltage: Some(3.9),
current_ma: Some(-1200.0),
temperature_celsius: Some(32.0),
power_consumption_mw: Some(2200.0),
estimated_time_remaining_minutes: Some(200),
})
}
#[cfg(target_os = "ios")]
fn read_ios_battery_info(&mut self) -> Result<BatteryReading> {
Ok(BatteryReading {
timestamp: Instant::now(),
level_percent: Some(85),
charging_status: ChargingStatus::Discharging,
voltage: Some(3.85),
current_ma: Some(-1000.0),
temperature_celsius: Some(28.0),
power_consumption_mw: Some(1800.0),
estimated_time_remaining_minutes: Some(240),
})
}
}
impl PowerPredictor {
fn new(_prediction_window: u32) -> Self {
Self {
usage_patterns: Vec::new(),
prediction_models: HashMap::new(),
historical_data: VecDeque::new(),
accuracy_metrics: PredictionAccuracyMetrics {
mean_absolute_error: 0.0,
root_mean_square_error: 0.0,
mean_absolute_percentage_error: 0.0,
prediction_confidence: 0.8,
},
}
}
fn update(&mut self, _battery_monitor: &BatteryMonitor) -> Result<()> {
Ok(())
}
fn predict_consumption(&self, duration_minutes: u32) -> Result<PowerPrediction> {
let base_consumption = 2500.0; let predicted_consumption = base_consumption * (duration_minutes as f32 / 60.0);
Ok(PowerPrediction {
predicted_consumption_mw: predicted_consumption,
confidence_interval: (predicted_consumption * 0.8, predicted_consumption * 1.2),
accuracy_metrics: self.accuracy_metrics.clone(),
factors: vec![
PredictionFactor {
factor_name: "Base Consumption".to_string(),
impact_weight: 0.6,
description: "Baseline ML inference power consumption".to_string(),
},
PredictionFactor {
factor_name: "Usage Duration".to_string(),
impact_weight: 0.4,
description: "Expected inference duration".to_string(),
},
],
})
}
}
impl AdaptiveInferenceScheduler {
fn new(_config: &BatteryConfig) -> Self {
Self {
inference_queue: VecDeque::new(),
quality_levels: QualityLevelConfig {
levels: vec![
QualityLevel::Minimal,
QualityLevel::Low,
QualityLevel::Medium,
QualityLevel::High,
QualityLevel::Maximum,
],
current_index: 2, },
current_quality_level: QualityLevel::Medium,
adaptation_history: VecDeque::new(),
}
}
fn adapt_quality(
&mut self,
target_level: QualityLevel,
reason: AdaptationReason,
battery_level: u8,
) {
let old_level = self.current_quality_level;
self.current_quality_level = target_level;
let adaptation = QualityAdaptation {
timestamp: Instant::now(),
from_level: old_level,
to_level: target_level,
reason,
battery_level,
power_consumption: 2500.0, };
self.adaptation_history.push_back(adaptation);
while self.adaptation_history.len() > 100 {
self.adaptation_history.pop_front();
}
}
}
impl BatteryOptimizer {
fn new() -> Self {
Self {
optimization_strategies: vec![
OptimizationStrategy::ReduceFrequency,
OptimizationStrategy::ReducePrecision,
OptimizationStrategy::BatchInferences,
OptimizationStrategy::DeferInferences,
],
optimization_history: VecDeque::new(),
effectiveness_metrics: OptimizationEffectiveness {
total_power_saved_mwh: 0.0,
battery_life_extension_minutes: 0,
average_quality_impact: 0.0,
successful_optimizations: 0,
failed_optimizations: 0,
},
}
}
fn apply_strategy(
&mut self,
strategy: OptimizationStrategy,
_config: &mut MobileConfig,
) -> Result<bool> {
match strategy {
OptimizationStrategy::ReduceFrequency => {
tracing::info!("Applied strategy: Reduce inference frequency");
},
OptimizationStrategy::ReducePrecision => {
tracing::info!("Applied strategy: Reduce precision");
},
OptimizationStrategy::BatchInferences => {
tracing::info!("Applied strategy: Batch inferences");
},
OptimizationStrategy::DeferInferences => {
tracing::info!("Applied strategy: Defer inferences");
},
_ => {
tracing::info!("Applied strategy: {:?}", strategy);
},
}
self.effectiveness_metrics.successful_optimizations += 1;
Ok(true)
}
}
impl BatteryUsageAnalytics {
fn new(_max_history: usize) -> Self {
Self {
usage_sessions: VecDeque::new(),
daily_summaries: HashMap::new(),
weekly_patterns: WeeklyUsagePattern {
peak_usage_hours: vec![9, 14, 20], average_daily_usage_minutes: 120.0,
most_power_intensive_day: "Monday".to_string(),
battery_health_trend: 0.98, optimization_opportunities: vec![
"Reduce inference frequency during peak hours".to_string(),
"Use lower precision models after 8pm".to_string(),
],
},
optimization_recommendations: vec![OptimizationRecommendation {
recommendation_type: "Precision Optimization".to_string(),
description: "Use INT8 quantization during low battery periods".to_string(),
estimated_power_savings_percent: 15.0,
estimated_quality_impact_percent: 3.0,
implementation_difficulty: DifficultyLevel::Easy,
confidence_score: 0.85,
}],
}
}
fn update(&mut self, _battery_monitor: &BatteryMonitor, _config: &MobileConfig) {
}
}
impl Clone for BatteryUsageAnalytics {
fn clone(&self) -> Self {
Self {
usage_sessions: self.usage_sessions.clone(),
daily_summaries: self.daily_summaries.clone(),
weekly_patterns: self.weekly_patterns.clone(),
optimization_recommendations: self.optimization_recommendations.clone(),
}
}
}
pub struct BatteryUtils;
impl BatteryUtils {
pub fn calculate_drain_rate(readings: &[BatteryReading]) -> f32 {
if readings.len() < 2 {
return 0.0;
}
let first = &readings[0];
let last = &readings[readings.len() - 1];
if let (Some(first_level), Some(last_level)) = (first.level_percent, last.level_percent) {
let level_change = first_level as f32 - last_level as f32;
let time_hours = last.timestamp.duration_since(first.timestamp).as_secs_f32() / 3600.0;
if time_hours > 0.0 {
level_change / time_hours
} else {
0.0
}
} else {
0.0
}
}
pub fn estimate_remaining_time(
current_level: u8,
drain_rate_per_hour: f32,
) -> Option<Duration> {
if drain_rate_per_hour <= 0.0 {
return None; }
let hours_remaining = current_level as f32 / drain_rate_per_hour;
Some(Duration::from_secs_f32(hours_remaining * 3600.0))
}
pub fn calculate_efficiency_score(
power_consumption_mw: f32,
inference_count: u32,
time_duration_minutes: f32,
) -> f32 {
if time_duration_minutes <= 0.0 || inference_count == 0 {
return 0.0;
}
let inferences_per_minute = inference_count as f32 / time_duration_minutes;
let power_per_inference = power_consumption_mw / inference_count as f32;
let efficiency = inferences_per_minute / (power_per_inference / 1000.0);
(efficiency / 10.0).min(1.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_battery_config_default() {
let config = BatteryConfig::default();
assert!(config.enable_monitoring);
assert!(config.enable_prediction);
assert!(config.enable_adaptive_quality);
assert!(matches!(
config.quality_strategy,
QualityAdaptationStrategy::Predictive
));
}
#[test]
fn test_battery_thresholds() {
let thresholds = BatteryThresholds::default();
assert!(thresholds.critical_percent < thresholds.low_percent);
assert!(thresholds.low_percent < thresholds.medium_percent);
assert!(thresholds.medium_percent < thresholds.high_percent);
}
#[test]
fn test_battery_level_categorization() {
assert_eq!(
MobileBatteryManager::categorize_battery_level(10, false),
BatteryLevel::Critical
);
assert_eq!(
MobileBatteryManager::categorize_battery_level(25, false),
BatteryLevel::Low
);
assert_eq!(
MobileBatteryManager::categorize_battery_level(45, false),
BatteryLevel::Medium
);
assert_eq!(
MobileBatteryManager::categorize_battery_level(75, false),
BatteryLevel::High
);
assert_eq!(
MobileBatteryManager::categorize_battery_level(25, true),
BatteryLevel::Charging
);
}
#[test]
fn test_battery_optimized_config() {
let base_config = crate::MobileConfig::default();
let critical_config =
MobileBatteryManager::create_battery_optimized_config(&base_config, 10, false);
assert_eq!(
critical_config.memory_optimization,
crate::MemoryOptimization::Maximum
);
assert_eq!(critical_config.num_threads, 1);
assert!(!critical_config.enable_batching);
let charging_config =
MobileBatteryManager::create_battery_optimized_config(&base_config, 50, true);
}
#[test]
fn test_drain_rate_calculation() {
let readings = vec![
BatteryReading {
timestamp: Instant::now(),
level_percent: Some(100),
charging_status: ChargingStatus::Discharging,
voltage: None,
current_ma: None,
temperature_celsius: None,
power_consumption_mw: None,
estimated_time_remaining_minutes: None,
},
BatteryReading {
timestamp: Instant::now() + Duration::from_secs(3600), level_percent: Some(90),
charging_status: ChargingStatus::Discharging,
voltage: None,
current_ma: None,
temperature_celsius: None,
power_consumption_mw: None,
estimated_time_remaining_minutes: None,
},
];
let drain_rate = BatteryUtils::calculate_drain_rate(&readings);
assert!((drain_rate - 10.0).abs() < 0.1); }
#[test]
fn test_efficiency_score_calculation() {
let score = BatteryUtils::calculate_efficiency_score(2000.0, 100, 10.0);
assert!(score >= 0.0);
assert!(score <= 1.0);
}
#[test]
fn test_quality_levels() {
assert!(matches!(QualityLevel::Minimal, QualityLevel::Minimal));
assert!(matches!(QualityLevel::Maximum, QualityLevel::Maximum));
}
#[test]
fn test_power_usage_limits() {
let limits = PowerUsageLimits::default();
assert!(limits.max_power_on_battery_mw < limits.max_power_when_charging_mw);
assert!(limits.max_background_power_mw < limits.max_power_on_battery_mw);
assert!(
limits.battery_level_budgets[&BatteryLevel::Critical]
< limits.battery_level_budgets[&BatteryLevel::High]
);
}
#[test]
fn test_optimization_recommendation() {
let recommendation = OptimizationRecommendation {
recommendation_type: "Test".to_string(),
description: "Test recommendation".to_string(),
estimated_power_savings_percent: 10.0,
estimated_quality_impact_percent: 2.0,
implementation_difficulty: DifficultyLevel::Easy,
confidence_score: 0.9,
};
assert_eq!(recommendation.estimated_power_savings_percent, 10.0);
assert!(matches!(
recommendation.implementation_difficulty,
DifficultyLevel::Easy
));
}
}