1use scirs2_core::ndarray::ShapeError;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum VisionError {
9 #[error("Failed to load image: {0}")]
11 ImageLoadError(String),
12
13 #[error("Invalid parameter: {0}")]
15 InvalidParameter(String),
16
17 #[error("Operation failed: {0}")]
19 OperationError(String),
20
21 #[error("ndimage error: {0}")]
23 NdimageError(String),
24
25 #[error("I/O error: {0}")]
27 IoError(#[from] std::io::Error),
28
29 #[error("Type conversion error: {0}")]
31 TypeConversionError(String),
32
33 #[error("Shape error: {0}")]
35 ShapeError(#[from] ShapeError),
36
37 #[error("Linear algebra error: {0}")]
39 LinAlgError(String),
40
41 #[error("GPU error: {0}")]
43 GpuError(String),
44
45 #[error("Dimension mismatch: {0}")]
47 DimensionMismatch(String),
48
49 #[error("Invalid input: {0}")]
51 InvalidInput(String),
52
53 #[error("{0}")]
55 Other(String),
56}
57
58impl Clone for VisionError {
59 fn clone(&self) -> Self {
60 match self {
61 VisionError::ImageLoadError(s) => VisionError::ImageLoadError(s.clone()),
62 VisionError::InvalidParameter(s) => VisionError::InvalidParameter(s.clone()),
63 VisionError::OperationError(s) => VisionError::OperationError(s.clone()),
64 VisionError::NdimageError(s) => VisionError::NdimageError(s.clone()),
65 VisionError::IoError(e) => VisionError::Other(format!("I/O error: {e}")),
66 VisionError::TypeConversionError(s) => VisionError::TypeConversionError(s.clone()),
67 VisionError::ShapeError(e) => VisionError::Other(format!("Shape error: {e}")),
68 VisionError::LinAlgError(s) => VisionError::LinAlgError(s.clone()),
69 VisionError::GpuError(s) => VisionError::GpuError(s.clone()),
70 VisionError::DimensionMismatch(s) => VisionError::DimensionMismatch(s.clone()),
71 VisionError::InvalidInput(s) => VisionError::InvalidInput(s.clone()),
72 VisionError::Other(s) => VisionError::Other(s.clone()),
73 }
74 }
75}
76
77impl From<scirs2_core::gpu::GpuError> for VisionError {
79 fn from(err: scirs2_core::gpu::GpuError) -> Self {
80 VisionError::GpuError(err.to_string())
81 }
82}
83
84pub type Result<T> = std::result::Result<T, VisionError>;
86
87#[derive(Debug, Clone)]
99pub enum RecoveryStrategy {
100 RetryWithReducedParams,
102 FallbackToCpu,
104 FallbackToScalar,
106 UseDefaultParams,
108 SkipOperation,
110 ReduceQuality,
112 AdaptiveAdjustment,
114 NoRecovery,
116}
117
118#[derive(Debug, Clone)]
120pub struct ErrorContext {
121 pub operation: String,
123 pub parameters: std::collections::HashMap<String, String>,
125 pub system_state: SystemState,
127 pub recovery_strategies: Vec<RecoveryStrategy>,
129 pub severity: ErrorSeverity,
131 pub timestamp: std::time::Instant,
133}
134
135#[derive(Debug, Clone)]
137pub struct SystemState {
138 pub available_memory: usize,
140 pub cpu_usage: f32,
142 pub gpu_available: bool,
144 pub simd_support: SimdSupport,
146 pub thread_count: usize,
148}
149
150#[derive(Debug, Clone, Copy)]
152pub enum SimdSupport {
153 None,
155 SSE,
157 AVX,
159 AVX2,
161 AVX512,
163 NEON,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
169pub enum ErrorSeverity {
170 Low,
172 Medium,
174 High,
176 Critical,
178}
179
180#[derive(Debug, Clone)]
182pub struct RecoverableVisionError {
183 pub base_error: VisionError,
185 pub context: ErrorContext,
187 pub recovery_attempts: Vec<RecoveryAttempt>,
189 pub is_recoverable: bool,
191}
192
193#[derive(Debug, Clone)]
195pub struct RecoveryAttempt {
196 pub strategy: RecoveryStrategy,
198 pub success: bool,
200 pub duration: std::time::Duration,
202 pub details: String,
204}
205
206pub struct ErrorRecoveryManager {
208 config: RecoveryConfig,
210 error_history: std::collections::VecDeque<RecoverableVisionError>,
212 system_monitor: SystemStateMonitor,
214 recovery_stats: RecoveryStatistics,
216}
217
218#[derive(Debug, Clone)]
220pub struct RecoveryConfig {
221 pub max_recovery_attempts: usize,
223 pub enable_adaptive_params: bool,
225 pub enable_performance_recovery: bool,
227 pub memory_threshold: usize,
229 pub cpu_threshold: f32,
231 pub enable_logging: bool,
233 pub max_error_history: usize,
235}
236
237pub struct SystemStateMonitor {
239 last_state: SystemState,
241 update_interval: std::time::Duration,
243 last_update: std::time::Instant,
245}
246
247#[derive(Debug, Default)]
249pub struct RecoveryStatistics {
250 pub total_errors: usize,
252 pub successful_recoveries: usize,
254 pub failed_recoveries: usize,
256 pub strategy_success_rates: std::collections::HashMap<String, f32>,
258 pub avg_recovery_time: std::time::Duration,
260 pub common_errors: std::collections::HashMap<String, usize>,
262}
263
264impl Default for RecoveryConfig {
265 fn default() -> Self {
266 Self {
267 max_recovery_attempts: 3,
268 enable_adaptive_params: true,
269 enable_performance_recovery: true,
270 memory_threshold: 1_073_741_824, cpu_threshold: 80.0,
272 enable_logging: true,
273 max_error_history: 1000,
274 }
275 }
276}
277
278impl Default for SystemState {
279 fn default() -> Self {
280 Self {
281 available_memory: 2_147_483_648, cpu_usage: 0.0,
283 gpu_available: false,
284 simd_support: SimdSupport::None,
285 thread_count: 1,
286 }
287 }
288}
289
290impl SystemStateMonitor {
291 pub fn new() -> Self {
293 Self {
294 last_state: SystemState::default(),
295 update_interval: std::time::Duration::from_secs(1),
296 last_update: std::time::Instant::now(),
297 }
298 }
299
300 pub fn get_current_state(&mut self) -> &SystemState {
302 let now = std::time::Instant::now();
303 if now.duration_since(self.last_update) >= self.update_interval {
304 self.update_system_state();
305 self.last_update = now;
306 }
307 &self.last_state
308 }
309
310 fn update_system_state(&mut self) {
312 self.last_state.simd_support = detect_simd_support();
314
315 self.last_state.thread_count = num_cpus::get();
317
318 self.last_state.available_memory = 2_147_483_648; self.last_state.cpu_usage = 25.0; self.last_state.gpu_available = check_gpu_availability();
324 }
325}
326
327impl Default for SystemStateMonitor {
328 fn default() -> Self {
329 Self::new()
330 }
331}
332
333impl ErrorRecoveryManager {
334 pub fn new(config: RecoveryConfig) -> Self {
336 Self {
337 config,
338 error_history: std::collections::VecDeque::with_capacity(1000),
339 system_monitor: SystemStateMonitor::new(),
340 recovery_stats: RecoveryStatistics::default(),
341 }
342 }
343
344 pub fn recover_from_error(
346 &mut self,
347 error: VisionError,
348 operation: &str,
349 parameters: std::collections::HashMap<String, String>,
350 ) -> Result<RecoveryStrategy> {
351 let start_time = std::time::Instant::now();
352
353 let system_state = self.system_monitor.get_current_state().clone();
355
356 let recovery_strategies = self.analyze_error(&error, &system_state, operation);
358
359 let context = ErrorContext {
361 operation: operation.to_string(),
362 parameters,
363 system_state,
364 recovery_strategies: recovery_strategies.clone(),
365 severity: self.determine_error_severity(&error),
366 timestamp: start_time,
367 };
368
369 let mut recoverable_error = RecoverableVisionError {
371 base_error: error,
372 context,
373 recovery_attempts: Vec::new(),
374 is_recoverable: !recovery_strategies.is_empty(),
375 };
376
377 for strategy in recovery_strategies {
379 if self.attempt_recovery(&mut recoverable_error, strategy.clone()) {
380 self.record_successful_recovery(&recoverable_error, start_time.elapsed());
381 return Ok(strategy);
382 }
383 }
384
385 self.record_failed_recovery(&recoverable_error);
387 Err(recoverable_error.base_error)
388 }
389
390 fn analyze_error(
392 &self,
393 error: &VisionError,
394 system_state: &SystemState,
395 operation: &str,
396 ) -> Vec<RecoveryStrategy> {
397 let mut strategies = Vec::new();
398
399 match error {
400 VisionError::OperationError(_) => {
401 if system_state.available_memory < self.config.memory_threshold {
403 strategies.push(RecoveryStrategy::ReduceQuality);
404 strategies.push(RecoveryStrategy::RetryWithReducedParams);
405 }
406
407 if system_state.cpu_usage > self.config.cpu_threshold {
408 strategies.push(RecoveryStrategy::FallbackToScalar);
409 }
410
411 if operation.contains("gpu") || operation.contains("GPU") {
413 strategies.push(RecoveryStrategy::FallbackToCpu);
414 }
415
416 if operation.contains("simd") || operation.contains("SIMD") {
418 strategies.push(RecoveryStrategy::FallbackToScalar);
419 }
420
421 strategies.push(RecoveryStrategy::UseDefaultParams);
422 strategies.push(RecoveryStrategy::AdaptiveAdjustment);
423 }
424
425 VisionError::InvalidParameter(_) => {
426 strategies.push(RecoveryStrategy::UseDefaultParams);
427 strategies.push(RecoveryStrategy::AdaptiveAdjustment);
428 strategies.push(RecoveryStrategy::RetryWithReducedParams);
429 }
430
431 VisionError::DimensionMismatch(_) | VisionError::ShapeError(_) => {
432 strategies.push(RecoveryStrategy::AdaptiveAdjustment);
433 strategies.push(RecoveryStrategy::UseDefaultParams);
434 }
435
436 VisionError::LinAlgError(_) => {
437 strategies.push(RecoveryStrategy::FallbackToScalar);
438 strategies.push(RecoveryStrategy::UseDefaultParams);
439 strategies.push(RecoveryStrategy::RetryWithReducedParams);
440 }
441
442 _ => {
443 strategies.push(RecoveryStrategy::UseDefaultParams);
445 strategies.push(RecoveryStrategy::SkipOperation);
446 }
447 }
448
449 strategies.sort_by_key(|s| format!("{s:?}"));
451 strategies.dedup_by_key(|s| format!("{s:?}"));
452
453 strategies
454 }
455
456 fn determine_error_severity(&self, error: &VisionError) -> ErrorSeverity {
458 match error {
459 VisionError::InvalidParameter(_) | VisionError::InvalidInput(_) => ErrorSeverity::Low,
460 VisionError::OperationError(_) | VisionError::TypeConversionError(_) => {
461 ErrorSeverity::Medium
462 }
463 VisionError::LinAlgError(_) | VisionError::DimensionMismatch(_) => ErrorSeverity::High,
464 VisionError::IoError(_) | VisionError::Other(_) => ErrorSeverity::Critical,
465 VisionError::ImageLoadError(_) => ErrorSeverity::High,
466 VisionError::NdimageError(_) => ErrorSeverity::Medium,
467 VisionError::ShapeError(_) => ErrorSeverity::Medium,
468 VisionError::GpuError(_) => ErrorSeverity::High,
469 }
470 }
471
472 fn attempt_recovery(
474 &mut self,
475 error: &mut RecoverableVisionError,
476 strategy: RecoveryStrategy,
477 ) -> bool {
478 let start_time = std::time::Instant::now();
479
480 let success = match strategy {
482 RecoveryStrategy::FallbackToCpu | RecoveryStrategy::FallbackToScalar => true,
483 RecoveryStrategy::UseDefaultParams => true,
484 RecoveryStrategy::RetryWithReducedParams => true,
485 RecoveryStrategy::ReduceQuality => true,
486 RecoveryStrategy::AdaptiveAdjustment => true,
487 RecoveryStrategy::SkipOperation => true,
488 RecoveryStrategy::NoRecovery => false,
489 };
490
491 let attempt = RecoveryAttempt {
492 strategy: strategy.clone(),
493 success,
494 duration: start_time.elapsed(),
495 details: format!("Attempted {strategy:?} recovery"),
496 };
497
498 error.recovery_attempts.push(attempt);
499 success
500 }
501
502 fn record_successful_recovery(
504 &mut self,
505 error: &RecoverableVisionError,
506 total_duration: std::time::Duration,
507 ) {
508 self.recovery_stats.total_errors += 1;
509 self.recovery_stats.successful_recoveries += 1;
510
511 for attempt in &error.recovery_attempts {
513 if attempt.success {
514 let strategy_name = format!("{:?}", attempt.strategy);
515 let current_rate = self
516 .recovery_stats
517 .strategy_success_rates
518 .get(&strategy_name)
519 .copied()
520 .unwrap_or(0.0);
521
522 let new_rate = (current_rate + 1.0) / 2.0;
524 self.recovery_stats
525 .strategy_success_rates
526 .insert(strategy_name, new_rate);
527 break;
528 }
529 }
530
531 let current_avg = self.recovery_stats.avg_recovery_time;
533 let avg_nanos = ((current_avg.as_nanos() + total_duration.as_nanos()) / 2)
534 .try_into()
535 .unwrap_or(u64::MAX);
536 let new_avg = std::time::Duration::from_nanos(avg_nanos);
537 self.recovery_stats.avg_recovery_time = new_avg;
538
539 self.add_to_error_history(error.clone());
541
542 if self.config.enable_logging {
543 eprintln!("Successfully recovered from error: {}", error.base_error);
544 }
545 }
546
547 fn record_failed_recovery(&mut self, error: &RecoverableVisionError) {
549 self.recovery_stats.total_errors += 1;
550 self.recovery_stats.failed_recoveries += 1;
551
552 let error_type = format!("{:?}", error.base_error);
554 let count = self
555 .recovery_stats
556 .common_errors
557 .get(&error_type)
558 .copied()
559 .unwrap_or(0);
560 self.recovery_stats
561 .common_errors
562 .insert(error_type, count + 1);
563
564 self.add_to_error_history(error.clone());
566
567 if self.config.enable_logging {
568 eprintln!("Failed to recover from error: {}", error.base_error);
569 }
570 }
571
572 fn add_to_error_history(&mut self, error: RecoverableVisionError) {
574 self.error_history.push_back(error);
575
576 if self.error_history.len() > self.config.max_error_history {
578 self.error_history.pop_front();
579 }
580 }
581
582 pub fn get_statistics(&self) -> &RecoveryStatistics {
584 &self.recovery_stats
585 }
586
587 pub fn generate_recovery_report(&self) -> String {
589 let mut report = String::new();
590
591 report.push_str("=== Error Recovery Report ===\n");
592 report.push_str(&format!(
593 "Total errors: {}\n",
594 self.recovery_stats.total_errors
595 ));
596 report.push_str(&format!(
597 "Successful recoveries: {}\n",
598 self.recovery_stats.successful_recoveries
599 ));
600 report.push_str(&format!(
601 "Failed recoveries: {}\n",
602 self.recovery_stats.failed_recoveries
603 ));
604
605 let success_rate = if self.recovery_stats.total_errors > 0 {
606 self.recovery_stats.successful_recoveries as f32
607 / self.recovery_stats.total_errors as f32
608 * 100.0
609 } else {
610 0.0
611 };
612 report.push_str(&format!("Overall success rate: {success_rate:.1}%\n"));
613
614 report.push_str(&format!(
615 "Average recovery time: {:?}\n",
616 self.recovery_stats.avg_recovery_time
617 ));
618
619 report.push_str("\n--- Strategy Success Rates ---\n");
620 for (strategy, rate) in &self.recovery_stats.strategy_success_rates {
621 let rate_pct = rate * 100.0;
622 report.push_str(&format!("{strategy}: {rate_pct:.1}%\n"));
623 }
624
625 report.push_str("\n--- Common Error Types ---\n");
626 for (error_type, count) in &self.recovery_stats.common_errors {
627 report.push_str(&format!("{error_type}: {count} occurrences\n"));
628 }
629
630 report
631 }
632}
633
634#[allow(dead_code)]
636fn detect_simd_support() -> SimdSupport {
637 #[cfg(target_arch = "x86_64")]
640 {
641 if std::arch::is_x86_feature_detected!("avx512f") {
642 SimdSupport::AVX512
643 } else if std::arch::is_x86_feature_detected!("avx2") {
644 SimdSupport::AVX2
645 } else if std::arch::is_x86_feature_detected!("avx") {
646 SimdSupport::AVX
647 } else if std::arch::is_x86_feature_detected!("sse") {
648 SimdSupport::SSE
649 } else {
650 SimdSupport::None
651 }
652 }
653 #[cfg(target_arch = "aarch64")]
654 {
655 SimdSupport::NEON
656 }
657 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
658 {
659 SimdSupport::None
660 }
661}
662
663#[allow(dead_code)]
665fn check_gpu_availability() -> bool {
666 false
669}
670
671static ERROR_RECOVERY: std::sync::Mutex<Option<ErrorRecoveryManager>> = std::sync::Mutex::new(None);
673
674#[allow(dead_code)]
676pub fn initialize_error_recovery(config: RecoveryConfig) {
677 let mut global_recovery = ERROR_RECOVERY.lock().expect("Operation failed");
678 *global_recovery = Some(ErrorRecoveryManager::new(config));
679}
680
681#[allow(dead_code)]
683pub fn get_error_recovery() -> std::sync::MutexGuard<'static, Option<ErrorRecoveryManager>> {
684 ERROR_RECOVERY.lock().expect("Operation failed")
685}
686
687#[macro_export]
689macro_rules! recover_or_fallback {
690 ($operation:expr, $operation_name:expr, $params:expr, $fallback:expr) => {{
691 match $operation {
692 Ok(result) => Ok(result),
693 Err(error) => {
694 let mut recovery_manager = $crate::error::get_error_recovery();
695 if let Some(ref mut manager) = *recovery_manager {
696 match manager.recover_from_error(error, $operation_name, $params) {
697 Ok(strategy) => {
698 eprintln!("Recovered using strategy: {:?}", strategy);
699 $fallback
700 }
701 Err(unrecoverable_error) => Err(unrecoverable_error),
702 }
703 } else {
704 Err(error)
705 }
706 }
707 }
708 }};
709}
710
711pub trait GracefulDegradation {
713 type Output;
715 type Params;
717
718 fn try_full_quality(&self, params: &Self::Params) -> Result<Self::Output>;
720
721 fn fallback_reduced_quality(&self, params: &Self::Params) -> Result<Self::Output>;
723
724 fn fallback_minimal_quality(&self, params: &Self::Params) -> Result<Self::Output>;
726
727 fn execute_with_degradation(&self, params: &Self::Params) -> Result<Self::Output> {
729 if let Ok(result) = self.try_full_quality(params) {
731 return Ok(result);
732 }
733
734 if let Ok(result) = self.fallback_reduced_quality(params) {
736 eprintln!("Degraded to reduced quality");
737 return Ok(result);
738 }
739
740 eprintln!("Degraded to minimal quality");
742 self.fallback_minimal_quality(params)
743 }
744}