cleanup and files added

This commit is contained in:
2025-08-26 13:24:53 -07:00
parent a379d7a063
commit 51a14cd61c
8968 changed files with 1292619 additions and 0 deletions

View File

@@ -0,0 +1,673 @@
"""
Enhanced Smart Intersection Controller with MQTT + InfluxDB Integration
Hybrid Desktop + Services architecture for professional monitoring
"""
import json
import time
import logging
import asyncio
import threading
from datetime import datetime
from typing import Dict, List, Optional, Any
from pathlib import Path
from dataclasses import dataclass
import paho.mqtt.client as mqtt
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
from PySide6.QtCore import QObject, Signal, QTimer, QThread
# Import the original controller
from controllers.smart_intersection_controller import SmartIntersectionController as BaseController
@dataclass
class ServiceConfig:
"""Configuration for MQTT and InfluxDB services"""
mqtt_broker: str = "localhost"
mqtt_port: int = 1883
mqtt_keepalive: int = 60
mqtt_client_id: str = "smart_intersection_desktop"
influxdb_url: str = "http://localhost:8086"
influxdb_token: str = "smart-intersection-super-secret-token"
influxdb_org: str = "smart-intersection-org"
influxdb_bucket: str = "traffic_monitoring"
# Service health monitoring
service_check_interval: int = 30 # seconds
auto_reconnect: bool = True
max_retries: int = 3
class MQTTPublisher(QObject):
"""Handles MQTT publishing for real-time events"""
# Signals for connection status
connected = Signal()
disconnected = Signal()
error_occurred = Signal(str)
message_published = Signal(str, dict)
def __init__(self, config: ServiceConfig):
super().__init__()
self.config = config
self.client = None
self.is_connected = False
self.topic_base = "smartintersection"
# Load topic definitions
self.topics = self._load_topic_definitions()
# Setup MQTT client
self._setup_mqtt_client()
def _load_topic_definitions(self) -> Dict:
"""Load MQTT topic definitions from config"""
try:
topics_path = Path(__file__).parent.parent / "services" / "mqtt" / "topics.json"
if topics_path.exists():
with open(topics_path, 'r') as f:
config = json.load(f)
return config.get('mqtt_topics', {}).get('topics', {})
except Exception as e:
print(f"Warning: Could not load MQTT topics config: {e}")
# Default topics
return {
"detection": {"topic": "smartintersection/detection", "qos": 1},
"violations": {"topic": "smartintersection/violations", "qos": 2},
"performance": {"topic": "smartintersection/performance", "qos": 0},
"traffic_flow": {"topic": "smartintersection/traffic/flow", "qos": 1},
"pedestrian_safety": {"topic": "smartintersection/safety/pedestrian", "qos": 2},
"roi_events": {"topic": "smartintersection/roi/events", "qos": 1},
"system_health": {"topic": "smartintersection/system/health", "qos": 1}
}
def _setup_mqtt_client(self):
"""Setup MQTT client with callbacks"""
self.client = mqtt.Client(client_id=self.config.mqtt_client_id)
# Set callbacks
self.client.on_connect = self._on_connect
self.client.on_disconnect = self._on_disconnect
self.client.on_publish = self._on_publish
self.client.on_message = self._on_message
def _on_connect(self, client, userdata, flags, rc):
"""Callback for successful MQTT connection"""
if rc == 0:
self.is_connected = True
self.connected.emit()
print("✅ MQTT: Connected to broker")
else:
self.error_occurred.emit(f"MQTT connection failed with code {rc}")
def _on_disconnect(self, client, userdata, rc):
"""Callback for MQTT disconnection"""
self.is_connected = False
self.disconnected.emit()
print("🔌 MQTT: Disconnected from broker")
def _on_publish(self, client, userdata, mid):
"""Callback for successful message publish"""
pass # Could emit signal with message ID
def _on_message(self, client, userdata, msg):
"""Callback for received messages"""
pass # Not used for publishing-only client
def connect(self) -> bool:
"""Connect to MQTT broker"""
try:
self.client.connect(
self.config.mqtt_broker,
self.config.mqtt_port,
self.config.mqtt_keepalive
)
self.client.loop_start()
return True
except Exception as e:
self.error_occurred.emit(f"MQTT connection error: {e}")
return False
def disconnect(self):
"""Disconnect from MQTT broker"""
if self.client:
self.client.loop_stop()
self.client.disconnect()
def publish_detection_event(self, detection_data: Dict):
"""Publish object detection event"""
self._publish_message("detection", detection_data)
def publish_violation_event(self, violation_data: Dict):
"""Publish traffic violation event"""
self._publish_message("violations", violation_data)
def publish_performance_metrics(self, performance_data: Dict):
"""Publish system performance metrics"""
self._publish_message("performance", performance_data)
def publish_traffic_flow(self, flow_data: Dict):
"""Publish traffic flow analytics"""
self._publish_message("traffic_flow", flow_data)
def publish_pedestrian_safety(self, safety_data: Dict):
"""Publish pedestrian safety events"""
self._publish_message("pedestrian_safety", safety_data)
def publish_roi_event(self, roi_data: Dict):
"""Publish ROI-based events"""
self._publish_message("roi_events", roi_data)
def publish_system_health(self, health_data: Dict):
"""Publish system health status"""
self._publish_message("system_health", health_data)
def _publish_message(self, topic_key: str, data: Dict):
"""Internal method to publish messages"""
if not self.is_connected:
return False
try:
topic_config = self.topics.get(topic_key, {})
topic = topic_config.get("topic", f"{self.topic_base}/{topic_key}")
qos = topic_config.get("qos", 0)
# Add timestamp if not present
if "timestamp" not in data:
data["timestamp"] = datetime.now().isoformat()
# Convert to JSON
message = json.dumps(data)
# Publish message
result = self.client.publish(topic, message, qos=qos)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
self.message_published.emit(topic, data)
return True
else:
self.error_occurred.emit(f"Failed to publish to {topic}: {result.rc}")
return False
except Exception as e:
self.error_occurred.emit(f"Error publishing message: {e}")
return False
class InfluxDBWriter(QObject):
"""Handles InfluxDB time series data writing"""
# Signals for connection status
connected = Signal()
disconnected = Signal()
error_occurred = Signal(str)
data_written = Signal(str, int) # measurement, points_count
def __init__(self, config: ServiceConfig):
super().__init__()
self.config = config
self.client = None
self.write_api = None
self.is_connected = False
# Connect to InfluxDB
self._connect()
def _connect(self):
"""Connect to InfluxDB"""
try:
self.client = InfluxDBClient(
url=self.config.influxdb_url,
token=self.config.influxdb_token,
org=self.config.influxdb_org
)
# Test connection
self.client.ping()
# Setup write API
self.write_api = self.client.write_api(write_options=SYNCHRONOUS)
self.is_connected = True
self.connected.emit()
print("✅ InfluxDB: Connected successfully")
except Exception as e:
self.is_connected = False
self.error_occurred.emit(f"InfluxDB connection error: {e}")
print(f"❌ InfluxDB: Connection failed - {e}")
def disconnect(self):
"""Disconnect from InfluxDB"""
if self.client:
self.client.close()
self.is_connected = False
self.disconnected.emit()
def write_performance_metrics(self, metrics: Dict):
"""Write performance metrics to InfluxDB"""
if not self.is_connected:
return
try:
point = Point("performance") \
.tag("service", "desktop_app") \
.field("fps", float(metrics.get("fps", 0))) \
.field("gpu_usage", float(metrics.get("gpu_usage", 0))) \
.field("memory_usage", float(metrics.get("memory_usage", 0))) \
.field("processing_time_ms", float(metrics.get("processing_time_ms", 0))) \
.field("camera_count", int(metrics.get("camera_count", 0))) \
.field("active_objects", int(metrics.get("active_objects", 0))) \
.time(datetime.utcnow(), WritePrecision.NS)
self.write_api.write(bucket=self.config.influxdb_bucket, record=point)
self.data_written.emit("performance", 1)
except Exception as e:
self.error_occurred.emit(f"Error writing performance metrics: {e}")
def write_detection_event(self, detection: Dict):
"""Write detection event to InfluxDB"""
if not self.is_connected:
return
try:
camera_id = detection.get("camera_id", "unknown")
camera_position = detection.get("camera_position", "unknown")
point = Point("detection_events") \
.tag("camera_id", camera_id) \
.tag("camera_position", camera_position) \
.tag("detection_model", detection.get("model", "yolo")) \
.field("object_count", int(detection.get("object_count", 0))) \
.field("vehicle_count", int(detection.get("vehicle_count", 0))) \
.field("pedestrian_count", int(detection.get("pedestrian_count", 0))) \
.field("confidence_avg", float(detection.get("confidence_avg", 0))) \
.field("processing_time", float(detection.get("processing_time", 0))) \
.time(datetime.utcnow(), WritePrecision.NS)
self.write_api.write(bucket=self.config.influxdb_bucket, record=point)
self.data_written.emit("detection_events", 1)
except Exception as e:
self.error_occurred.emit(f"Error writing detection event: {e}")
def write_violation_event(self, violation: Dict):
"""Write violation event to InfluxDB"""
if not self.is_connected:
return
try:
point = Point("violation_events") \
.tag("camera_id", violation.get("camera_id", "unknown")) \
.tag("roi_id", violation.get("roi_id", "unknown")) \
.tag("violation_category", violation.get("category", "unknown")) \
.field("violation_type", violation.get("type", "")) \
.field("severity_level", violation.get("severity", "")) \
.field("object_id", violation.get("object_id", "")) \
.field("confidence", float(violation.get("confidence", 0))) \
.time(datetime.utcnow(), WritePrecision.NS)
self.write_api.write(bucket=self.config.influxdb_bucket, record=point)
self.data_written.emit("violation_events", 1)
except Exception as e:
self.error_occurred.emit(f"Error writing violation event: {e}")
def write_traffic_flow(self, flow_data: Dict):
"""Write traffic flow data to InfluxDB"""
if not self.is_connected:
return
try:
point = Point("traffic_flow") \
.tag("lane_id", flow_data.get("lane_id", "unknown")) \
.tag("direction", flow_data.get("direction", "unknown")) \
.tag("camera_position", flow_data.get("camera_position", "unknown")) \
.field("vehicle_count", int(flow_data.get("vehicle_count", 0))) \
.field("average_speed", float(flow_data.get("average_speed", 0))) \
.field("lane_occupancy", float(flow_data.get("lane_occupancy", 0))) \
.field("congestion_level", flow_data.get("congestion_level", "")) \
.time(datetime.utcnow(), WritePrecision.NS)
self.write_api.write(bucket=self.config.influxdb_bucket, record=point)
self.data_written.emit("traffic_flow", 1)
except Exception as e:
self.error_occurred.emit(f"Error writing traffic flow: {e}")
def write_roi_event(self, roi_event: Dict):
"""Write ROI event to InfluxDB"""
if not self.is_connected:
return
try:
point = Point("roi_events") \
.tag("roi_id", roi_event.get("roi_id", "unknown")) \
.tag("roi_type", roi_event.get("roi_type", "unknown")) \
.tag("camera_id", roi_event.get("camera_id", "unknown")) \
.field("event_type", roi_event.get("event_type", "")) \
.field("dwell_time", float(roi_event.get("dwell_time", 0))) \
.field("object_count", int(roi_event.get("object_count", 0))) \
.field("activity_level", float(roi_event.get("activity_level", 0))) \
.time(datetime.utcnow(), WritePrecision.NS)
self.write_api.write(bucket=self.config.influxdb_bucket, record=point)
self.data_written.emit("roi_events", 1)
except Exception as e:
self.error_occurred.emit(f"Error writing ROI event: {e}")
class ServiceHealthMonitor(QObject):
"""Monitors health of MQTT and InfluxDB services"""
service_status_changed = Signal(str, bool) # service_name, is_healthy
all_services_healthy = Signal(bool)
def __init__(self, config: ServiceConfig):
super().__init__()
self.config = config
self.service_status = {
"mqtt": False,
"influxdb": False
}
# Setup monitoring timer
self.monitor_timer = QTimer()
self.monitor_timer.timeout.connect(self._check_services)
self.monitor_timer.start(config.service_check_interval * 1000)
def _check_services(self):
"""Check health of all services"""
self._check_mqtt()
self._check_influxdb()
# Emit overall health status
all_healthy = all(self.service_status.values())
self.all_services_healthy.emit(all_healthy)
def _check_mqtt(self):
"""Check MQTT broker health"""
try:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
result = sock.connect_ex((self.config.mqtt_broker, self.config.mqtt_port))
sock.close()
is_healthy = result == 0
if self.service_status["mqtt"] != is_healthy:
self.service_status["mqtt"] = is_healthy
self.service_status_changed.emit("mqtt", is_healthy)
except Exception:
if self.service_status["mqtt"]:
self.service_status["mqtt"] = False
self.service_status_changed.emit("mqtt", False)
def _check_influxdb(self):
"""Check InfluxDB health"""
try:
import requests
response = requests.get(f"{self.config.influxdb_url}/health", timeout=5)
is_healthy = response.status_code == 200
if self.service_status["influxdb"] != is_healthy:
self.service_status["influxdb"] = is_healthy
self.service_status_changed.emit("influxdb", is_healthy)
except Exception:
if self.service_status["influxdb"]:
self.service_status["influxdb"] = False
self.service_status_changed.emit("influxdb", False)
class EnhancedSmartIntersectionController(BaseController):
"""
Enhanced Smart Intersection Controller with MQTT + InfluxDB integration
Extends the base controller with hybrid desktop + services architecture
"""
# Additional signals for service integration
mqtt_connected = Signal()
mqtt_disconnected = Signal()
influxdb_connected = Signal()
influxdb_disconnected = Signal()
service_error = Signal(str, str) # service_name, error_message
services_healthy = Signal(bool)
def __init__(self, parent=None):
super().__init__(parent)
# Load service configuration
self.service_config = self._load_service_config()
# Initialize service components
self.mqtt_publisher = None
self.influxdb_writer = None
self.health_monitor = None
# Service integration enabled flag
self.services_enabled = True
# Initialize services
self._initialize_services()
print("🚀 Enhanced Smart Intersection Controller initialized with services integration")
def _load_service_config(self) -> ServiceConfig:
"""Load service configuration from file"""
try:
config_path = Path(__file__).parent.parent / "config" / "smart-intersection" / "services-config.json"
if config_path.exists():
with open(config_path, 'r') as f:
config_data = json.load(f)
return ServiceConfig(
mqtt_broker=config_data.get("mqtt", {}).get("broker", "localhost"),
mqtt_port=config_data.get("mqtt", {}).get("port", 1883),
influxdb_url=config_data.get("influxdb", {}).get("url", "http://localhost:8086"),
influxdb_token=config_data.get("influxdb", {}).get("token", "smart-intersection-super-secret-token"),
influxdb_org=config_data.get("influxdb", {}).get("org", "smart-intersection-org"),
influxdb_bucket=config_data.get("influxdb", {}).get("bucket", "traffic_monitoring")
)
except Exception as e:
print(f"Warning: Could not load service config: {e}")
# Return default configuration
return ServiceConfig()
def _initialize_services(self):
"""Initialize MQTT and InfluxDB services"""
if not self.services_enabled:
return
try:
# Initialize MQTT Publisher
self.mqtt_publisher = MQTTPublisher(self.service_config)
self.mqtt_publisher.connected.connect(self.mqtt_connected.emit)
self.mqtt_publisher.disconnected.connect(self.mqtt_disconnected.emit)
self.mqtt_publisher.error_occurred.connect(lambda err: self.service_error.emit("mqtt", err))
# Initialize InfluxDB Writer
self.influxdb_writer = InfluxDBWriter(self.service_config)
self.influxdb_writer.connected.connect(self.influxdb_connected.emit)
self.influxdb_writer.disconnected.connect(self.influxdb_disconnected.emit)
self.influxdb_writer.error_occurred.connect(lambda err: self.service_error.emit("influxdb", err))
# Initialize Health Monitor
self.health_monitor = ServiceHealthMonitor(self.service_config)
self.health_monitor.all_services_healthy.connect(self.services_healthy.emit)
# Connect to services
if self.mqtt_publisher:
self.mqtt_publisher.connect()
print("✅ Services integration initialized")
except Exception as e:
print(f"❌ Failed to initialize services: {e}")
self.services_enabled = False
def set_services_enabled(self, enabled: bool):
"""Enable or disable services integration"""
self.services_enabled = enabled
if enabled and not self.mqtt_publisher:
self._initialize_services()
elif not enabled:
self._disconnect_services()
def _disconnect_services(self):
"""Disconnect from all services"""
if self.mqtt_publisher:
self.mqtt_publisher.disconnect()
if self.influxdb_writer:
self.influxdb_writer.disconnect()
def process_frame(self, frame_data: Dict):
"""Override to add service integration"""
# Call parent method
result = super().process_frame(frame_data)
# Publish to services if enabled
if self.services_enabled and self.mqtt_publisher and self.mqtt_publisher.is_connected:
self._publish_frame_data(frame_data, result)
return result
def _publish_frame_data(self, frame_data: Dict, processing_result: Dict):
"""Publish frame processing data to services"""
try:
# Extract detection data
detections = frame_data.get('detections', [])
camera_id = frame_data.get('camera_id', 'unknown')
# Count objects by type
vehicle_count = len([d for d in detections if d.get('class_name') in ['car', 'truck', 'bus']])
pedestrian_count = len([d for d in detections if d.get('class_name') == 'person'])
# Calculate average confidence
confidences = [d.get('confidence', 0) for d in detections]
avg_confidence = sum(confidences) / len(confidences) if confidences else 0
# Detection event data
detection_data = {
"camera_id": camera_id,
"camera_position": frame_data.get('camera_position', 'unknown'),
"object_count": len(detections),
"vehicle_count": vehicle_count,
"pedestrian_count": pedestrian_count,
"confidence_avg": avg_confidence,
"processing_time": processing_result.get('processing_time_ms', 0),
"frame_number": self.frame_count
}
# Publish detection event
self.mqtt_publisher.publish_detection_event(detection_data)
# Write to InfluxDB
if self.influxdb_writer and self.influxdb_writer.is_connected:
self.influxdb_writer.write_detection_event(detection_data)
except Exception as e:
print(f"Error publishing frame data: {e}")
def _update_performance_stats(self):
"""Override to add service publishing"""
super()._update_performance_stats()
if not self.services_enabled:
return
try:
# Get current performance data
performance_data = self.analytics_data.get('performance', {})
performance_data.update({
"camera_count": len(self.scene_adapters),
"active_objects": self.analytics_data.get('total_objects', 0),
"timestamp": datetime.now().isoformat()
})
# Publish performance metrics
if self.mqtt_publisher and self.mqtt_publisher.is_connected:
self.mqtt_publisher.publish_performance_metrics(performance_data)
# Write to InfluxDB
if self.influxdb_writer and self.influxdb_writer.is_connected:
self.influxdb_writer.write_performance_metrics(performance_data)
except Exception as e:
print(f"Error updating performance stats: {e}")
def get_service_status(self) -> Dict:
"""Get status of all integrated services"""
status = {
"services_enabled": self.services_enabled,
"mqtt": {
"connected": self.mqtt_publisher.is_connected if self.mqtt_publisher else False,
"broker": self.service_config.mqtt_broker,
"port": self.service_config.mqtt_port
},
"influxdb": {
"connected": self.influxdb_writer.is_connected if self.influxdb_writer else False,
"url": self.service_config.influxdb_url,
"bucket": self.service_config.influxdb_bucket
},
"health_monitor": {
"active": self.health_monitor is not None
}
}
return status
def publish_violation_event(self, violation_data: Dict):
"""Publish traffic violation event"""
if self.services_enabled and self.mqtt_publisher and self.mqtt_publisher.is_connected:
self.mqtt_publisher.publish_violation_event(violation_data)
if self.services_enabled and self.influxdb_writer and self.influxdb_writer.is_connected:
self.influxdb_writer.write_violation_event(violation_data)
def publish_traffic_flow_data(self, flow_data: Dict):
"""Publish traffic flow analytics"""
if self.services_enabled and self.mqtt_publisher and self.mqtt_publisher.is_connected:
self.mqtt_publisher.publish_traffic_flow(flow_data)
if self.services_enabled and self.influxdb_writer and self.influxdb_writer.is_connected:
self.influxdb_writer.write_traffic_flow(flow_data)
def publish_pedestrian_safety_event(self, safety_data: Dict):
"""Publish pedestrian safety event"""
if self.services_enabled and self.mqtt_publisher and self.mqtt_publisher.is_connected:
self.mqtt_publisher.publish_pedestrian_safety(safety_data)
def publish_roi_event(self, roi_data: Dict):
"""Publish ROI-based event"""
if self.services_enabled and self.mqtt_publisher and self.mqtt_publisher.is_connected:
self.mqtt_publisher.publish_roi_event(roi_data)
if self.services_enabled and self.influxdb_writer and self.influxdb_writer.is_connected:
self.influxdb_writer.write_roi_event(roi_data)
def shutdown_services(self):
"""Gracefully shutdown all services"""
print("🛑 Shutting down services integration...")
if self.mqtt_publisher:
self.mqtt_publisher.disconnect()
if self.influxdb_writer:
self.influxdb_writer.disconnect()
if self.health_monitor:
self.health_monitor.monitor_timer.stop()
print("✅ Services integration shut down successfully")