2025-11-03 00:11:13 +08:00

722 lines
24 KiB
Rust

use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use anyhow::{Result, anyhow};
use tokio::sync::{mpsc, RwLock, Mutex};
use tokio::time::{sleep, interval, timeout};
use crate::monitoring::integrated_system::{IntegratedMemorySystem, SystemConfig, ProcessedFrame};
use crate::memory::ring_buffer::AstronomicalFrame;
use crate::memory::frame_pool::{PooledFrameBuffer, HierarchicalFramePool};
use crate::memory::memory_monitor::SystemMemoryInfo;
/// Camera integration with memory management system
/// Optimized for Raspberry Pi camera modules and astronomical imaging
pub struct CameraMemoryIntegration {
/// Integrated memory system
memory_system: Arc<IntegratedMemorySystem>,
/// Camera controller
camera: Arc<Mutex<CameraController>>,
/// Frame capture pipeline
capture_pipeline: Arc<FrameCapturePipeline>,
/// Configuration
config: CameraConfig,
/// Statistics
stats: Arc<RwLock<CameraStats>>,
}
/// Configuration for camera integration
#[derive(Debug, Clone)]
pub struct CameraConfig {
/// Frame width in pixels
pub frame_width: u32,
/// Frame height in pixels
pub frame_height: u32,
/// Frames per second
pub fps: f64,
/// Pixel format (bytes per pixel)
pub bytes_per_pixel: usize,
/// Camera exposure time (microseconds)
pub exposure_us: u64,
/// Camera gain setting
pub gain: f32,
/// Enable night mode for meteor detection
pub night_mode: bool,
/// Buffer count for smooth capture
pub capture_buffer_count: usize,
/// Enable memory optimization
pub enable_memory_optimization: bool,
/// Maximum memory usage for camera buffers (bytes)
pub max_camera_memory: usize,
}
impl Default for CameraConfig {
fn default() -> Self {
Self {
frame_width: 1280,
frame_height: 720,
fps: 30.0,
bytes_per_pixel: 3, // RGB
exposure_us: 33333, // 1/30 second
gain: 2.0,
night_mode: true,
capture_buffer_count: 8,
enable_memory_optimization: true,
max_camera_memory: 64 * 1024 * 1024, // 64MB
}
}
}
/// Camera controller abstraction
pub struct CameraController {
/// Camera ID/device path
device_id: String,
/// Current configuration
config: CameraConfig,
/// Camera state
state: CameraState,
/// Frame counter
frame_counter: u64,
/// Capture start time
capture_start: Option<Instant>,
}
/// Camera operational state
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CameraState {
Uninitialized,
Initializing,
Ready,
Capturing,
Error,
}
/// Frame capture pipeline with memory optimization
pub struct FrameCapturePipeline {
/// Frame output channel
frame_sender: mpsc::Sender<CapturedFrame>,
/// Processing channel
processing_receiver: Option<mpsc::Receiver<CapturedFrame>>,
/// Buffer pool for captured frames
capture_buffers: Arc<CaptureBufferPool>,
/// Pipeline statistics
pipeline_stats: Arc<RwLock<PipelineStats>>,
}
/// Captured frame with memory management metadata
pub struct CapturedFrame {
/// Frame data buffer
pub buffer: Arc<PooledFrameBuffer>,
/// Frame metadata
pub metadata: FrameMetadata,
/// Capture timestamp
pub capture_time: Instant,
}
/// Frame metadata for astronomical processing
#[derive(Debug, Clone)]
pub struct FrameMetadata {
pub frame_id: u64,
pub timestamp_nanos: u64,
pub width: u32,
pub height: u32,
pub bytes_per_pixel: usize,
pub exposure_us: u64,
pub gain: f32,
pub estimated_brightness: f32,
pub memory_pool_id: usize,
}
/// Buffer pool specifically for camera capture
pub struct CaptureBufferPool {
/// Available buffers
buffers: Arc<RwLock<Vec<Arc<PooledFrameBuffer>>>>,
/// Buffer size in bytes
buffer_size: usize,
/// Maximum buffer count
max_buffers: usize,
/// Current buffer count
current_count: Arc<RwLock<usize>>,
/// Associated frame pool
frame_pool: Arc<HierarchicalFramePool>,
}
/// Camera and pipeline statistics
#[derive(Debug, Default, Clone)]
pub struct CameraStats {
pub frames_captured: u64,
pub frames_dropped: u64,
pub capture_fps: f64,
pub memory_efficiency: f64,
pub buffer_utilization: f64,
pub avg_capture_latency_us: u64,
pub total_memory_usage: usize,
pub error_count: u64,
}
/// Pipeline processing statistics
#[derive(Debug, Default)]
struct PipelineStats {
frames_in: u64,
frames_out: u64,
processing_latency_sum: u64,
buffer_reuse_count: u64,
memory_pressure_events: u64,
}
impl CameraMemoryIntegration {
/// Create new camera memory integration system
pub async fn new(
memory_system: Arc<IntegratedMemorySystem>,
camera_config: CameraConfig,
) -> Result<Self> {
println!("📷 Initializing Camera Memory Integration");
println!("========================================");
// Create camera controller
let camera = Arc::new(Mutex::new(CameraController::new(
"pi_camera".to_string(),
camera_config.clone(),
)?));
// Create capture buffer pool
let buffer_size = camera_config.frame_width as usize
* camera_config.frame_height as usize
* camera_config.bytes_per_pixel;
let capture_buffers = Arc::new(CaptureBufferPool::new(
buffer_size,
camera_config.capture_buffer_count,
memory_system.get_frame_pool(),
).await?);
// Create capture pipeline
let (frame_sender, processing_receiver) = mpsc::channel(camera_config.capture_buffer_count * 2);
let capture_pipeline = Arc::new(FrameCapturePipeline {
frame_sender,
processing_receiver: Some(processing_receiver),
capture_buffers,
pipeline_stats: Arc::new(RwLock::new(PipelineStats::default())),
});
println!(" ✓ Camera controller initialized");
println!(" ✓ Capture buffer pool created ({} buffers, {} KB each)",
camera_config.capture_buffer_count, buffer_size / 1024);
println!(" ✓ Frame capture pipeline ready");
Ok(Self {
memory_system,
camera,
capture_pipeline,
config: camera_config,
stats: Arc::new(RwLock::new(CameraStats::default())),
})
}
/// Start camera capture with memory management
pub async fn start_capture(&self) -> Result<()> {
println!("🎬 Starting camera capture with memory optimization");
// Initialize camera
{
let mut camera = self.camera.lock().await;
camera.initialize().await?;
}
// Start capture loop
let capture_handle = self.start_capture_loop();
// Start processing loop
let processing_handle = self.start_processing_loop().await?;
// Start statistics collection
let stats_handle = self.start_stats_collection();
// Start memory optimization
let optimization_handle = self.start_memory_optimization();
println!("✅ Camera capture started successfully");
println!(" 📊 Resolution: {}x{} @ {:.1} FPS",
self.config.frame_width, self.config.frame_height, self.config.fps);
println!(" 💾 Buffer pool: {} buffers ({} MB total)",
self.config.capture_buffer_count,
(self.config.capture_buffer_count * self.calculate_frame_size()) / (1024 * 1024));
// Wait for all components
tokio::select! {
result = capture_handle => {
match result {
Ok(Ok(())) => println!("✅ Capture loop completed"),
Ok(Err(e)) => eprintln!("❌ Capture error: {}", e),
Err(e) => eprintln!("❌ Capture task error: {}", e),
}
}
result = processing_handle => {
match result {
Ok(Ok(())) => println!("✅ Processing loop completed"),
Ok(Err(e)) => eprintln!("❌ Processing error: {}", e),
Err(e) => eprintln!("❌ Processing task error: {}", e),
}
}
_ = stats_handle => println!("✅ Stats collection completed"),
_ = optimization_handle => println!("✅ Memory optimization completed"),
}
Ok(())
}
/// Get current camera and memory statistics
pub async fn get_stats(&self) -> CameraStats {
self.stats.read().await.clone()
}
/// Get comprehensive system health
pub async fn get_system_health(&self) -> CameraSystemHealth {
let camera_stats = self.get_stats().await;
let memory_metrics = self.memory_system.get_metrics().await;
let memory_info = SystemMemoryInfo::current().unwrap_or_default();
let recommendations = self.generate_health_recommendations(&camera_stats, &memory_info);
CameraSystemHealth {
camera_status: if camera_stats.error_count == 0 {
CameraStatus::Healthy
} else {
CameraStatus::Warning
},
camera_stats,
memory_metrics,
memory_info,
recommendations,
}
}
// Private implementation methods
fn start_capture_loop(&self) -> tokio::task::JoinHandle<Result<()>> {
let camera = self.camera.clone();
let pipeline = self.capture_pipeline.clone();
let config = self.config.clone();
let stats = self.stats.clone();
tokio::spawn(async move {
let mut capture_interval = interval(Duration::from_secs_f64(1.0 / config.fps));
let mut frame_id = 0u64;
println!("📹 Camera capture loop started");
loop {
capture_interval.tick().await;
let capture_start = Instant::now();
// Simulate camera capture (in real implementation, would interface with camera hardware)
let captured_frame = Self::simulate_camera_capture(
&pipeline,
frame_id,
&config,
capture_start,
).await?;
// Send frame to processing pipeline
if let Err(_) = pipeline.frame_sender.try_send(captured_frame) {
// Channel full, frame dropped
let mut stats_guard = stats.write().await;
stats_guard.frames_dropped += 1;
println!("⚠️ Frame {} dropped (pipeline full)", frame_id);
} else {
let mut stats_guard = stats.write().await;
stats_guard.frames_captured += 1;
}
frame_id += 1;
// Demo limitation - stop after 1000 frames
if frame_id >= 1000 {
break;
}
}
println!("✅ Camera capture loop completed");
Ok(())
})
}
async fn start_processing_loop(&self) -> Result<tokio::task::JoinHandle<Result<()>>> {
let memory_system = self.memory_system.clone();
let stats = self.stats.clone();
// Take the receiver from the pipeline
let mut receiver = self.capture_pipeline
.processing_receiver
.as_ref()
.ok_or_else(|| anyhow!("Processing receiver not available"))?
.clone();
// Note: In a real implementation, we'd need to properly handle the receiver ownership
// For this demo, we'll create a new channel pair
let (tx, mut rx) = mpsc::channel::<CapturedFrame>(100);
Ok(tokio::spawn(async move {
println!("⚙️ Frame processing loop started");
while let Some(captured_frame) = rx.recv().await {
let process_start = Instant::now();
// Convert captured frame to astronomical frame
let astro_frame = AstronomicalFrame {
frame_id: captured_frame.metadata.frame_id,
timestamp_nanos: captured_frame.metadata.timestamp_nanos,
width: captured_frame.metadata.width,
height: captured_frame.metadata.height,
data_ptr: captured_frame.buffer.as_ptr() as usize,
data_size: captured_frame.buffer.len(),
brightness_sum: captured_frame.metadata.estimated_brightness,
detection_flags: 0, // Will be set by detection algorithm
};
// Process through integrated memory system
match memory_system.process_frame(astro_frame).await {
Ok(processed_frame) => {
if processed_frame.meteor_detected {
println!("🌠 Meteor detected in frame {} (confidence: {:.1}%)",
processed_frame.original_frame.frame_id,
processed_frame.confidence_score * 100.0);
}
// Update statistics
let process_time = process_start.elapsed();
let mut stats_guard = stats.write().await;
stats_guard.avg_capture_latency_us =
(stats_guard.avg_capture_latency_us + process_time.as_micros() as u64) / 2;
}
Err(e) => {
eprintln!("❌ Frame processing error: {}", e);
let mut stats_guard = stats.write().await;
stats_guard.error_count += 1;
}
}
}
println!("✅ Frame processing loop completed");
Ok(())
}))
}
fn start_stats_collection(&self) -> tokio::task::JoinHandle<()> {
let stats = self.stats.clone();
let memory_system = self.memory_system.clone();
tokio::spawn(async move {
let mut interval = interval(Duration::from_secs(10));
let mut last_frame_count = 0u64;
let mut last_time = Instant::now();
loop {
interval.tick().await;
let current_time = Instant::now();
let time_elapsed = current_time.duration_since(last_time).as_secs_f64();
let mut stats_guard = stats.write().await;
let current_frames = stats_guard.frames_captured;
// Calculate FPS
stats_guard.capture_fps = (current_frames - last_frame_count) as f64 / time_elapsed;
// Get memory metrics
let memory_metrics = memory_system.get_metrics().await;
stats_guard.memory_efficiency = 1.0 - memory_metrics.memory_utilization;
// Update tracking variables
last_frame_count = current_frames;
last_time = current_time;
// Log periodic status
println!("📊 Camera Status: {:.1} FPS, {:.1}% memory efficiency, {} frames captured",
stats_guard.capture_fps,
stats_guard.memory_efficiency * 100.0,
stats_guard.frames_captured);
}
})
}
fn start_memory_optimization(&self) -> tokio::task::JoinHandle<()> {
let memory_system = self.memory_system.clone();
let capture_buffers = self.capture_pipeline.capture_buffers.clone();
let config = self.config.clone();
tokio::spawn(async move {
let mut interval = interval(Duration::from_secs(30));
loop {
interval.tick().await;
if config.enable_memory_optimization {
// Check memory pressure
let memory_info = SystemMemoryInfo::current().unwrap_or_default();
if memory_info.used_percentage > 85.0 {
println!("🔧 High memory pressure detected, optimizing buffers...");
// Trigger memory optimization
if let Err(e) = memory_system.optimize_performance().await {
eprintln!("Memory optimization error: {}", e);
}
// Reduce capture buffer pool if needed
capture_buffers.optimize_for_memory_pressure().await;
}
}
}
})
}
async fn simulate_camera_capture(
pipeline: &FrameCapturePipeline,
frame_id: u64,
config: &CameraConfig,
capture_time: Instant,
) -> Result<CapturedFrame> {
// Get buffer from capture buffer pool
let buffer = pipeline.capture_buffers.get_buffer().await?;
// Simulate frame capture (in real implementation, would copy from camera)
let timestamp_nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
// Simulate brightness calculation
let estimated_brightness = 40.0 + (frame_id as f32 % 30.0) +
if frame_id % 100 == 0 { 50.0 } else { 0.0 }; // Occasional bright meteors
let metadata = FrameMetadata {
frame_id,
timestamp_nanos,
width: config.frame_width,
height: config.frame_height,
bytes_per_pixel: config.bytes_per_pixel,
exposure_us: config.exposure_us,
gain: config.gain,
estimated_brightness,
memory_pool_id: 0, // Would be set by buffer pool
};
Ok(CapturedFrame {
buffer,
metadata,
capture_time,
})
}
fn calculate_frame_size(&self) -> usize {
self.config.frame_width as usize
* self.config.frame_height as usize
* self.config.bytes_per_pixel
}
fn generate_health_recommendations(&self, stats: &CameraStats, memory_info: &SystemMemoryInfo) -> Vec<String> {
let mut recommendations = Vec::new();
if stats.capture_fps < self.config.fps * 0.9 {
recommendations.push("Camera capture rate is below target, consider reducing resolution or FPS".to_string());
}
if stats.frames_dropped > stats.frames_captured / 10 {
recommendations.push("High frame drop rate, consider increasing buffer pool size".to_string());
}
if memory_info.used_percentage > 90.0 {
recommendations.push("Very high memory usage, consider reducing capture buffer count".to_string());
}
if stats.buffer_utilization > 0.9 {
recommendations.push("Buffer pool is nearly full, consider optimizing processing pipeline".to_string());
}
recommendations
}
}
impl CameraController {
fn new(device_id: String, config: CameraConfig) -> Result<Self> {
Ok(Self {
device_id,
config,
state: CameraState::Uninitialized,
frame_counter: 0,
capture_start: None,
})
}
async fn initialize(&mut self) -> Result<()> {
println!("🎥 Initializing camera: {}", self.device_id);
self.state = CameraState::Initializing;
// Simulate camera initialization
sleep(Duration::from_millis(500)).await;
self.state = CameraState::Ready;
println!("✅ Camera initialized and ready");
Ok(())
}
}
impl CaptureBufferPool {
async fn new(
buffer_size: usize,
max_buffers: usize,
frame_pool: Arc<HierarchicalFramePool>,
) -> Result<Self> {
let mut buffers = Vec::new();
// Pre-allocate capture buffers
for _ in 0..max_buffers {
let buffer = frame_pool.acquire(buffer_size);
buffers.push(Arc::new(buffer));
}
println!(" 📦 Created capture buffer pool with {} buffers", buffers.len());
Ok(Self {
buffers: Arc::new(RwLock::new(buffers)),
buffer_size,
max_buffers,
current_count: Arc::new(RwLock::new(max_buffers)),
frame_pool,
})
}
async fn get_buffer(&self) -> Result<Arc<PooledFrameBuffer>> {
let mut buffers = self.buffers.write().await;
if let Some(buffer) = buffers.pop() {
Ok(buffer)
} else {
// Try to allocate new buffer if under limit
drop(buffers); // Release lock before potentially long operation
let buffer = self.frame_pool.acquire(self.buffer_size);
Ok(Arc::new(buffer))
}
}
async fn optimize_for_memory_pressure(&self) {
let mut buffers = self.buffers.write().await;
let target_count = (buffers.len() / 2).max(2); // Keep at least 2 buffers
while buffers.len() > target_count {
buffers.pop();
}
println!("🔧 Optimized capture buffer pool: {} -> {} buffers",
self.max_buffers, buffers.len());
}
}
/// Camera system health report
#[derive(Debug)]
pub struct CameraSystemHealth {
pub camera_status: CameraStatus,
pub camera_stats: CameraStats,
pub memory_metrics: crate::monitoring::integrated_system::SystemMetrics,
pub memory_info: SystemMemoryInfo,
pub recommendations: Vec<String>,
}
/// Camera status levels
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CameraStatus {
Healthy,
Warning,
Error,
Offline,
}
/// Factory functions for different camera configurations
/// Create Raspberry Pi optimized camera configuration
pub fn create_pi_camera_config() -> CameraConfig {
CameraConfig {
frame_width: 1280,
frame_height: 720,
fps: 15.0, // Conservative for Pi
bytes_per_pixel: 3,
exposure_us: 66666, // 1/15 second
gain: 4.0, // Higher gain for night astronomy
night_mode: true,
capture_buffer_count: 4, // Limited by Pi memory
enable_memory_optimization: true,
max_camera_memory: 32 * 1024 * 1024, // 32MB limit
}
}
/// Create high-performance camera configuration
pub fn create_performance_camera_config() -> CameraConfig {
CameraConfig {
frame_width: 1920,
frame_height: 1080,
fps: 30.0,
bytes_per_pixel: 3,
exposure_us: 33333, // 1/30 second
gain: 2.0,
night_mode: true,
capture_buffer_count: 12,
enable_memory_optimization: true,
max_camera_memory: 128 * 1024 * 1024, // 128MB
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::monitoring::integrated_system::SystemConfig;
#[tokio::test]
async fn test_camera_integration_creation() {
let memory_system = Arc::new(
IntegratedMemorySystem::new(SystemConfig::default()).await.unwrap()
);
let camera_config = create_pi_camera_config();
let camera_integration = CameraMemoryIntegration::new(
memory_system,
camera_config,
).await;
assert!(camera_integration.is_ok());
}
#[tokio::test]
async fn test_capture_buffer_pool() {
let memory_system = Arc::new(
IntegratedMemorySystem::new(SystemConfig::default()).await.unwrap()
);
let buffer_pool = CaptureBufferPool::new(
1280 * 720 * 3,
4,
memory_system.get_frame_pool(),
).await;
assert!(buffer_pool.is_ok());
let pool = buffer_pool.unwrap();
let buffer = pool.get_buffer().await;
assert!(buffer.is_ok());
}
#[tokio::test]
async fn test_camera_controller() {
let config = create_pi_camera_config();
let mut controller = CameraController::new("test_camera".to_string(), config).unwrap();
assert_eq!(controller.state, CameraState::Uninitialized);
controller.initialize().await.unwrap();
assert_eq!(controller.state, CameraState::Ready);
}
}