323 lines
13 KiB
Rust
323 lines
13 KiB
Rust
use anyhow::Result;
|
|
use std::time::Duration;
|
|
use tokio::task::JoinHandle;
|
|
use tokio::time::sleep;
|
|
|
|
use crate::events::{EventBus, SystemEvent, SystemStartedEvent};
|
|
use crate::camera::{CameraController, CameraConfig};
|
|
use crate::config::{load_camera_config, load_storage_config, load_communication_config, ConfigManager};
|
|
use crate::detection::{DetectionController, DetectionConfig};
|
|
use crate::storage::{StorageController, StorageConfig};
|
|
use crate::communication::{CommunicationController, CommunicationConfig};
|
|
use crate::api::ApiClient;
|
|
use crate::memory_monitor::{MemoryMonitor, record_frame_processed};
|
|
|
|
/// Core application coordinator that manages the event bus and background tasks
|
|
pub struct Application {
|
|
event_bus: EventBus,
|
|
background_tasks: Vec<JoinHandle<()>>,
|
|
memory_monitor: MemoryMonitor,
|
|
}
|
|
|
|
impl Application {
|
|
/// Create a new Application instance with an event bus
|
|
pub fn new(event_bus_capacity: usize) -> Self {
|
|
Self {
|
|
event_bus: EventBus::new(event_bus_capacity),
|
|
background_tasks: Vec::new(),
|
|
memory_monitor: MemoryMonitor::new(),
|
|
}
|
|
}
|
|
|
|
/// Start the application and run the main event loop
|
|
pub async fn run(&mut self) -> Result<()> {
|
|
println!("🚀 Starting Meteor Edge Client Application...");
|
|
|
|
// Create a test subscriber to verify event flow
|
|
let mut test_subscriber = self.event_bus.subscribe();
|
|
|
|
// Spawn a background task to handle test events
|
|
let test_handle = tokio::spawn(async move {
|
|
println!("📡 Test subscriber started, waiting for events...");
|
|
let mut system_started = false;
|
|
let mut frame_count = 0;
|
|
|
|
while let Ok(event) = test_subscriber.recv().await {
|
|
match event.as_ref() {
|
|
SystemEvent::SystemStarted(system_event) => {
|
|
println!("✅ Received SystemStartedEvent!");
|
|
println!(" Timestamp: {}", system_event.timestamp);
|
|
println!(" Version: {}", system_event.version);
|
|
println!(" Event verification successful! 🎉");
|
|
system_started = true;
|
|
}
|
|
SystemEvent::FrameCaptured(frame_event) => {
|
|
frame_count += 1;
|
|
|
|
// Record memory optimization metrics
|
|
record_frame_processed(frame_event.data_size(), 3); // Assume 3 subscribers
|
|
|
|
if frame_count <= 5 || frame_count % 30 == 0 {
|
|
println!("📸 Received FrameCapturedEvent #{}", frame_event.frame_id);
|
|
println!(" Timestamp: {}", frame_event.timestamp);
|
|
let (width, height) = frame_event.dimensions();
|
|
println!(" Resolution: {}x{}", width, height);
|
|
println!(" Data size: {} bytes (zero-copy!)", frame_event.data_size());
|
|
println!(" Format: {:?}", frame_event.frame_data.format);
|
|
}
|
|
|
|
// Exit after receiving some frames for demo
|
|
if frame_count >= 10 {
|
|
println!("🎬 Received {} frames, test subscriber stopping...", frame_count);
|
|
break;
|
|
}
|
|
}
|
|
SystemEvent::MeteorDetected(meteor_event) => {
|
|
println!("🌟 METEOR ALERT! Frame #{}, Confidence: {:.2}%",
|
|
meteor_event.trigger_frame_id,
|
|
meteor_event.confidence_score * 100.0
|
|
);
|
|
println!(" Algorithm: {}", meteor_event.algorithm_name);
|
|
println!(" Detected at: {}", meteor_event.detection_timestamp);
|
|
}
|
|
SystemEvent::EventPackageArchived(archive_event) => {
|
|
println!("📦 EVENT ARCHIVED! ID: {}, Size: {} bytes",
|
|
archive_event.event_id,
|
|
archive_event.archive_size_bytes
|
|
);
|
|
println!(" Directory: {:?}", archive_event.event_directory_path);
|
|
println!(" Frames: {}", archive_event.total_frames);
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("🔚 Test subscriber finished");
|
|
});
|
|
|
|
self.background_tasks.push(test_handle);
|
|
|
|
// Give the subscriber a moment to be ready
|
|
sleep(Duration::from_millis(10)).await;
|
|
|
|
// Publish the SystemStartedEvent to verify the event flow
|
|
println!("📢 Publishing SystemStartedEvent...");
|
|
let system_started_event = SystemStartedEvent::new();
|
|
|
|
self.event_bus.publish_system_started(system_started_event)?;
|
|
println!(" Event published successfully!");
|
|
|
|
// Initialize and start camera controller
|
|
println!("🎥 Initializing camera controller...");
|
|
let camera_config = load_camera_config()?;
|
|
let mut camera_controller = CameraController::new(camera_config, self.event_bus.clone());
|
|
|
|
// Spawn camera controller in background task
|
|
let camera_handle = tokio::spawn(async move {
|
|
if let Err(e) = camera_controller.run().await {
|
|
eprintln!("❌ Camera controller error: {}", e);
|
|
}
|
|
});
|
|
|
|
self.background_tasks.push(camera_handle);
|
|
|
|
// Start memory monitoring reporting
|
|
println!("📊 Starting memory optimization monitoring...");
|
|
let memory_handle = tokio::spawn(async move {
|
|
use crate::memory_monitor::GLOBAL_MEMORY_MONITOR;
|
|
GLOBAL_MEMORY_MONITOR.start_reporting(30).await; // Report every 30 seconds
|
|
});
|
|
self.background_tasks.push(memory_handle);
|
|
|
|
// Initialize and start detection controller
|
|
println!("🔍 Initializing detection controller...");
|
|
let detection_config = DetectionConfig::default();
|
|
let mut detection_controller = DetectionController::new(detection_config, self.event_bus.clone());
|
|
|
|
// Spawn detection controller in background task
|
|
let detection_handle = tokio::spawn(async move {
|
|
if let Err(e) = detection_controller.run().await {
|
|
eprintln!("❌ Detection controller error: {}", e);
|
|
}
|
|
});
|
|
|
|
self.background_tasks.push(detection_handle);
|
|
|
|
// Initialize and start storage controller
|
|
println!("💾 Initializing storage controller...");
|
|
let storage_config = load_storage_config()?;
|
|
let mut storage_controller = match StorageController::new(storage_config, self.event_bus.clone()) {
|
|
Ok(controller) => controller,
|
|
Err(e) => {
|
|
eprintln!("❌ Failed to create storage controller: {}", e);
|
|
return Err(e);
|
|
}
|
|
};
|
|
|
|
// Spawn storage controller in background task
|
|
let storage_handle = tokio::spawn(async move {
|
|
if let Err(e) = storage_controller.run().await {
|
|
eprintln!("❌ Storage controller error: {}", e);
|
|
}
|
|
});
|
|
|
|
self.background_tasks.push(storage_handle);
|
|
|
|
// Initialize and start communication controller
|
|
println!("📡 Initializing communication controller...");
|
|
let communication_config = load_communication_config()?;
|
|
let heartbeat_config = communication_config.clone(); // Clone before moving
|
|
|
|
let mut communication_controller = match CommunicationController::new(communication_config, self.event_bus.clone()) {
|
|
Ok(controller) => controller,
|
|
Err(e) => {
|
|
eprintln!("❌ Failed to create communication controller: {}", e);
|
|
return Err(e);
|
|
}
|
|
};
|
|
|
|
// Spawn communication controller in background task
|
|
let communication_handle = tokio::spawn(async move {
|
|
if let Err(e) = communication_controller.run().await {
|
|
eprintln!("❌ Communication controller error: {}", e);
|
|
}
|
|
});
|
|
|
|
self.background_tasks.push(communication_handle);
|
|
|
|
// Initialize and start heartbeat task
|
|
println!("💓 Initializing heartbeat task...");
|
|
let heartbeat_handle = tokio::spawn(async move {
|
|
if let Err(e) = Self::run_heartbeat_task(heartbeat_config).await {
|
|
eprintln!("❌ Heartbeat task error: {}", e);
|
|
}
|
|
});
|
|
|
|
self.background_tasks.push(heartbeat_handle);
|
|
|
|
// Run the main application loop
|
|
println!("🔄 Starting main application loop...");
|
|
self.main_loop().await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Main application loop - this will eventually coordinate all modules
|
|
async fn main_loop(&mut self) -> Result<()> {
|
|
println!("⏳ Main loop running... (will exit after 10 seconds for demo)");
|
|
|
|
// For now, just wait a bit to allow the camera to capture frames and test subscriber to process events
|
|
sleep(Duration::from_secs(10)).await;
|
|
|
|
println!("🛑 Stopping application...");
|
|
|
|
// Wait for all background tasks to complete
|
|
for task in self.background_tasks.drain(..) {
|
|
if let Err(e) = task.await {
|
|
eprintln!("❌ Background task error: {}", e);
|
|
}
|
|
}
|
|
|
|
println!("✅ Application stopped successfully");
|
|
Ok(())
|
|
}
|
|
|
|
/// Get a reference to the event bus (for other modules to use)
|
|
pub fn event_bus(&self) -> &EventBus {
|
|
&self.event_bus
|
|
}
|
|
|
|
/// Get the number of active subscribers to the event bus
|
|
pub fn subscriber_count(&self) -> usize {
|
|
self.event_bus.subscriber_count()
|
|
}
|
|
|
|
/// Background task for sending heartbeat signals to the backend
|
|
async fn run_heartbeat_task(config: CommunicationConfig) -> Result<()> {
|
|
println!("💓 Starting heartbeat task...");
|
|
println!(" Heartbeat interval: {}s", config.heartbeat_interval_seconds);
|
|
|
|
let api_client = ApiClient::new(config.api_base_url.clone());
|
|
let config_manager = ConfigManager::new();
|
|
|
|
loop {
|
|
// Wait for the configured interval
|
|
sleep(Duration::from_secs(config.heartbeat_interval_seconds)).await;
|
|
|
|
// Check if device is registered and has configuration
|
|
if !config_manager.config_exists() {
|
|
println!("⚠️ No device configuration found, skipping heartbeat");
|
|
continue;
|
|
}
|
|
|
|
let device_config = match config_manager.load_config() {
|
|
Ok(config) => config,
|
|
Err(e) => {
|
|
eprintln!("❌ Failed to load device configuration: {}", e);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
// Skip heartbeat if device is not registered
|
|
if !device_config.registered {
|
|
println!("⚠️ Device not registered, skipping heartbeat");
|
|
continue;
|
|
}
|
|
|
|
// Skip heartbeat if no JWT token is available
|
|
let jwt_token = match device_config.jwt_token {
|
|
Some(token) => token,
|
|
None => {
|
|
eprintln!("❌ No JWT token available for heartbeat authentication");
|
|
continue;
|
|
}
|
|
};
|
|
|
|
// Send heartbeat
|
|
match api_client.send_heartbeat(device_config.hardware_id, jwt_token).await {
|
|
Ok(_) => {
|
|
println!("✅ Heartbeat sent successfully");
|
|
}
|
|
Err(e) => {
|
|
eprintln!("❌ Heartbeat failed: {}", e);
|
|
// Continue the loop - don't crash on heartbeat failures
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use tokio::time::{timeout, Duration};
|
|
|
|
#[tokio::test]
|
|
async fn test_application_creation() {
|
|
let app = Application::new(100);
|
|
assert_eq!(app.subscriber_count(), 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_application_event_bus_access() {
|
|
let app = Application::new(100);
|
|
|
|
// Test that we can access the event bus
|
|
let event_bus = app.event_bus();
|
|
let mut receiver = event_bus.subscribe();
|
|
|
|
// Verify subscriber count increased
|
|
assert_eq!(app.subscriber_count(), 1);
|
|
|
|
// Test publishing through the app's event bus
|
|
let test_event = SystemStartedEvent::new();
|
|
event_bus.publish_system_started(test_event.clone()).unwrap();
|
|
|
|
// Verify we can receive the event
|
|
let received = timeout(Duration::from_millis(100), receiver.recv())
|
|
.await
|
|
.expect("Should receive event")
|
|
.unwrap();
|
|
|
|
assert!(matches!(received, SystemEvent::SystemStarted(_)));
|
|
}
|
|
} |