Files
Traffic-Intersection-Monito…/qt_app_pyside1/services/scripts/setup_services.py
2025-08-26 13:24:53 -07:00

473 lines
18 KiB
Python

#!/usr/bin/env python3
"""
Smart Intersection Services Setup Script
Automated setup for MQTT + InfluxDB + Grafana integration
"""
import os
import sys
import json
import time
import zipfile
import subprocess
import requests
from pathlib import Path
from urllib.request import urlretrieve
class ServicesSetup:
"""Automated setup for Smart Intersection services"""
def __init__(self):
self.base_dir = Path(__file__).parent.parent
self.services_dir = self.base_dir / "services"
self.downloads_dir = self.services_dir / "downloads"
self.install_dir = Path("C:/SmartIntersectionServices")
# Service configurations
self.services = {
"mosquitto": {
"name": "Eclipse Mosquitto MQTT Broker",
"url": "https://mosquitto.org/files/binary/win64/mosquitto-2.0.18-install-windows-x64.exe",
"filename": "mosquitto-2.0.18-install-windows-x64.exe",
"install_dir": "C:/Program Files/mosquitto",
"port": 1883,
"check_url": None
},
"influxdb": {
"name": "InfluxDB v2",
"url": "https://github.com/influxdata/influxdb/releases/download/v2.7.11/influxdb2-2.7.11-windows-amd64.zip",
"filename": "influxdb2-2.7.11-windows-amd64.zip",
"install_dir": Path("C:/Users/devcloud/Desktop/Clean/clean-final-push/qt_app_pyside1/services/services/downloads/influxdb2-2.7.11-windows"),
"port": 8086,
"check_url": "http://localhost:8086/health"
},
"grafana": {
"name": "Grafana",
"url": "https://dl.grafana.com/oss/release/grafana-10.2.2.windows-amd64.zip",
"filename": "grafana-10.2.2.windows-amd64.zip",
"install_dir": Path("C:/Users/devcloud/Desktop/Clean/clean-final-push/qt_app_pyside1/services/services/downloads/grafana-10.2.2.windows-amd64"),
"port": 3000,
"check_url": "http://localhost:3000/api/health"
}
}
def run_setup(self):
"""Run the complete setup process"""
print("=" * 70)
print("🚦 SMART INTERSECTION SERVICES SETUP")
print("=" * 70)
print()
# Check if running as administrator (for Mosquitto installation)
if not self._is_admin():
print("⚠️ Administrator privileges recommended for complete setup")
print(" Some services may require manual installation")
print()
# Create directories
self._create_directories()
# Check Docker availability
docker_available = self._check_docker()
if docker_available:
print("✅ Docker is available!")
print(" Recommended: Use Docker Compose for easy setup")
print(" Alternative: Manual installation")
print()
choice = input("Use Docker Compose setup? (y/n) [y]: ").strip().lower()
if choice in ['', 'y', 'yes']:
self._setup_docker_compose()
return
print("🔧 Proceeding with manual installation...")
print()
# Install Python dependencies
self._install_python_dependencies()
# Setup each service
for service_key, service_config in self.services.items():
print(f"📦 Setting up {service_config['name']}...")
self._setup_service(service_key, service_config)
print()
# Configure services
self._configure_services()
# Test services
self._test_services()
# Create startup scripts
self._create_startup_scripts()
# Final instructions
self._show_final_instructions()
def _is_admin(self):
"""Check if running with administrator privileges"""
try:
import ctypes
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def _create_directories(self):
"""Create necessary directories"""
print("📁 Creating directories...")
directories = [
self.downloads_dir,
self.install_dir,
self.install_dir / "influxdb" / "data",
self.install_dir / "grafana" / "data",
self.services_dir / "logs"
]
for directory in directories:
directory.mkdir(parents=True, exist_ok=True)
print(f"{directory}")
print()
def _check_docker(self):
"""Check if Docker is available"""
try:
result = subprocess.run(
["docker", "--version"],
capture_output=True,
text=True,
timeout=10
)
return result.returncode == 0
except:
return False
def _install_python_dependencies(self):
"""Install required Python packages"""
print("🐍 Installing Python dependencies...")
packages = [
"paho-mqtt==1.6.1",
"influxdb-client==1.43.0",
"grafana-api==1.0.3",
"aiohttp==3.9.1",
"asyncio-mqtt==0.13.0",
"requests>=2.28.0"
]
for package in packages:
try:
print(f" Installing {package}...")
result = subprocess.run(
[sys.executable, "-m", "pip", "install", package],
capture_output=True,
text=True
)
if result.returncode == 0:
print(f"{package}")
else:
print(f" ⚠️ {package} - {result.stderr.strip()}")
except Exception as e:
print(f" ❌ Failed to install {package}: {e}")
print()
def _setup_service(self, service_key: str, config: dict):
"""Setup individual service"""
filename = config["filename"]
download_path = self.downloads_dir / filename
# Download if not exists
if not download_path.exists():
print(f" 📥 Downloading {config['name']}...")
try:
self._download_with_progress(config["url"], download_path)
print(f" ✅ Downloaded to {download_path}")
except Exception as e:
print(f" ❌ Download failed: {e}")
print(f" 📌 Manual download: {config['url']}")
return
else:
print(f" ✅ Already downloaded: {filename}")
# Install/Extract service
if service_key == "mosquitto":
self._install_mosquitto(download_path)
elif filename.endswith(".zip"):
self._extract_service(download_path, config["install_dir"])
def _download_with_progress(self, url: str, filepath: Path):
"""Download file with progress indicator"""
def progress_hook(block_num, block_size, total_size):
if total_size > 0:
percent = min(100, (block_num * block_size * 100) // total_size)
print(f"\r Progress: {percent}%", end="", flush=True)
urlretrieve(url, filepath, progress_hook)
print() # New line after progress
def _install_mosquitto(self, installer_path: Path):
"""Install Mosquitto MQTT broker"""
print(f" 🔧 Installing Mosquitto...")
print(f" 📌 Please run as administrator: {installer_path}")
print(f" 📌 Install to default location: C:\\Program Files\\mosquitto\\")
# Attempt automatic installation if admin
if self._is_admin():
try:
result = subprocess.run(
[str(installer_path), "/S"], # Silent install
timeout=300
)
if result.returncode == 0:
print(f" ✅ Mosquitto installed successfully")
else:
print(f" ⚠️ Installation may require manual confirmation")
except Exception as e:
print(f" ⚠️ Automatic installation failed: {e}")
print(f" 📌 Please run manually: {installer_path}")
else:
print(f" 📌 Run manually as administrator: {installer_path}")
def _extract_service(self, zip_path: Path, install_dir: Path):
"""Extract service from zip file"""
print(f" 📦 Extracting to {install_dir}...")
try:
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
# Extract to temporary directory first
temp_dir = install_dir.parent / f"temp_{install_dir.name}"
zip_ref.extractall(temp_dir)
# Find the main directory in extracted files
extracted_items = list(temp_dir.iterdir())
if len(extracted_items) == 1 and extracted_items[0].is_dir():
# Move contents from subdirectory
source_dir = extracted_items[0]
install_dir.mkdir(parents=True, exist_ok=True)
for item in source_dir.iterdir():
target = install_dir / item.name
if item.is_dir():
import shutil
shutil.copytree(item, target, dirs_exist_ok=True)
else:
import shutil
shutil.copy2(item, target)
# Clean up temporary directory
import shutil
shutil.rmtree(temp_dir)
else:
# Move entire temp directory
import shutil
if install_dir.exists():
shutil.rmtree(install_dir)
shutil.move(temp_dir, install_dir)
print(f" ✅ Extracted successfully")
except Exception as e:
print(f" ❌ Extraction failed: {e}")
def _configure_services(self):
"""Configure all services"""
print("⚙️ Configuring services...")
# Copy configuration files
config_mappings = [
(self.base_dir / "mqtt" / "mosquitto.conf",
Path("C:/Program Files/mosquitto/mosquitto.conf")),
(self.base_dir / "influxdb" / "config.yml",
Path("C:/Users/devcloud/Desktop/Clean/clean-final-push/qt_app_pyside1/services/services/downloads/influxdb2-2.7.11-windows/config.yml")),
(self.base_dir / "grafana" / "datasources" / "influxdb.yml",
Path("C:/Users/devcloud/Desktop/Clean/clean-final-push/qt_app_pyside1/services/services/downloads/grafana-10.2.2.windows-amd64/conf/provisioning/datasources/influxdb.yml"))
]
for source, target in config_mappings:
if source.exists():
try:
target.parent.mkdir(parents=True, exist_ok=True)
import shutil
shutil.copy2(source, target)
print(f" ✅ Configured {target.name}")
except Exception as e:
print(f" ⚠️ Could not copy {source.name}: {e}")
else:
print(f" ⚠️ Source config not found: {source}")
print()
def _setup_docker_compose(self):
"""Setup using Docker Compose"""
print("🐳 Setting up with Docker Compose...")
compose_file = self.services_dir / "docker" / "docker-compose.yml"
if not compose_file.exists():
print("❌ Docker Compose file not found!")
return
try:
# Change to docker directory
docker_dir = self.services_dir / "docker"
os.chdir(docker_dir)
# Pull images
print(" 📥 Pulling Docker images...")
subprocess.run(["docker-compose", "pull"], check=True)
# Start services
print(" 🚀 Starting services...")
subprocess.run(["docker-compose", "up", "-d"], check=True)
print(" ✅ Docker services started successfully!")
print()
# Wait for services to be ready
print(" ⏳ Waiting for services to initialize...")
time.sleep(30)
# Test services
self._test_docker_services()
except subprocess.CalledProcessError as e:
print(f" ❌ Docker Compose failed: {e}")
except Exception as e:
print(f" ❌ Setup failed: {e}")
def _test_services(self):
"""Test if services are running"""
print("🧪 Testing services...")
for service_key, config in self.services.items():
port = config["port"]
check_url = config.get("check_url")
print(f" Testing {config['name']} (port {port})...")
# Test port
if self._test_port(port):
print(f" ✅ Port {port} is open")
# Test HTTP endpoint if available
if check_url:
if self._test_http(check_url):
print(f" ✅ HTTP endpoint responding")
else:
print(f" ⚠️ HTTP endpoint not ready yet")
else:
print(f" ❌ Port {port} not responding")
print(f" 📌 Make sure {config['name']} is running")
print()
def _test_docker_services(self):
"""Test Docker services"""
print(" 🧪 Testing Docker services...")
services_to_test = [
("MQTT Broker", 1883, None),
("InfluxDB", 8086, "http://localhost:8086/health"),
("Grafana", 3000, "http://localhost:3000/api/health")
]
for name, port, url in services_to_test:
print(f" Testing {name}...")
if self._test_port(port):
print(f"{name} port {port} is open")
if url and self._test_http(url):
print(f"{name} HTTP endpoint responding")
else:
print(f"{name} port {port} not responding")
def _test_port(self, port: int) -> bool:
"""Test if port is open"""
try:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
result = sock.connect_ex(("localhost", port))
sock.close()
return result == 0
except:
return False
def _test_http(self, url: str) -> bool:
"""Test HTTP endpoint"""
try:
response = requests.get(url, timeout=10)
return response.status_code in [200, 204]
except:
return False
def _create_startup_scripts(self):
"""Create startup scripts"""
print("📝 Creating startup scripts...")
# Scripts are already created in the setup
scripts = [
self.services_dir / "scripts" / "start_services.bat",
self.services_dir / "scripts" / "stop_services.bat"
]
for script in scripts:
if script.exists():
print(f"{script.name}")
else:
print(f" ⚠️ {script.name} not found")
print()
def _show_final_instructions(self):
"""Show final setup instructions"""
print("🎉 SETUP COMPLETE!")
print("=" * 50)
print()
print("📋 Next Steps:")
print("1. Start services:")
print(f" Run: {self.services_dir}/scripts/start_services.bat")
print()
print("2. Access services:")
print(" 🌐 Grafana: http://localhost:3000 (admin/admin)")
print(" 📊 InfluxDB: http://localhost:8086")
print(" 📡 MQTT: localhost:1883")
print()
print("3. Run desktop application:")
print(" cd qt_app_pyside1")
print(" python main.py")
print()
print("4. Service status:")
print(" Desktop app will show service connection status")
print(" Green indicators = services connected")
print()
print("🔧 Configuration:")
print(f" Services config: {self.base_dir}/config/smart-intersection/services-config.json")
print(f" MQTT topics: {self.services_dir}/mqtt/topics.json")
print()
print("📊 Grafana Dashboards:")
print(" Pre-configured dashboards will load automatically")
print(" View real-time traffic analytics and performance metrics")
print()
print("💡 Tips:")
print(" - Services may take 30-60 seconds to fully initialize")
print(" - Check Windows Firewall if connections fail")
print(" - Use Docker Compose for easier management")
print(" - Monitor logs in services/logs/ directory")
print()
def main():
"""Main setup function"""
try:
setup = ServicesSetup()
setup.run_setup()
except KeyboardInterrupt:
print("\n⚠️ Setup interrupted by user")
except Exception as e:
print(f"\n❌ Setup failed with error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()