use scirs2_core::ndarray::ShapeError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum VisionError {
#[error("Failed to load image: {0}")]
ImageLoadError(String),
#[error("Invalid parameter: {0}")]
InvalidParameter(String),
#[error("Operation failed: {0}")]
OperationError(String),
#[error("ndimage error: {0}")]
NdimageError(String),
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Type conversion error: {0}")]
TypeConversionError(String),
#[error("Shape error: {0}")]
ShapeError(#[from] ShapeError),
#[error("Linear algebra error: {0}")]
LinAlgError(String),
#[error("GPU error: {0}")]
GpuError(String),
#[error("Dimension mismatch: {0}")]
DimensionMismatch(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("{0}")]
Other(String),
}
impl Clone for VisionError {
fn clone(&self) -> Self {
match self {
VisionError::ImageLoadError(s) => VisionError::ImageLoadError(s.clone()),
VisionError::InvalidParameter(s) => VisionError::InvalidParameter(s.clone()),
VisionError::OperationError(s) => VisionError::OperationError(s.clone()),
VisionError::NdimageError(s) => VisionError::NdimageError(s.clone()),
VisionError::IoError(e) => VisionError::Other(format!("I/O error: {e}")),
VisionError::TypeConversionError(s) => VisionError::TypeConversionError(s.clone()),
VisionError::ShapeError(e) => VisionError::Other(format!("Shape error: {e}")),
VisionError::LinAlgError(s) => VisionError::LinAlgError(s.clone()),
VisionError::GpuError(s) => VisionError::GpuError(s.clone()),
VisionError::DimensionMismatch(s) => VisionError::DimensionMismatch(s.clone()),
VisionError::InvalidInput(s) => VisionError::InvalidInput(s.clone()),
VisionError::Other(s) => VisionError::Other(s.clone()),
}
}
}
impl From<scirs2_core::gpu::GpuError> for VisionError {
fn from(err: scirs2_core::gpu::GpuError) -> Self {
VisionError::GpuError(err.to_string())
}
}
pub type Result<T> = std::result::Result<T, VisionError>;
#[derive(Debug, Clone)]
pub enum RecoveryStrategy {
RetryWithReducedParams,
FallbackToCpu,
FallbackToScalar,
UseDefaultParams,
SkipOperation,
ReduceQuality,
AdaptiveAdjustment,
NoRecovery,
}
#[derive(Debug, Clone)]
pub struct ErrorContext {
pub operation: String,
pub parameters: std::collections::HashMap<String, String>,
pub system_state: SystemState,
pub recovery_strategies: Vec<RecoveryStrategy>,
pub severity: ErrorSeverity,
pub timestamp: std::time::Instant,
}
#[derive(Debug, Clone)]
pub struct SystemState {
pub available_memory: usize,
pub cpu_usage: f32,
pub gpu_available: bool,
pub simd_support: SimdSupport,
pub thread_count: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum SimdSupport {
None,
SSE,
AVX,
AVX2,
AVX512,
NEON,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct RecoverableVisionError {
pub base_error: VisionError,
pub context: ErrorContext,
pub recovery_attempts: Vec<RecoveryAttempt>,
pub is_recoverable: bool,
}
#[derive(Debug, Clone)]
pub struct RecoveryAttempt {
pub strategy: RecoveryStrategy,
pub success: bool,
pub duration: std::time::Duration,
pub details: String,
}
pub struct ErrorRecoveryManager {
config: RecoveryConfig,
error_history: std::collections::VecDeque<RecoverableVisionError>,
system_monitor: SystemStateMonitor,
recovery_stats: RecoveryStatistics,
}
#[derive(Debug, Clone)]
pub struct RecoveryConfig {
pub max_recovery_attempts: usize,
pub enable_adaptive_params: bool,
pub enable_performance_recovery: bool,
pub memory_threshold: usize,
pub cpu_threshold: f32,
pub enable_logging: bool,
pub max_error_history: usize,
}
pub struct SystemStateMonitor {
last_state: SystemState,
update_interval: std::time::Duration,
last_update: std::time::Instant,
}
#[derive(Debug, Default)]
pub struct RecoveryStatistics {
pub total_errors: usize,
pub successful_recoveries: usize,
pub failed_recoveries: usize,
pub strategy_success_rates: std::collections::HashMap<String, f32>,
pub avg_recovery_time: std::time::Duration,
pub common_errors: std::collections::HashMap<String, usize>,
}
impl Default for RecoveryConfig {
fn default() -> Self {
Self {
max_recovery_attempts: 3,
enable_adaptive_params: true,
enable_performance_recovery: true,
memory_threshold: 1_073_741_824, cpu_threshold: 80.0,
enable_logging: true,
max_error_history: 1000,
}
}
}
impl Default for SystemState {
fn default() -> Self {
Self {
available_memory: 2_147_483_648, cpu_usage: 0.0,
gpu_available: false,
simd_support: SimdSupport::None,
thread_count: 1,
}
}
}
impl SystemStateMonitor {
pub fn new() -> Self {
Self {
last_state: SystemState::default(),
update_interval: std::time::Duration::from_secs(1),
last_update: std::time::Instant::now(),
}
}
pub fn get_current_state(&mut self) -> &SystemState {
let now = std::time::Instant::now();
if now.duration_since(self.last_update) >= self.update_interval {
self.update_system_state();
self.last_update = now;
}
&self.last_state
}
fn update_system_state(&mut self) {
self.last_state.simd_support = detect_simd_support();
self.last_state.thread_count = num_cpus::get();
self.last_state.available_memory = 2_147_483_648; self.last_state.cpu_usage = 25.0;
self.last_state.gpu_available = check_gpu_availability();
}
}
impl Default for SystemStateMonitor {
fn default() -> Self {
Self::new()
}
}
impl ErrorRecoveryManager {
pub fn new(config: RecoveryConfig) -> Self {
Self {
config,
error_history: std::collections::VecDeque::with_capacity(1000),
system_monitor: SystemStateMonitor::new(),
recovery_stats: RecoveryStatistics::default(),
}
}
pub fn recover_from_error(
&mut self,
error: VisionError,
operation: &str,
parameters: std::collections::HashMap<String, String>,
) -> Result<RecoveryStrategy> {
let start_time = std::time::Instant::now();
let system_state = self.system_monitor.get_current_state().clone();
let recovery_strategies = self.analyze_error(&error, &system_state, operation);
let context = ErrorContext {
operation: operation.to_string(),
parameters,
system_state,
recovery_strategies: recovery_strategies.clone(),
severity: self.determine_error_severity(&error),
timestamp: start_time,
};
let mut recoverable_error = RecoverableVisionError {
base_error: error,
context,
recovery_attempts: Vec::new(),
is_recoverable: !recovery_strategies.is_empty(),
};
for strategy in recovery_strategies {
if self.attempt_recovery(&mut recoverable_error, strategy.clone()) {
self.record_successful_recovery(&recoverable_error, start_time.elapsed());
return Ok(strategy);
}
}
self.record_failed_recovery(&recoverable_error);
Err(recoverable_error.base_error)
}
fn analyze_error(
&self,
error: &VisionError,
system_state: &SystemState,
operation: &str,
) -> Vec<RecoveryStrategy> {
let mut strategies = Vec::new();
match error {
VisionError::OperationError(_) => {
if system_state.available_memory < self.config.memory_threshold {
strategies.push(RecoveryStrategy::ReduceQuality);
strategies.push(RecoveryStrategy::RetryWithReducedParams);
}
if system_state.cpu_usage > self.config.cpu_threshold {
strategies.push(RecoveryStrategy::FallbackToScalar);
}
if operation.contains("gpu") || operation.contains("GPU") {
strategies.push(RecoveryStrategy::FallbackToCpu);
}
if operation.contains("simd") || operation.contains("SIMD") {
strategies.push(RecoveryStrategy::FallbackToScalar);
}
strategies.push(RecoveryStrategy::UseDefaultParams);
strategies.push(RecoveryStrategy::AdaptiveAdjustment);
}
VisionError::InvalidParameter(_) => {
strategies.push(RecoveryStrategy::UseDefaultParams);
strategies.push(RecoveryStrategy::AdaptiveAdjustment);
strategies.push(RecoveryStrategy::RetryWithReducedParams);
}
VisionError::DimensionMismatch(_) | VisionError::ShapeError(_) => {
strategies.push(RecoveryStrategy::AdaptiveAdjustment);
strategies.push(RecoveryStrategy::UseDefaultParams);
}
VisionError::LinAlgError(_) => {
strategies.push(RecoveryStrategy::FallbackToScalar);
strategies.push(RecoveryStrategy::UseDefaultParams);
strategies.push(RecoveryStrategy::RetryWithReducedParams);
}
_ => {
strategies.push(RecoveryStrategy::UseDefaultParams);
strategies.push(RecoveryStrategy::SkipOperation);
}
}
strategies.sort_by_key(|s| format!("{s:?}"));
strategies.dedup_by_key(|s| format!("{s:?}"));
strategies
}
fn determine_error_severity(&self, error: &VisionError) -> ErrorSeverity {
match error {
VisionError::InvalidParameter(_) | VisionError::InvalidInput(_) => ErrorSeverity::Low,
VisionError::OperationError(_) | VisionError::TypeConversionError(_) => {
ErrorSeverity::Medium
}
VisionError::LinAlgError(_) | VisionError::DimensionMismatch(_) => ErrorSeverity::High,
VisionError::IoError(_) | VisionError::Other(_) => ErrorSeverity::Critical,
VisionError::ImageLoadError(_) => ErrorSeverity::High,
VisionError::NdimageError(_) => ErrorSeverity::Medium,
VisionError::ShapeError(_) => ErrorSeverity::Medium,
VisionError::GpuError(_) => ErrorSeverity::High,
}
}
fn attempt_recovery(
&mut self,
error: &mut RecoverableVisionError,
strategy: RecoveryStrategy,
) -> bool {
let start_time = std::time::Instant::now();
let success = match strategy {
RecoveryStrategy::FallbackToCpu | RecoveryStrategy::FallbackToScalar => true,
RecoveryStrategy::UseDefaultParams => true,
RecoveryStrategy::RetryWithReducedParams => true,
RecoveryStrategy::ReduceQuality => true,
RecoveryStrategy::AdaptiveAdjustment => true,
RecoveryStrategy::SkipOperation => true,
RecoveryStrategy::NoRecovery => false,
};
let attempt = RecoveryAttempt {
strategy: strategy.clone(),
success,
duration: start_time.elapsed(),
details: format!("Attempted {strategy:?} recovery"),
};
error.recovery_attempts.push(attempt);
success
}
fn record_successful_recovery(
&mut self,
error: &RecoverableVisionError,
total_duration: std::time::Duration,
) {
self.recovery_stats.total_errors += 1;
self.recovery_stats.successful_recoveries += 1;
for attempt in &error.recovery_attempts {
if attempt.success {
let strategy_name = format!("{:?}", attempt.strategy);
let current_rate = self
.recovery_stats
.strategy_success_rates
.get(&strategy_name)
.copied()
.unwrap_or(0.0);
let new_rate = (current_rate + 1.0) / 2.0;
self.recovery_stats
.strategy_success_rates
.insert(strategy_name, new_rate);
break;
}
}
let current_avg = self.recovery_stats.avg_recovery_time;
let avg_nanos = ((current_avg.as_nanos() + total_duration.as_nanos()) / 2)
.try_into()
.unwrap_or(u64::MAX);
let new_avg = std::time::Duration::from_nanos(avg_nanos);
self.recovery_stats.avg_recovery_time = new_avg;
self.add_to_error_history(error.clone());
if self.config.enable_logging {
eprintln!("Successfully recovered from error: {}", error.base_error);
}
}
fn record_failed_recovery(&mut self, error: &RecoverableVisionError) {
self.recovery_stats.total_errors += 1;
self.recovery_stats.failed_recoveries += 1;
let error_type = format!("{:?}", error.base_error);
let count = self
.recovery_stats
.common_errors
.get(&error_type)
.copied()
.unwrap_or(0);
self.recovery_stats
.common_errors
.insert(error_type, count + 1);
self.add_to_error_history(error.clone());
if self.config.enable_logging {
eprintln!("Failed to recover from error: {}", error.base_error);
}
}
fn add_to_error_history(&mut self, error: RecoverableVisionError) {
self.error_history.push_back(error);
if self.error_history.len() > self.config.max_error_history {
self.error_history.pop_front();
}
}
pub fn get_statistics(&self) -> &RecoveryStatistics {
&self.recovery_stats
}
pub fn generate_recovery_report(&self) -> String {
let mut report = String::new();
report.push_str("=== Error Recovery Report ===\n");
report.push_str(&format!(
"Total errors: {}\n",
self.recovery_stats.total_errors
));
report.push_str(&format!(
"Successful recoveries: {}\n",
self.recovery_stats.successful_recoveries
));
report.push_str(&format!(
"Failed recoveries: {}\n",
self.recovery_stats.failed_recoveries
));
let success_rate = if self.recovery_stats.total_errors > 0 {
self.recovery_stats.successful_recoveries as f32
/ self.recovery_stats.total_errors as f32
* 100.0
} else {
0.0
};
report.push_str(&format!("Overall success rate: {success_rate:.1}%\n"));
report.push_str(&format!(
"Average recovery time: {:?}\n",
self.recovery_stats.avg_recovery_time
));
report.push_str("\n--- Strategy Success Rates ---\n");
for (strategy, rate) in &self.recovery_stats.strategy_success_rates {
let rate_pct = rate * 100.0;
report.push_str(&format!("{strategy}: {rate_pct:.1}%\n"));
}
report.push_str("\n--- Common Error Types ---\n");
for (error_type, count) in &self.recovery_stats.common_errors {
report.push_str(&format!("{error_type}: {count} occurrences\n"));
}
report
}
}
#[allow(dead_code)]
fn detect_simd_support() -> SimdSupport {
#[cfg(target_arch = "x86_64")]
{
if std::arch::is_x86_feature_detected!("avx512f") {
SimdSupport::AVX512
} else if std::arch::is_x86_feature_detected!("avx2") {
SimdSupport::AVX2
} else if std::arch::is_x86_feature_detected!("avx") {
SimdSupport::AVX
} else if std::arch::is_x86_feature_detected!("sse") {
SimdSupport::SSE
} else {
SimdSupport::None
}
}
#[cfg(target_arch = "aarch64")]
{
SimdSupport::NEON
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
SimdSupport::None
}
}
#[allow(dead_code)]
fn check_gpu_availability() -> bool {
false
}
static ERROR_RECOVERY: std::sync::Mutex<Option<ErrorRecoveryManager>> = std::sync::Mutex::new(None);
#[allow(dead_code)]
pub fn initialize_error_recovery(config: RecoveryConfig) {
let mut global_recovery = ERROR_RECOVERY.lock().expect("Operation failed");
*global_recovery = Some(ErrorRecoveryManager::new(config));
}
#[allow(dead_code)]
pub fn get_error_recovery() -> std::sync::MutexGuard<'static, Option<ErrorRecoveryManager>> {
ERROR_RECOVERY.lock().expect("Operation failed")
}
#[macro_export]
macro_rules! recover_or_fallback {
($operation:expr, $operation_name:expr, $params:expr, $fallback:expr) => {{
match $operation {
Ok(result) => Ok(result),
Err(error) => {
let mut recovery_manager = $crate::error::get_error_recovery();
if let Some(ref mut manager) = *recovery_manager {
match manager.recover_from_error(error, $operation_name, $params) {
Ok(strategy) => {
eprintln!("Recovered using strategy: {:?}", strategy);
$fallback
}
Err(unrecoverable_error) => Err(unrecoverable_error),
}
} else {
Err(error)
}
}
}
}};
}
pub trait GracefulDegradation {
type Output;
type Params;
fn try_full_quality(&self, params: &Self::Params) -> Result<Self::Output>;
fn fallback_reduced_quality(&self, params: &Self::Params) -> Result<Self::Output>;
fn fallback_minimal_quality(&self, params: &Self::Params) -> Result<Self::Output>;
fn execute_with_degradation(&self, params: &Self::Params) -> Result<Self::Output> {
if let Ok(result) = self.try_full_quality(params) {
return Ok(result);
}
if let Ok(result) = self.fallback_reduced_quality(params) {
eprintln!("Degraded to reduced quality");
return Ok(result);
}
eprintln!("Degraded to minimal quality");
self.fallback_minimal_quality(params)
}
}