722 lines
24 KiB
Rust
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);
|
|
}
|
|
} |