diff --git a/.env b/.env index deedbdf..b651f5b 100644 --- a/.env +++ b/.env @@ -17,6 +17,11 @@ TWILIO_ACCOUNT_SID=your_twilio_account_sid_here TWILIO_AUTH_TOKEN=your_twilio_auth_token_here TWILIO_PHONE_NUMBER=your_twilio_phone_number_here +# Health Probe Simulator Configuration +PROBE_FAILRATE=30 +PROBE_INTERVAL_SECONDS=60 +API_BASE_URL=https://selfservice.cqers.com/drones/api + # Optional: Override default settings # NODE_ENV=production # CORS_ORIGIN=http://localhost:3000 diff --git a/Dockerfile.healthprobe b/Dockerfile.healthprobe new file mode 100644 index 0000000..e4168fa --- /dev/null +++ b/Dockerfile.healthprobe @@ -0,0 +1,18 @@ +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Install required Python packages +RUN pip install --no-cache-dir requests + +# Copy the health probe simulator script +COPY health_probe_simulator.py . + +# Set environment variables with defaults +ENV PROBE_FAILRATE=30 +ENV PROBE_INTERVAL_SECONDS=60 +ENV API_BASE_URL=https://selfservice.cqers.com/drones/api + +# Run the health probe simulator +CMD ["python", "health_probe_simulator.py"] diff --git a/docker-compose.yml b/docker-compose.yml index 5f1703e..14a2687 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -142,6 +142,25 @@ services: - simulation command: python drone_simulator.py --devices 5 --duration 3600 + # Health Probe Simulator (Continuous Device Heartbeats) + healthprobe: + build: + context: . + dockerfile: Dockerfile.healthprobe + container_name: drone-detection-healthprobe + restart: unless-stopped + environment: + PROBE_FAILRATE: ${PROBE_FAILRATE:-30} + PROBE_INTERVAL_SECONDS: ${PROBE_INTERVAL_SECONDS:-60} + API_BASE_URL: http://backend:3001/api + networks: + - drone-network + depends_on: + backend: + condition: service_healthy + profiles: + - healthprobe + volumes: postgres_data: driver: local diff --git a/docs/HEALTH_PROBE_SIMULATOR.md b/docs/HEALTH_PROBE_SIMULATOR.md new file mode 100644 index 0000000..5e2287a --- /dev/null +++ b/docs/HEALTH_PROBE_SIMULATOR.md @@ -0,0 +1,174 @@ +# Health Probe Simulator + +The Health Probe Simulator continuously sends heartbeat messages from all devices to test the offline detection feature of the drone detection system. + +## Features + +- πŸ”„ **Continuous Monitoring**: Sends heartbeats every minute (configurable) +- πŸ“Š **Configurable Failure Rate**: Simulates device failures to test offline detection +- 🎯 **Real Device Integration**: Uses actual devices from your system +- πŸ“ˆ **Statistics Reporting**: Shows success/failure rates for each cycle +- 🐳 **Docker Support**: Can run as a container alongside the main system + +## Configuration + +### Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `PROBE_FAILRATE` | `30` | Percentage of probes that should fail (0-100) | +| `PROBE_INTERVAL_SECONDS` | `60` | Interval between probe cycles in seconds | +| `API_BASE_URL` | `https://selfservice.cqers.com/drones/api` | API endpoint URL | + +### .env File + +Add these lines to your `.env` file: + +```bash +# Health Probe Simulator Configuration +PROBE_FAILRATE=30 +PROBE_INTERVAL_SECONDS=60 +API_BASE_URL=https://selfservice.cqers.com/drones/api +``` + +## Usage + +### Option 1: Docker Compose (Recommended) + +Start the health probe simulator as a Docker service: + +```bash +# Start with health probe simulator +docker-compose --profile healthprobe up -d + +# View logs +docker-compose logs -f healthprobe + +# Stop the service +docker-compose --profile healthprobe down +``` + +### Option 2: Local Python Script + +Run directly with Python: + +```bash +# Linux/Mac +./start-healthprobe.sh + +# Windows +start-healthprobe.bat + +# Or directly with Python +python3 health_probe_simulator.py +``` + +### Option 3: Manual Docker Build + +```bash +# Build the health probe image +docker build -f Dockerfile.healthprobe -t health-probe-simulator . + +# Run the container +docker run -d \ + --name health-probe \ + --network uamils_drone-network \ + -e PROBE_FAILRATE=30 \ + -e PROBE_INTERVAL_SECONDS=60 \ + -e API_BASE_URL=http://backend:3001/api \ + health-probe-simulator +``` + +## Output Example + +``` +πŸ₯ DRONE DETECTION SYSTEM - HEALTH PROBE SIMULATOR +============================================================ + +πŸ“‹ Configuration: + PROBE_FAILRATE: 30% + PROBE_INTERVAL_SECONDS: 60s + API_BASE_URL: https://selfservice.cqers.com/drones/api + +πŸ”§ Health Probe Simulator Configuration: + API URL: https://selfservice.cqers.com/drones/api + Probe Interval: 60 seconds + Failure Rate: 30% + +πŸ“‘ Fetched 4 devices for health monitoring + - Device 1941875381: Arlanda Airport Detector (Arlanda Airport Terminal 2) + - Device 1725834521: Royal Castle Detector (Royal Castle Stockholm) + - Device 1456789123: MuskΓΆ Naval Base Detector (MuskΓΆ Naval Base) + - Device 1987654321: Gothenburg Port Detector (Port of Gothenburg) + +πŸ”„ Starting probe cycle at 2025-08-19 14:30:00 + Probing 4 devices... + +πŸ’“ Device 1941875381: Heartbeat sent successfully (Arlanda Airport Detector - Arlanda Airport Terminal 2) +πŸ’” Simulating probe failure for Device 1725834521 (30% failure rate) +πŸ’“ Device 1456789123: Heartbeat sent successfully (MuskΓΆ Naval Base Detector - MuskΓΆ Naval Base) +πŸ’” Simulating probe failure for Device 1987654321 (30% failure rate) + +πŸ“Š Probe Cycle Summary: + βœ… Successful: 2 + ❌ Failed: 2 + πŸ“ˆ Success Rate: 50.0% + 🎯 Target Failure Rate: 30% + ⏰ Next cycle in 60 seconds +``` + +## Testing Offline Detection + +The health probe simulator is perfect for testing your system's offline detection capabilities: + +1. **Set Failure Rate**: Adjust `PROBE_FAILRATE` to control how many devices appear offline +2. **Monitor Dashboard**: Watch the map view to see devices changing status +3. **Check Alerts**: Verify that offline device alerts are triggered +4. **Test Recovery**: Failed devices will randomly come back online in subsequent cycles + +## Use Cases + +- **πŸ§ͺ Testing**: Verify offline detection works correctly +- **🎭 Demo**: Show system behavior during device failures +- **πŸ“Š Load Testing**: Generate consistent heartbeat traffic +- **πŸ”§ Development**: Test heartbeat handling and device status logic + +## Stopping the Simulator + +### Docker Compose +```bash +docker-compose --profile healthprobe down +``` + +### Local Script +Press `Ctrl+C` to stop the simulator gracefully. + +### Docker Container +```bash +docker stop health-probe +docker rm health-probe +``` + +## Troubleshooting + +### Common Issues + +1. **Connection Errors**: Check API_BASE_URL and network connectivity +2. **Authentication Errors**: Ensure the API endpoint doesn't require authentication for heartbeats +3. **High CPU Usage**: Increase PROBE_INTERVAL_SECONDS to reduce frequency + +### Logs + +Check the container logs for detailed output: +```bash +docker-compose logs healthprobe +``` + +## Integration with Main System + +The health probe simulator: +- βœ… Uses the same heartbeat API as real devices +- βœ… Automatically discovers all devices in your system +- βœ… Respects your configured offline detection timeouts +- βœ… Generates realistic device status changes +- βœ… Helps validate your monitoring and alerting systems diff --git a/health_probe_simulator.py b/health_probe_simulator.py new file mode 100644 index 0000000..87a6d86 --- /dev/null +++ b/health_probe_simulator.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +""" +Health Check Probe Simulator +Continuously sends heartbeat probes from all devices to test offline detection +""" + +import requests +import time +import random +import os +import json +from datetime import datetime +from typing import List, Dict + +class DeviceProbeSimulator: + def __init__(self): + self.api_base_url = os.getenv('API_BASE_URL', 'https://selfservice.cqers.com/drones/api') + self.probe_interval = int(os.getenv('PROBE_INTERVAL_SECONDS', '60')) # 1 minute default + self.probe_failrate = int(os.getenv('PROBE_FAILRATE', '30')) # 30% failure rate + self.devices = [] + self.session = requests.Session() + self.session.headers.update({ + 'Content-Type': 'application/json', + 'User-Agent': 'HealthProbeSimulator/1.0' + }) + + print(f"πŸ”§ Health Probe Simulator Configuration:") + print(f" API URL: {self.api_base_url}") + print(f" Probe Interval: {self.probe_interval} seconds") + print(f" Failure Rate: {self.probe_failrate}%") + print() + + def fetch_devices(self) -> bool: + """Fetch all devices from the API""" + try: + response = self.session.get(f"{self.api_base_url}/devices") + if response.status_code == 200: + data = response.json() + if data.get('success') and 'data' in data: + self.devices = data['data'] + print(f"πŸ“‘ Fetched {len(self.devices)} devices for health monitoring") + for device in self.devices: + print(f" - Device {device['id']}: {device.get('name', 'Unnamed')} ({device.get('location_description', 'No location')})") + return True + else: + print(f"❌ API returned invalid data structure: {data}") + return False + else: + print(f"❌ Failed to fetch devices: HTTP {response.status_code}") + print(f" Response: {response.text}") + return False + except Exception as e: + print(f"❌ Error fetching devices: {e}") + return False + + def send_heartbeat(self, device: Dict) -> bool: + """Send heartbeat for a specific device""" + device_id = device['id'] + + # Simulate probe failure based on failure rate + if random.randint(1, 100) <= self.probe_failrate: + print(f"πŸ’” Simulating probe failure for Device {device_id} ({self.probe_failrate}% failure rate)") + return False + + try: + payload = { + 'device_id': device_id, + 'timestamp': int(time.time()), + 'status': 'online', + 'system_info': { + 'cpu_usage': random.randint(20, 80), + 'memory_usage': random.randint(30, 90), + 'disk_usage': random.randint(40, 85), + 'temperature': random.randint(25, 65), + 'uptime': random.randint(3600, 86400) # 1 hour to 1 day + } + } + + response = self.session.post( + f"{self.api_base_url}/devices/heartbeat", + json=payload, + timeout=10 + ) + + if response.status_code in [200, 201]: + device_name = device.get('name', f'Device {device_id}') + location = device.get('location_description', 'Unknown location') + print(f"πŸ’“ Device {device_id}: Heartbeat sent successfully ({device_name} - {location})") + return True + else: + print(f"❌ Failed to send heartbeat for Device {device_id}: HTTP {response.status_code}") + print(f" Response: {response.text}") + return False + + except requests.exceptions.Timeout: + print(f"⏰ Heartbeat timeout for Device {device_id}") + return False + except Exception as e: + print(f"❌ Error sending heartbeat for Device {device_id}: {e}") + return False + + def run_probe_cycle(self): + """Run one complete probe cycle for all devices""" + if not self.devices: + print("⚠️ No devices to probe, fetching device list...") + if not self.fetch_devices(): + print("❌ Cannot continue without device list") + return + + print(f"\nπŸ”„ Starting probe cycle at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f" Probing {len(self.devices)} devices...") + + successful_probes = 0 + failed_probes = 0 + + for device in self.devices: + if self.send_heartbeat(device): + successful_probes += 1 + else: + failed_probes += 1 + + # Small delay between probes to avoid overwhelming the server + time.sleep(0.5) + + success_rate = (successful_probes / len(self.devices)) * 100 if self.devices else 0 + print(f"\nπŸ“Š Probe Cycle Summary:") + print(f" βœ… Successful: {successful_probes}") + print(f" ❌ Failed: {failed_probes}") + print(f" πŸ“ˆ Success Rate: {success_rate:.1f}%") + print(f" 🎯 Target Failure Rate: {self.probe_failrate}%") + print(f" ⏰ Next cycle in {self.probe_interval} seconds") + + def run_continuous(self): + """Run continuous health check probes""" + print("πŸš€ Starting continuous health probe simulator...") + print(" Press Ctrl+C to stop") + print() + + # Initial device fetch + if not self.fetch_devices(): + print("❌ Failed to fetch initial device list, exiting") + return + + try: + while True: + self.run_probe_cycle() + + # Re-fetch devices every 10 cycles to catch new devices + if hasattr(self, 'cycle_count'): + self.cycle_count += 1 + else: + self.cycle_count = 1 + + if self.cycle_count % 10 == 0: + print(f"\nπŸ”„ Refreshing device list (cycle {self.cycle_count})...") + self.fetch_devices() + + # Wait for next cycle + time.sleep(self.probe_interval) + + except KeyboardInterrupt: + print(f"\n\nπŸ›‘ Health probe simulator stopped by user") + except Exception as e: + print(f"\n\n❌ Unexpected error in probe simulator: {e}") + +def main(): + print("=" * 60) + print("πŸ₯ DRONE DETECTION SYSTEM - HEALTH PROBE SIMULATOR") + print("=" * 60) + print() + + # Load environment variables + probe_failrate = os.getenv('PROBE_FAILRATE', '30') + probe_interval = os.getenv('PROBE_INTERVAL_SECONDS', '60') + api_url = os.getenv('API_BASE_URL', 'https://selfservice.cqers.com/drones/api') + + print(f"πŸ“‹ Configuration:") + print(f" PROBE_FAILRATE: {probe_failrate}%") + print(f" PROBE_INTERVAL_SECONDS: {probe_interval}s") + print(f" API_BASE_URL: {api_url}") + print() + + simulator = DeviceProbeSimulator() + simulator.run_continuous() + +if __name__ == "__main__": + main() diff --git a/server/services/alertService.js b/server/services/alertService.js index 30050b2..07db392 100644 --- a/server/services/alertService.js +++ b/server/services/alertService.js @@ -158,24 +158,57 @@ class AlertService { model: User, as: 'user', where: { is_active: true } - }] + }], + order: [ + // Order by priority: critical, high, medium, low + ['priority', 'DESC'] + ] }); + console.log(`πŸ“‹ Found ${alertRules.length} active alert rules`); + + // ENHANCED: Filter rules that match this detection and find the highest priority one + const matchingRules = []; + for (const rule of alertRules) { if (await this.shouldTriggerAlert(rule, detection, threatAssessment)) { - await this.triggerAlert(rule, detection, threatAssessment); - - // Track active alert for potential clear notification - const alertKey = `${rule.id}-${detection.device_id}`; - this.activeAlerts.set(alertKey, { - rule, - detection, - threatAssessment, - alertTime: new Date() - }); + matchingRules.push(rule); + console.log(`βœ… Rule "${rule.name}" matches (priority: ${rule.priority})`); + } else { + console.log(`❌ Rule "${rule.name}" does not match conditions`); } } + if (matchingRules.length === 0) { + console.log('πŸ“­ No alert rules match this detection'); + return; + } + + // PRIORITY-BASED DEDUPLICATION: Only trigger the highest priority rule + const highestPriorityRule = matchingRules[0]; // Already sorted by priority DESC + + console.log(`🎯 ALERT DEDUPLICATION: ${matchingRules.length} rules matched, triggering only highest priority:`); + console.log(` Selected Rule: "${highestPriorityRule.name}" (priority: ${highestPriorityRule.priority})`); + + if (matchingRules.length > 1) { + console.log(` Skipped Rules:`); + matchingRules.slice(1).forEach(rule => { + console.log(` - "${rule.name}" (priority: ${rule.priority})`); + }); + } + + // Trigger only the highest priority alert + await this.triggerAlert(highestPriorityRule, detection, threatAssessment); + + // Track active alert for potential clear notification + const alertKey = `${highestPriorityRule.id}-${detection.device_id}`; + this.activeAlerts.set(alertKey, { + rule: highestPriorityRule, + detection, + threatAssessment, + alertTime: new Date() + }); + } catch (error) { console.error('Error processing alert:', error); throw error; @@ -184,64 +217,84 @@ class AlertService { async shouldTriggerAlert(rule, detection, threatAssessment) { try { - // CRITICAL SECURITY: Orlan drones (type 1) ALWAYS trigger alerts regardless of any other conditions - if (detection.drone_type === 1) { - console.log(`🚨 ORLAN DRONE DETECTED - Force triggering alert for rule ${rule.name} (bypassing all filters)`); + console.log(`πŸ” Evaluating rule "${rule.name}" for detection from device ${detection.device_id}`); + + // PRIORITY 1: Device-specific filter (most important) + if (rule.device_ids && rule.device_ids.length > 0) { + if (!rule.device_ids.includes(detection.device_id)) { + console.log(`❌ Rule "${rule.name}": Device ${detection.device_id} not in allowed devices [${rule.device_ids.join(', ')}]`); + return false; + } else { + console.log(`βœ… Rule "${rule.name}": Device ${detection.device_id} is in allowed devices`); + } + } else { + console.log(`βœ… Rule "${rule.name}": No device filter (applies to all devices)`); + } + + // PRIORITY 2: Critical threats and Orlan drones (security overrides) + const isOrlanDrone = detection.drone_type === 1; + const isCriticalThreat = threatAssessment.level === 'critical'; + + if (isOrlanDrone) { + console.log(`🚨 ORLAN DRONE DETECTED - Rule "${rule.name}" will trigger (security override)`); + return true; + } + + if (isCriticalThreat) { + console.log(`🚨 CRITICAL THREAT DETECTED - Rule "${rule.name}" will trigger (security override)`); return true; } - // SECURITY ENHANCEMENT: Check threat level requirements + // PRIORITY 3: Threat level requirements if (rule.min_threat_level) { const threatLevels = { 'monitoring': 0, 'low': 1, 'medium': 2, 'high': 3, 'critical': 4 }; const requiredLevel = threatLevels[rule.min_threat_level] || 0; const currentLevel = threatLevels[threatAssessment.level] || 0; if (currentLevel < requiredLevel) { - console.log(`Alert rule ${rule.name}: Threat level ${threatAssessment.level} below minimum ${rule.min_threat_level}`); + console.log(`❌ Rule "${rule.name}": Threat level ${threatAssessment.level} below minimum ${rule.min_threat_level}`); return false; + } else { + console.log(`βœ… Rule "${rule.name}": Threat level ${threatAssessment.level} meets minimum ${rule.min_threat_level}`); } } - // SECURITY ENHANCEMENT: For government/sensitive sites, always alert on critical threats - if (threatAssessment.level === 'critical') { - console.log(`🚨 CRITICAL THREAT DETECTED - Force triggering alert for rule ${rule.name}`); - return true; - } - - // Check device filter - if (rule.device_ids && rule.device_ids.length > 0 && - !rule.device_ids.includes(detection.device_id)) { - return false; - } - - // Check drone type filter + // PRIORITY 4: Drone type filter if (rule.drone_types && rule.drone_types.length > 0 && !rule.drone_types.includes(detection.drone_type)) { + console.log(`❌ Rule "${rule.name}": Drone type ${detection.drone_type} not in allowed types [${rule.drone_types.join(', ')}]`); return false; } - // Check RSSI thresholds (enhanced for security) + // PRIORITY 5: RSSI thresholds (distance-based) if (rule.min_rssi && detection.rssi < rule.min_rssi) { + console.log(`❌ Rule "${rule.name}": RSSI ${detection.rssi} below minimum ${rule.min_rssi}`); return false; } - // SECURITY ENHANCEMENT: Check estimated distance + if (rule.max_rssi && detection.rssi > rule.max_rssi) { + console.log(`❌ Rule "${rule.name}": RSSI ${detection.rssi} above maximum ${rule.max_rssi}`); + return false; + } + + // PRIORITY 6: Distance requirements if (rule.max_distance && threatAssessment.estimatedDistance > rule.max_distance) { - console.log(`Alert rule ${rule.name}: Distance ${threatAssessment.estimatedDistance}m exceeds maximum ${rule.max_distance}m`); + console.log(`❌ Rule "${rule.name}": Distance ${threatAssessment.estimatedDistance}m exceeds maximum ${rule.max_distance}m`); return false; } - // Check frequency ranges + // PRIORITY 7: Frequency ranges if (rule.frequency_ranges && rule.frequency_ranges.length > 0) { const inRange = rule.frequency_ranges.some(range => detection.freq >= range.min && detection.freq <= range.max ); if (!inRange) { + console.log(`❌ Rule "${rule.name}": Frequency ${detection.freq}MHz not in allowed ranges`); return false; } } - // Check time window and minimum detections + // PRIORITY 8: Time window and minimum detections if (rule.min_detections > 1) { const timeWindowStart = new Date(Date.now() - rule.time_window * 1000); const recentDetections = await DroneDetection.count({ @@ -255,11 +308,12 @@ class AlertService { }); if (recentDetections < rule.min_detections) { + console.log(`❌ Rule "${rule.name}": Only ${recentDetections} detections, need ${rule.min_detections}`); return false; } } - // Check cooldown period + // PRIORITY 9: Cooldown period check if (rule.cooldown_period > 0) { const cooldownStart = new Date(Date.now() - rule.cooldown_period * 1000); const recentAlert = await AlertLog.findOne({ @@ -281,11 +335,12 @@ class AlertService { }); if (recentAlert) { + console.log(`❌ Rule "${rule.name}": Still in cooldown period`); return false; } } - // Check active hours and days + // PRIORITY 10: Active hours and days if (rule.active_hours || rule.active_days) { const now = new Date(); const currentHour = now.getHours(); @@ -294,6 +349,7 @@ class AlertService { const currentDay = now.getDay() || 7; // Convert Sunday from 0 to 7 if (rule.active_days && !rule.active_days.includes(currentDay)) { + console.log(`❌ Rule "${rule.name}": Not active on day ${currentDay}`); return false; } @@ -305,11 +361,13 @@ class AlertService { if (startTime <= endTime) { // Same day range if (currentTime < startTime || currentTime > endTime) { + console.log(`❌ Rule "${rule.name}": Outside active hours`); return false; } } else { // Overnight range if (currentTime < startTime && currentTime > endTime) { + console.log(`❌ Rule "${rule.name}": Outside active hours (overnight)`); return false; } } @@ -317,10 +375,11 @@ class AlertService { } } + console.log(`βœ… Rule "${rule.name}": All conditions met - WILL TRIGGER`); return true; } catch (error) { - console.error('Error checking alert conditions:', error); + console.error(`Error evaluating alert rule ${rule.name}:`, error); return false; } } diff --git a/start-healthprobe.bat b/start-healthprobe.bat new file mode 100644 index 0000000..8624db4 --- /dev/null +++ b/start-healthprobe.bat @@ -0,0 +1,46 @@ +@echo off +REM Health Probe Simulator Startup Script (Windows) +REM This script starts the health probe simulator that continuously sends heartbeats + +echo πŸ₯ Starting Health Probe Simulator... +echo ================================== + +REM Set defaults if not provided +if not defined PROBE_FAILRATE set PROBE_FAILRATE=30 +if not defined PROBE_INTERVAL_SECONDS set PROBE_INTERVAL_SECONDS=60 +if not defined API_BASE_URL set API_BASE_URL=https://selfservice.cqers.com/drones/api + +echo πŸ”§ Configuration: +echo Failure Rate: %PROBE_FAILRATE%%% +echo Probe Interval: %PROBE_INTERVAL_SECONDS% seconds +echo API URL: %API_BASE_URL% +echo. + +REM Check if running with Docker +if "%1"=="docker" ( + echo 🐳 Starting with Docker Compose... + docker-compose --profile healthprobe up healthprobe +) else ( + echo πŸ’» Running locally... + + REM Check if Python is available + python --version >nul 2>&1 + if errorlevel 1 ( + echo ❌ Python is required but not installed + pause + exit /b 1 + ) + + REM Check if requests module is available + python -c "import requests" >nul 2>&1 + if errorlevel 1 ( + echo πŸ“¦ Installing requests module... + pip install requests + ) + + REM Run the health probe simulator + echo πŸš€ Starting health probe simulator... + python health_probe_simulator.py +) + +pause diff --git a/start-healthprobe.sh b/start-healthprobe.sh new file mode 100644 index 0000000..a3e0f67 --- /dev/null +++ b/start-healthprobe.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Health Probe Simulator Startup Script +# This script starts the health probe simulator that continuously sends heartbeats + +echo "πŸ₯ Starting Health Probe Simulator..." +echo "==================================" + +# Load environment variables +if [ -f .env ]; then + echo "πŸ“‹ Loading environment from .env file..." + export $(cat .env | grep -v '^#' | xargs) +fi + +# Set defaults if not provided +export PROBE_FAILRATE=${PROBE_FAILRATE:-30} +export PROBE_INTERVAL_SECONDS=${PROBE_INTERVAL_SECONDS:-60} +export API_BASE_URL=${API_BASE_URL:-https://selfservice.cqers.com/drones/api} + +echo "πŸ”§ Configuration:" +echo " Failure Rate: ${PROBE_FAILRATE}%" +echo " Probe Interval: ${PROBE_INTERVAL_SECONDS} seconds" +echo " API URL: ${API_BASE_URL}" +echo "" + +# Check if running in Docker +if [ -n "$DOCKER_CONTAINER" ]; then + echo "🐳 Running in Docker container..." + # Run with Docker Compose profile + docker-compose --profile healthprobe up healthprobe +else + echo "πŸ’» Running locally..." + # Check if Python is available + if ! command -v python3 &> /dev/null; then + echo "❌ Python 3 is required but not installed" + exit 1 + fi + + # Check if requests module is available + if ! python3 -c "import requests" &> /dev/null; then + echo "πŸ“¦ Installing requests module..." + pip3 install requests + fi + + # Run the health probe simulator + echo "πŸš€ Starting health probe simulator..." + python3 health_probe_simulator.py +fi