1use crate::{
7 core::EmotionProcessor,
8 history::{EmotionHistory, EmotionHistoryEntry},
9 types::{Emotion, EmotionParameters, EmotionVector},
10 Error, Result,
11};
12
13use serde::{Deserialize, Serialize};
14use std::{
15 collections::HashMap,
16 time::{Duration, SystemTime},
17};
18use tracing::{debug, info, warn};
19
20#[derive(Debug)]
22pub struct EmotionDebugger {
23 config: DebugConfig,
25 captured_states: Vec<EmotionStateSnapshot>,
27 performance_metrics: DebugPerformanceMetrics,
29 recent_operations: Vec<(std::time::Instant, Duration)>,
31 cpu_window_size: usize,
33}
34
35#[derive(Debug, Clone)]
37pub struct DebugConfig {
38 pub capture_detailed_states: bool,
40 pub track_performance: bool,
42 pub capture_interval: Duration,
44 pub max_snapshots: usize,
46 pub analyze_audio: bool,
48 pub output_format: DebugOutputFormat,
50}
51
52#[derive(Debug, Clone)]
54pub enum DebugOutputFormat {
55 Text,
57 Json,
59 Csv,
61 Html,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct EmotionStateSnapshot {
68 pub timestamp: SystemTime,
70 pub emotion_parameters: EmotionParameters,
72 pub dominant_emotion: (String, f32),
74 pub emotion_vector_values: HashMap<String, f32>,
76 pub performance: Option<SnapshotPerformanceMetrics>,
78 pub audio_characteristics: Option<AudioCharacteristics>,
80 pub metadata: HashMap<String, String>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct SnapshotPerformanceMetrics {
87 pub processing_time_us: u64,
89 pub memory_usage_bytes: usize,
91 pub cpu_usage_percent: f32,
93 pub active_interpolations: usize,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct AudioCharacteristics {
100 pub rms_energy: f32,
102 pub spectral_centroid: f32,
104 pub zero_crossing_rate: f32,
106 pub estimated_pitch: Option<f32>,
108}
109
110#[derive(Debug, Clone)]
112pub struct DebugPerformanceMetrics {
113 pub total_processing_time: Duration,
115 pub average_processing_time: Duration,
117 pub peak_memory_usage: usize,
119 pub operation_count: usize,
121 pub error_count: usize,
123}
124
125impl EmotionDebugger {
126 pub fn new(config: DebugConfig) -> Self {
128 Self {
129 config,
130 captured_states: Vec::new(),
131 performance_metrics: DebugPerformanceMetrics::default(),
132 recent_operations: Vec::with_capacity(100),
133 cpu_window_size: 100, }
135 }
136
137 #[allow(clippy::should_implement_trait)]
139 pub fn default() -> Self {
140 Self::new(DebugConfig::default())
141 }
142
143 pub async fn capture_state(
145 &mut self,
146 processor: &EmotionProcessor,
147 context: Option<&str>,
148 ) -> Result<()> {
149 let start_time = std::time::Instant::now();
150
151 debug!("Capturing emotion state for debugging");
152
153 let emotion_params = EmotionParameters::neutral(); let dominant_emotion = emotion_params
157 .emotion_vector
158 .dominant_emotion()
159 .map(|(e, i)| (e.as_str().to_string(), i.value()))
160 .unwrap_or(("neutral".to_string(), 0.0));
161
162 let emotion_vector_values =
164 self.extract_emotion_vector_values(&emotion_params.emotion_vector);
165
166 let performance = if self.config.track_performance {
168 Some(SnapshotPerformanceMetrics {
169 processing_time_us: start_time.elapsed().as_micros() as u64,
170 memory_usage_bytes: self.estimate_memory_usage(),
171 cpu_usage_percent: self.estimate_cpu_usage(),
172 active_interpolations: 0, })
174 } else {
175 None
176 };
177
178 let mut metadata = HashMap::new();
180 metadata.insert("context".to_string(), context.unwrap_or("none").to_string());
181 metadata.insert("capture_method".to_string(), "manual".to_string());
182
183 let snapshot = EmotionStateSnapshot {
185 timestamp: SystemTime::now(),
186 emotion_parameters: emotion_params,
187 dominant_emotion,
188 emotion_vector_values,
189 performance,
190 audio_characteristics: None, metadata,
192 };
193
194 self.captured_states.push(snapshot);
196
197 if self.captured_states.len() > self.config.max_snapshots {
199 self.captured_states.remove(0);
200 }
201
202 let processing_time = start_time.elapsed();
204 self.performance_metrics.operation_count += 1;
205 self.performance_metrics.total_processing_time += processing_time;
206
207 self.record_operation(processing_time);
209
210 debug!("Emotion state captured successfully");
211 Ok(())
212 }
213
214 pub async fn start_continuous_monitoring(
216 &mut self,
217 processor: &EmotionProcessor,
218 ) -> Result<()> {
219 info!("Starting continuous emotion state monitoring");
220
221 self.capture_state(processor, Some("continuous_monitoring"))
224 .await?;
225
226 Ok(())
227 }
228
229 pub fn analyze_transitions(&self) -> EmotionTransitionAnalysis {
231 debug!("Analyzing emotion transitions");
232
233 let mut transitions = Vec::new();
234 let mut emotion_durations = HashMap::new();
235 let mut transition_frequencies = HashMap::new();
236
237 for window in self.captured_states.windows(2) {
238 let prev_state = &window[0];
239 let curr_state = &window[1];
240
241 let prev_emotion = &prev_state.dominant_emotion.0;
242 let curr_emotion = &curr_state.dominant_emotion.0;
243
244 if prev_emotion != curr_emotion {
245 let transition = EmotionTransition {
246 from_emotion: prev_emotion.clone(),
247 to_emotion: curr_emotion.clone(),
248 from_intensity: prev_state.dominant_emotion.1,
249 to_intensity: curr_state.dominant_emotion.1,
250 duration: curr_state
251 .timestamp
252 .duration_since(prev_state.timestamp)
253 .unwrap_or(Duration::from_millis(0)),
254 timestamp: curr_state.timestamp,
255 };
256
257 transitions.push(transition);
258
259 let transition_key = format!("{}->{}", prev_emotion, curr_emotion);
261 *transition_frequencies.entry(transition_key).or_insert(0) += 1;
262 }
263
264 let duration = curr_state
266 .timestamp
267 .duration_since(prev_state.timestamp)
268 .unwrap_or(Duration::from_millis(0));
269 emotion_durations
270 .entry(prev_emotion.clone())
271 .and_modify(|d: &mut Duration| *d += duration)
272 .or_insert(duration);
273 }
274
275 EmotionTransitionAnalysis {
276 transitions,
277 emotion_durations,
278 transition_frequencies,
279 total_transitions: self.captured_states.len().saturating_sub(1),
280 analysis_timestamp: SystemTime::now(),
281 }
282 }
283
284 pub fn generate_debug_report(&self) -> Result<String> {
286 match self.config.output_format {
287 DebugOutputFormat::Text => self.generate_text_report(),
288 DebugOutputFormat::Json => self.generate_json_report(),
289 DebugOutputFormat::Csv => self.generate_csv_report(),
290 DebugOutputFormat::Html => self.generate_html_report(),
291 }
292 }
293
294 pub fn clear_captured_states(&mut self) {
296 self.captured_states.clear();
297 debug!("Captured states cleared");
298 }
299
300 pub fn captured_states_count(&self) -> usize {
302 self.captured_states.len()
303 }
304
305 pub fn get_performance_metrics(&self) -> &DebugPerformanceMetrics {
307 &self.performance_metrics
308 }
309
310 fn extract_emotion_vector_values(&self, vector: &EmotionVector) -> HashMap<String, f32> {
313 let mut values = HashMap::new();
314
315 if let Some((emotion, intensity)) = vector.dominant_emotion() {
319 values.insert(emotion.as_str().to_string(), intensity.value());
320 }
321
322 values
323 }
324
325 fn estimate_memory_usage(&self) -> usize {
326 std::mem::size_of::<EmotionDebugger>()
328 + self.captured_states.len() * std::mem::size_of::<EmotionStateSnapshot>()
329 }
330
331 fn estimate_cpu_usage(&self) -> f32 {
332 if self.recent_operations.is_empty() {
336 return 0.0;
337 }
338
339 let now = std::time::Instant::now();
341 let window_start = self
342 .recent_operations
343 .first()
344 .expect("recent_operations should not be empty at this point")
345 .0;
346 let wall_clock_time = now.duration_since(window_start);
347
348 let total_processing_time: Duration = self
350 .recent_operations
351 .iter()
352 .map(|(_, duration)| *duration)
353 .sum();
354
355 if wall_clock_time.as_secs_f32() > 0.0 {
357 (total_processing_time.as_secs_f32() / wall_clock_time.as_secs_f32() * 100.0).min(100.0)
358 } else {
359 0.0
360 }
361 }
362
363 fn record_operation(&mut self, processing_time: Duration) {
365 let now = std::time::Instant::now();
366 self.recent_operations.push((now, processing_time));
367
368 if self.recent_operations.len() > self.cpu_window_size {
370 self.recent_operations.remove(0);
371 }
372 }
373
374 fn generate_text_report(&self) -> Result<String> {
375 let mut report = String::new();
376
377 report.push_str("=== EMOTION DEBUG REPORT ===\n\n");
378
379 report.push_str(&format!(
381 "Captured States: {}\n",
382 self.captured_states.len()
383 ));
384 report.push_str(&format!(
385 "Total Operations: {}\n",
386 self.performance_metrics.operation_count
387 ));
388 report.push_str(&format!(
389 "Average Processing Time: {:?}\n",
390 self.performance_metrics.average_processing_time
391 ));
392 report.push('\n');
393
394 report.push_str("Recent Emotion States:\n");
396 for (i, state) in self.captured_states.iter().rev().take(10).enumerate() {
397 report.push_str(&format!(
398 " {}: {} ({:.2}) at {:?}\n",
399 i + 1,
400 state.dominant_emotion.0,
401 state.dominant_emotion.1,
402 state.timestamp
403 ));
404 }
405
406 let transition_analysis = self.analyze_transitions();
408 report.push_str("\nTransition Analysis:\n");
409 report.push_str(&format!(
410 " Total Transitions: {}\n",
411 transition_analysis.total_transitions
412 ));
413
414 for (transition_key, frequency) in transition_analysis.transition_frequencies.iter().take(5)
415 {
416 report.push_str(&format!(" {}: {} times\n", transition_key, frequency));
417 }
418
419 Ok(report)
420 }
421
422 fn generate_json_report(&self) -> Result<String> {
423 let report_data = serde_json::json!({
424 "summary": {
425 "captured_states": self.captured_states.len(),
426 "operation_count": self.performance_metrics.operation_count,
427 "total_processing_time_ms": self.performance_metrics.total_processing_time.as_millis(),
428 },
429 "captured_states": self.captured_states,
430 "transition_analysis": self.analyze_transitions(),
431 "performance_metrics": {
432 "peak_memory_usage": self.performance_metrics.peak_memory_usage,
433 "error_count": self.performance_metrics.error_count,
434 }
435 });
436
437 serde_json::to_string_pretty(&report_data).map_err(Error::Serialization)
438 }
439
440 fn generate_csv_report(&self) -> Result<String> {
441 let mut csv = String::new();
442
443 csv.push_str("timestamp,emotion,intensity,pitch_shift,tempo_scale,energy_scale\n");
445
446 for state in &self.captured_states {
448 csv.push_str(&format!(
449 "{:?},{},{},{},{},{}\n",
450 state.timestamp,
451 state.dominant_emotion.0,
452 state.dominant_emotion.1,
453 state.emotion_parameters.pitch_shift,
454 state.emotion_parameters.tempo_scale,
455 state.emotion_parameters.energy_scale
456 ));
457 }
458
459 Ok(csv)
460 }
461
462 fn generate_html_report(&self) -> Result<String> {
463 let mut html = String::new();
464
465 html.push_str(
466 "<!DOCTYPE html><html><head><title>Emotion Debug Report</title></head><body>",
467 );
468 html.push_str("<h1>Emotion Debug Report</h1>");
469
470 html.push_str("<h2>Summary</h2>");
472 html.push_str(&format!(
473 "<p>Captured States: {}</p>",
474 self.captured_states.len()
475 ));
476 html.push_str(&format!(
477 "<p>Total Operations: {}</p>",
478 self.performance_metrics.operation_count
479 ));
480
481 html.push_str("<h2>Recent Emotion States</h2>");
483 html.push_str(
484 "<table border='1'><tr><th>Timestamp</th><th>Emotion</th><th>Intensity</th></tr>",
485 );
486
487 for state in self.captured_states.iter().rev().take(20) {
488 html.push_str(&format!(
489 "<tr><td>{:?}</td><td>{}</td><td>{:.2}</td></tr>",
490 state.timestamp, state.dominant_emotion.0, state.dominant_emotion.1
491 ));
492 }
493
494 html.push_str("</table>");
495 html.push_str("</body></html>");
496
497 Ok(html)
498 }
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct EmotionTransitionAnalysis {
504 pub transitions: Vec<EmotionTransition>,
506 pub emotion_durations: HashMap<String, Duration>,
508 pub transition_frequencies: HashMap<String, usize>,
510 pub total_transitions: usize,
512 pub analysis_timestamp: SystemTime,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct EmotionTransition {
519 pub from_emotion: String,
521 pub to_emotion: String,
523 pub from_intensity: f32,
525 pub to_intensity: f32,
527 #[serde(with = "duration_serde")]
529 pub duration: Duration,
530 pub timestamp: SystemTime,
532}
533
534mod duration_serde {
536 use serde::{Deserialize, Deserializer, Serializer};
537 use std::time::Duration;
538
539 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
540 where
541 S: Serializer,
542 {
543 serializer.serialize_u64(duration.as_millis() as u64)
544 }
545
546 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
547 where
548 D: Deserializer<'de>,
549 {
550 let millis = u64::deserialize(deserializer)?;
551 Ok(Duration::from_millis(millis))
552 }
553}
554
555impl Default for DebugConfig {
556 fn default() -> Self {
557 Self {
558 capture_detailed_states: true,
559 track_performance: true,
560 capture_interval: Duration::from_secs(1),
561 max_snapshots: 1000,
562 analyze_audio: false,
563 output_format: DebugOutputFormat::Text,
564 }
565 }
566}
567
568impl Default for DebugPerformanceMetrics {
569 fn default() -> Self {
570 Self {
571 total_processing_time: Duration::from_secs(0),
572 average_processing_time: Duration::from_secs(0),
573 peak_memory_usage: 0,
574 operation_count: 0,
575 error_count: 0,
576 }
577 }
578}
579
580#[cfg(test)]
581mod tests {
582 use super::*;
583
584 #[tokio::test]
585 async fn test_debugger_creation() {
586 let debugger = EmotionDebugger::default();
587 assert_eq!(debugger.captured_states_count(), 0);
588 assert_eq!(debugger.get_performance_metrics().operation_count, 0);
589 }
590
591 #[tokio::test]
592 async fn test_state_capture() {
593 let mut debugger = EmotionDebugger::default();
594 let processor = crate::core::EmotionProcessor::new().unwrap();
595
596 debugger
598 .capture_state(&processor, Some("test"))
599 .await
600 .unwrap();
601 assert_eq!(debugger.captured_states_count(), 1);
602
603 debugger
605 .capture_state(&processor, Some("test2"))
606 .await
607 .unwrap();
608 assert_eq!(debugger.captured_states_count(), 2);
609 }
610
611 #[tokio::test]
612 async fn test_debug_config_default() {
613 let config = DebugConfig::default();
614 assert!(config.capture_detailed_states);
615 assert!(config.track_performance);
616 assert_eq!(config.max_snapshots, 1000);
617 }
618
619 #[tokio::test]
620 async fn test_transition_analysis() {
621 let mut debugger = EmotionDebugger::default();
622 let processor = crate::core::EmotionProcessor::new().unwrap();
623
624 for _ in 0..5 {
626 debugger
627 .capture_state(&processor, Some("test"))
628 .await
629 .unwrap();
630 }
631
632 let analysis = debugger.analyze_transitions();
633 assert!(analysis.total_transitions <= 4); }
635
636 #[test]
637 fn test_debug_output_formats() {
638 let debugger = EmotionDebugger::default();
639
640 let text_report = debugger.generate_text_report().unwrap();
642 assert!(text_report.contains("EMOTION DEBUG REPORT"));
643
644 let json_report = debugger.generate_json_report().unwrap();
646 assert!(json_report.contains("captured_states"));
647
648 let csv_report = debugger.generate_csv_report().unwrap();
650 assert!(csv_report.contains("timestamp,emotion,intensity"));
651
652 let html_report = debugger.generate_html_report().unwrap();
654 assert!(html_report.contains("<html>"));
655 assert!(html_report.contains("Emotion Debug Report"));
656 }
657
658 #[test]
659 fn test_clear_captured_states() {
660 let mut debugger = EmotionDebugger::default();
661
662 let snapshot = EmotionStateSnapshot {
664 timestamp: SystemTime::now(),
665 emotion_parameters: crate::types::EmotionParameters::neutral(),
666 dominant_emotion: ("neutral".to_string(), 0.5),
667 emotion_vector_values: HashMap::new(),
668 performance: None,
669 audio_characteristics: None,
670 metadata: HashMap::new(),
671 };
672
673 debugger.captured_states.push(snapshot);
674 assert_eq!(debugger.captured_states_count(), 1);
675
676 debugger.clear_captured_states();
677 assert_eq!(debugger.captured_states_count(), 0);
678 }
679
680 #[test]
681 fn test_memory_and_cpu_estimation() {
682 let debugger = EmotionDebugger::default();
683
684 let memory_usage = debugger.estimate_memory_usage();
685 assert!(memory_usage > 0);
686
687 let cpu_usage = debugger.estimate_cpu_usage();
688 assert!(cpu_usage >= 0.0 && cpu_usage <= 100.0);
689 }
690}