cleanup and files added
This commit is contained in:
54
qt_app_pyside1/services/scripts/fix_setup.bat
Normal file
54
qt_app_pyside1/services/scripts/fix_setup.bat
Normal file
@@ -0,0 +1,54 @@
|
||||
@echo off
|
||||
echo ======================================================================
|
||||
echo 🔧 SMART INTERSECTION SERVICES - QUICK FIX
|
||||
echo ======================================================================
|
||||
|
||||
echo.
|
||||
echo 🛠️ Fixing setup issues...
|
||||
|
||||
echo.
|
||||
echo 1️⃣ Installing Mosquitto MQTT Broker...
|
||||
echo 📌 Please install manually as administrator:
|
||||
echo 📂 Location: C:\Users\devcloud\Desktop\Clean\clean-final-push\qt_app_pyside1\services\services\downloads\mosquitto-2.0.18-install-windows-x64.exe
|
||||
echo 📍 Install to: C:\Program Files\mosquitto\
|
||||
echo.
|
||||
echo Or run this command as administrator:
|
||||
echo C:\Users\devcloud\Desktop\Clean\clean-final-push\qt_app_pyside1\services\services\downloads\mosquitto-2.0.18-install-windows-x64.exe /S
|
||||
echo.
|
||||
|
||||
echo 2️⃣ Manual InfluxDB Setup Required:
|
||||
echo 🌐 Download from: https://github.com/influxdata/influxdb/releases/latest
|
||||
echo 📁 Extract to: C:\SmartIntersectionServices\influxdb\
|
||||
echo 💡 Look for file like: influxdb2-2.7.11-windows-amd64.zip
|
||||
echo.
|
||||
|
||||
echo 3️⃣ Grafana is ready! ✅
|
||||
echo 📂 Already extracted to: C:\SmartIntersectionServices\grafana\
|
||||
echo.
|
||||
|
||||
echo 4️⃣ After manual installs, run:
|
||||
echo 📜 start_services.bat
|
||||
echo.
|
||||
|
||||
echo ======================================================================
|
||||
echo 📋 MANUAL INSTALLATION STEPS:
|
||||
echo ======================================================================
|
||||
echo.
|
||||
echo Step 1: Install Mosquitto
|
||||
echo • Right-click PowerShell/CMD and "Run as administrator"
|
||||
echo • Run: C:\Users\devcloud\Desktop\Clean\clean-final-push\qt_app_pyside1\services\services\downloads\mosquitto-2.0.18-install-windows-x64.exe
|
||||
echo • Use default installation path: C:\Program Files\mosquitto\
|
||||
echo.
|
||||
echo Step 2: Install InfluxDB
|
||||
echo • Download: https://github.com/influxdata/influxdb/releases/download/v2.7.11/influxdb2-2.7.11-windows-amd64.zip
|
||||
echo • Extract to: C:\SmartIntersectionServices\influxdb\
|
||||
echo • Should contain: influxd.exe and other files
|
||||
echo.
|
||||
echo Step 3: Test Services
|
||||
echo • Run: start_services.bat
|
||||
echo • Check: http://localhost:3000 (Grafana)
|
||||
echo • Check: http://localhost:8086 (InfluxDB)
|
||||
echo • MQTT: localhost:1883
|
||||
echo.
|
||||
|
||||
pause
|
||||
139
qt_app_pyside1/services/scripts/install_services.ps1
Normal file
139
qt_app_pyside1/services/scripts/install_services.ps1
Normal file
@@ -0,0 +1,139 @@
|
||||
# Smart Intersection Services - Automated Installation
|
||||
# Run this as Administrator for best results
|
||||
|
||||
Write-Host "======================================================================" -ForegroundColor Cyan
|
||||
Write-Host "🚦 SMART INTERSECTION SERVICES - AUTOMATED INSTALLATION" -ForegroundColor Cyan
|
||||
Write-Host "======================================================================" -ForegroundColor Cyan
|
||||
|
||||
# Check if running as administrator
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
|
||||
|
||||
if (-not $isAdmin) {
|
||||
Write-Host "⚠️ Warning: Not running as administrator" -ForegroundColor Yellow
|
||||
Write-Host " Some installations may fail" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Paths
|
||||
$servicesDir = "C:\Users\devcloud\Desktop\Clean\clean-final-push\qt_app_pyside1\services"
|
||||
$downloadsDir = "$servicesDir\services\downloads"
|
||||
$installDir = "C:\SmartIntersectionServices"
|
||||
|
||||
Write-Host "🛠️ Starting automated installation..." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
# 1. Install Mosquitto MQTT Broker
|
||||
Write-Host "1️⃣ Installing Mosquitto MQTT Broker..." -ForegroundColor White
|
||||
$mosquittoInstaller = "$downloadsDir\mosquitto-2.0.18-install-windows-x64.exe"
|
||||
|
||||
if (Test-Path $mosquittoInstaller) {
|
||||
Write-Host " 📦 Found installer: $mosquittoInstaller" -ForegroundColor Green
|
||||
try {
|
||||
if ($isAdmin) {
|
||||
Write-Host " 🔧 Installing Mosquitto silently..." -ForegroundColor Yellow
|
||||
Start-Process -FilePath $mosquittoInstaller -ArgumentList "/S" -Wait -NoNewWindow
|
||||
Write-Host " ✅ Mosquitto installed successfully!" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " 📌 Starting installer (requires manual approval)..." -ForegroundColor Yellow
|
||||
Start-Process -FilePath $mosquittoInstaller -Wait
|
||||
}
|
||||
} catch {
|
||||
Write-Host " ❌ Installation failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host " 📌 Please run installer manually: $mosquittoInstaller" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host " ❌ Installer not found: $mosquittoInstaller" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# 2. Download and install InfluxDB
|
||||
Write-Host "2️⃣ Installing InfluxDB..." -ForegroundColor White
|
||||
$influxDir = "$installDir\influxdb"
|
||||
$influxExe = "$influxDir\influxd.exe"
|
||||
|
||||
if (-not (Test-Path $influxExe)) {
|
||||
Write-Host " 📥 Downloading InfluxDB v2.7.11..." -ForegroundColor Yellow
|
||||
$influxUrl = "https://github.com/influxdata/influxdb/releases/download/v2.7.11/influxdb2-2.7.11-windows-amd64.zip"
|
||||
$influxZip = "$downloadsDir\influxdb2-2.7.11-windows-amd64.zip"
|
||||
|
||||
try {
|
||||
# Download InfluxDB
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.DownloadFile($influxUrl, $influxZip)
|
||||
Write-Host " ✅ Downloaded InfluxDB" -ForegroundColor Green
|
||||
|
||||
# Extract InfluxDB
|
||||
Write-Host " 📦 Extracting InfluxDB..." -ForegroundColor Yellow
|
||||
if (-not (Test-Path $influxDir)) {
|
||||
New-Item -ItemType Directory -Path $influxDir -Force | Out-Null
|
||||
}
|
||||
Expand-Archive -Path $influxZip -DestinationPath $influxDir -Force
|
||||
|
||||
# Move files from subdirectory if needed
|
||||
$subDir = Get-ChildItem -Path $influxDir -Directory | Select-Object -First 1
|
||||
if ($subDir) {
|
||||
Get-ChildItem -Path $subDir.FullName -Recurse | Move-Item -Destination $influxDir -Force
|
||||
Remove-Item -Path $subDir.FullName -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Host " ✅ InfluxDB extracted to: $influxDir" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " ❌ InfluxDB installation failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host " 📌 Please download manually from: $influxUrl" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host " ✅ InfluxDB already installed: $influxExe" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# 3. Check Grafana
|
||||
Write-Host "3️⃣ Checking Grafana..." -ForegroundColor White
|
||||
$grafanaDir = "$installDir\grafana"
|
||||
$grafanaExe = "$grafanaDir\bin\grafana-server.exe"
|
||||
|
||||
if (Test-Path $grafanaExe) {
|
||||
Write-Host " ✅ Grafana ready: $grafanaExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ Grafana not found: $grafanaExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# 4. Test installations
|
||||
Write-Host "4️⃣ Testing installations..." -ForegroundColor White
|
||||
|
||||
$mosquittoExe = "C:\Program Files\mosquitto\mosquitto.exe"
|
||||
if (Test-Path $mosquittoExe) {
|
||||
Write-Host " ✅ Mosquitto: $mosquittoExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ Mosquitto not found: $mosquittoExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if (Test-Path $influxExe) {
|
||||
Write-Host " ✅ InfluxDB: $influxExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ InfluxDB not found: $influxExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if (Test-Path $grafanaExe) {
|
||||
Write-Host " ✅ Grafana: $grafanaExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ Grafana not found: $grafanaExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "======================================================================" -ForegroundColor Cyan
|
||||
Write-Host "🎉 Installation Complete!" -ForegroundColor Green
|
||||
Write-Host "======================================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "📋 Next Steps:" -ForegroundColor White
|
||||
Write-Host " 1. Run: start_services.bat" -ForegroundColor Yellow
|
||||
Write-Host " 2. Open: http://localhost:3000 (Grafana - admin/admin)" -ForegroundColor Yellow
|
||||
Write-Host " 3. Open: http://localhost:8086 (InfluxDB)" -ForegroundColor Yellow
|
||||
Write-Host " 4. MQTT available at: localhost:1883" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "💡 Services may take 30-60 seconds to fully start" -ForegroundColor Cyan
|
||||
|
||||
Read-Host "Press Enter to continue..."
|
||||
179
qt_app_pyside1/services/scripts/install_services_fixed.ps1
Normal file
179
qt_app_pyside1/services/scripts/install_services_fixed.ps1
Normal file
@@ -0,0 +1,179 @@
|
||||
# Smart Intersection Services - Simple Installation Script
|
||||
# Run this as Administrator for best results
|
||||
|
||||
Write-Host "======================================================================"
|
||||
Write-Host "🚦 SMART INTERSECTION SERVICES - AUTOMATED INSTALLATION"
|
||||
Write-Host "======================================================================"
|
||||
|
||||
# Check if running as administrator
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
|
||||
|
||||
if (-not $isAdmin) {
|
||||
Write-Host "⚠️ Warning: Not running as administrator" -ForegroundColor Yellow
|
||||
Write-Host " Some installations may fail" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Paths
|
||||
$servicesDir = "C:\Users\devcloud\Desktop\Clean\clean-final-push\qt_app_pyside1\services"
|
||||
$downloadsDir = "$servicesDir\services\downloads"
|
||||
$installDir = "C:\SmartIntersectionServices"
|
||||
|
||||
Write-Host "🛠️ Starting automated installation..." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
# 1. Install Mosquitto MQTT Broker
|
||||
Write-Host "1️⃣ Installing Mosquitto MQTT Broker..." -ForegroundColor White
|
||||
$mosquittoInstaller = "$downloadsDir\mosquitto-2.0.18-install-windows-x64.exe"
|
||||
|
||||
if (Test-Path $mosquittoInstaller) {
|
||||
Write-Host " 📦 Found installer: $mosquittoInstaller" -ForegroundColor Green
|
||||
if ($isAdmin) {
|
||||
Write-Host " 🔧 Installing Mosquitto silently..." -ForegroundColor Yellow
|
||||
try {
|
||||
# Try multiple silent install arguments
|
||||
$silentArgs = @("/S", "/silent", "/verysilent")
|
||||
$installed = $false
|
||||
|
||||
foreach ($arg in $silentArgs) {
|
||||
try {
|
||||
Start-Process -FilePath $mosquittoInstaller -ArgumentList $arg -Wait -NoNewWindow -ErrorAction Stop
|
||||
Write-Host " ✅ Mosquitto installed successfully with $arg!" -ForegroundColor Green
|
||||
$installed = $true
|
||||
break
|
||||
} catch {
|
||||
Write-Host " ⚠️ Silent install with $arg failed, trying next..." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $installed) {
|
||||
throw "All silent install methods failed"
|
||||
}
|
||||
} catch {
|
||||
Write-Host " ❌ Installation failed" -ForegroundColor Red
|
||||
Write-Host " 📌 Please run installer manually: $mosquittoInstaller" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host " 📌 Starting installer (requires manual approval)..." -ForegroundColor Yellow
|
||||
Start-Process -FilePath $mosquittoInstaller -Wait
|
||||
}
|
||||
} else {
|
||||
Write-Host " ❌ Installer not found: $mosquittoInstaller" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# 2. Download and install InfluxDB
|
||||
Write-Host "2️⃣ Installing InfluxDB..." -ForegroundColor White
|
||||
$influxDir = "$installDir\influxdb"
|
||||
$influxExe = "$influxDir\influxd.exe"
|
||||
|
||||
if (-not (Test-Path $influxExe)) {
|
||||
Write-Host " 📥 Downloading InfluxDB v2.7.11..." -ForegroundColor Yellow
|
||||
$influxUrl = "https://github.com/influxdata/influxdb/releases/download/v2.7.11/influxdb2-2.7.11-windows-amd64.zip"
|
||||
$influxZip = "$downloadsDir\influxdb2-2.7.11-windows-amd64.zip"
|
||||
|
||||
try {
|
||||
# Download InfluxDB using modern method
|
||||
Invoke-WebRequest -Uri $influxUrl -OutFile $influxZip -ErrorAction Stop
|
||||
Write-Host " ✅ Downloaded InfluxDB" -ForegroundColor Green
|
||||
|
||||
# Extract InfluxDB
|
||||
Write-Host " 📦 Extracting InfluxDB..." -ForegroundColor Yellow
|
||||
if (-not (Test-Path $influxDir)) {
|
||||
New-Item -ItemType Directory -Path $influxDir -Force | Out-Null
|
||||
}
|
||||
Expand-Archive -Path $influxZip -DestinationPath $influxDir -Force -ErrorAction Stop
|
||||
|
||||
# Move files from subdirectory if needed
|
||||
$subDirs = Get-ChildItem -Path $influxDir -Directory
|
||||
if ($subDirs.Count -gt 0) {
|
||||
$subDir = $subDirs[0]
|
||||
Get-ChildItem -Path $subDir.FullName -Recurse | Move-Item -Destination $influxDir -Force
|
||||
Remove-Item -Path $subDir.FullName -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Host " ✅ InfluxDB extracted to: $influxDir" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " ❌ InfluxDB installation failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host " 📌 Please download manually from: $influxUrl" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host " ✅ InfluxDB already installed: $influxExe" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# 3. Check and setup Grafana
|
||||
Write-Host "3️⃣ Setting up Grafana..." -ForegroundColor White
|
||||
$grafanaDir = "$installDir\grafana"
|
||||
$grafanaExe = "$grafanaDir\bin\grafana-server.exe"
|
||||
$grafanaZip = "$downloadsDir\grafana-10.2.2.windows-amd64.zip"
|
||||
|
||||
if (-not (Test-Path $grafanaExe)) {
|
||||
if (Test-Path $grafanaZip) {
|
||||
Write-Host " 📦 Extracting Grafana..." -ForegroundColor Yellow
|
||||
try {
|
||||
if (-not (Test-Path $grafanaDir)) {
|
||||
New-Item -ItemType Directory -Path $grafanaDir -Force | Out-Null
|
||||
}
|
||||
Expand-Archive -Path $grafanaZip -DestinationPath $grafanaDir -Force -ErrorAction Stop
|
||||
|
||||
# Move files from subdirectory if needed
|
||||
$subDirs = Get-ChildItem -Path $grafanaDir -Directory
|
||||
if ($subDirs.Count -gt 0) {
|
||||
$subDir = $subDirs[0]
|
||||
Get-ChildItem -Path $subDir.FullName -Recurse | Move-Item -Destination $grafanaDir -Force
|
||||
Remove-Item -Path $subDir.FullName -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Host " ✅ Grafana extracted to: $grafanaDir" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " ❌ Grafana extraction failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
}
|
||||
} else {
|
||||
Write-Host " ❌ Grafana zip not found: $grafanaZip" -ForegroundColor Red
|
||||
Write-Host " 📌 Download from: https://grafana.com/grafana/download?platform=windows" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host " ✅ Grafana already ready: $grafanaExe" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# 4. Test installations
|
||||
Write-Host "4️⃣ Testing installations..." -ForegroundColor White
|
||||
|
||||
$mosquittoExe = "C:\Program Files\mosquitto\mosquitto.exe"
|
||||
if (Test-Path $mosquittoExe) {
|
||||
Write-Host " ✅ Mosquitto: $mosquittoExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ Mosquitto not found: $mosquittoExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if (Test-Path $influxExe) {
|
||||
Write-Host " ✅ InfluxDB: $influxExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ InfluxDB not found: $influxExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if (Test-Path $grafanaExe) {
|
||||
Write-Host " ✅ Grafana: $grafanaExe" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ❌ Grafana not found: $grafanaExe" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "======================================================================"
|
||||
Write-Host "🎉 Installation Complete!" -ForegroundColor Green
|
||||
Write-Host "======================================================================"
|
||||
Write-Host ""
|
||||
Write-Host "📋 Next Steps:" -ForegroundColor White
|
||||
Write-Host " 1. Run: start_services.bat" -ForegroundColor Yellow
|
||||
Write-Host " 2. Open: http://localhost:3000 (Grafana - admin/admin)" -ForegroundColor Yellow
|
||||
Write-Host " 3. Open: http://localhost:8086 (InfluxDB)" -ForegroundColor Yellow
|
||||
Write-Host " 4. MQTT available at: localhost:1883" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "💡 Services may take 30-60 seconds to fully start" -ForegroundColor Cyan
|
||||
|
||||
Read-Host "Press Enter to continue..."
|
||||
472
qt_app_pyside1/services/scripts/setup_services.py
Normal file
472
qt_app_pyside1/services/scripts/setup_services.py
Normal file
@@ -0,0 +1,472 @@
|
||||
#!/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()
|
||||
114
qt_app_pyside1/services/scripts/start_services.bat
Normal file
114
qt_app_pyside1/services/scripts/start_services.bat
Normal file
@@ -0,0 +1,114 @@
|
||||
@echo off
|
||||
REM Smart Intersection Services Startup Script
|
||||
REM Starts all required services for hybrid desktop + services architecture
|
||||
|
||||
echo ===============================================
|
||||
echo Smart Intersection Services Startup
|
||||
echo ===============================================
|
||||
|
||||
REM Check if Docker is available
|
||||
docker --version >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo ✅ Docker is available - starting services via Docker Compose
|
||||
cd /d "%~dp0..\docker"
|
||||
docker-compose up -d
|
||||
echo.
|
||||
echo Services starting in Docker containers:
|
||||
echo - MQTT Broker: localhost:1883
|
||||
echo - InfluxDB: localhost:8086
|
||||
echo - Grafana: localhost:3000
|
||||
echo.
|
||||
echo Wait 30 seconds for services to initialize...
|
||||
timeout /t 30 /nobreak
|
||||
echo.
|
||||
echo ✅ Services should now be ready!
|
||||
echo 🌐 Open Grafana: http://localhost:3000 (admin/admin)
|
||||
echo 📊 InfluxDB UI: http://localhost:8086
|
||||
goto :end
|
||||
)
|
||||
|
||||
echo ⚠️ Docker not found - attempting standalone service startup
|
||||
echo.
|
||||
|
||||
REM Start Mosquitto MQTT Broker
|
||||
echo Starting Mosquitto MQTT Broker...
|
||||
if exist "C:\Program Files\mosquitto\mosquitto.exe" (
|
||||
start "Mosquitto MQTT" "C:\Program Files\mosquitto\mosquitto.exe" -c "%~dp0..\mqtt\mosquitto.conf"
|
||||
echo ✅ Mosquitto MQTT started on port 1883
|
||||
) else (
|
||||
echo ❌ Mosquitto not found at C:\Program Files\mosquitto\
|
||||
echo Please install Mosquitto from: https://mosquitto.org/download/
|
||||
)
|
||||
|
||||
REM Start InfluxDB
|
||||
echo Starting InfluxDB...
|
||||
|
||||
REM Use extracted InfluxDB in downloads folder
|
||||
if exist "C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\influxdb2-2.7.11-windows\influxd.exe" (
|
||||
start "InfluxDB" "C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\influxdb2-2.7.11-windows\influxd.exe"
|
||||
echo ✅ InfluxDB started on port 8086
|
||||
) else (
|
||||
echo ❌ InfluxDB not found in downloads folder
|
||||
echo Please extract InfluxDB to: C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\influxdb2-2.7.11-windows\
|
||||
)
|
||||
|
||||
REM Start Grafana
|
||||
echo Starting Grafana...
|
||||
|
||||
REM Use extracted Grafana in downloads folder
|
||||
if exist "C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\grafana-10.2.2.windows-amd64\grafana-v10.2.2\bin\grafana-server.exe" (
|
||||
start "Grafana" "C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\grafana-10.2.2.windows-amd64\grafana-v10.2.2\bin\grafana-server.exe" --homepath="C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\grafana-10.2.2.windows-amd64\grafana-v10.2.2"
|
||||
echo ✅ Grafana started on port 3000
|
||||
) else (
|
||||
echo ❌ Grafana not found in downloads folder
|
||||
echo Please extract Grafana to: C:\Users\devcloud\Desktop\Qt\clean-final-push\qt_app_pyside1\services\services\downloads\grafana-10.2.2.windows-amd64\grafana-v10.2.2\
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ⏳ Waiting 15 seconds for services to initialize...
|
||||
timeout /t 15 /nobreak
|
||||
|
||||
echo.
|
||||
echo ===============================================
|
||||
echo Service Status Check
|
||||
echo ===============================================
|
||||
|
||||
REM Check service ports
|
||||
echo Checking MQTT Broker (port 1883)...
|
||||
netstat -an | findstr :1883 >nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo ✅ MQTT Broker is listening on port 1883
|
||||
) else (
|
||||
echo ❌ MQTT Broker not responding on port 1883
|
||||
)
|
||||
|
||||
echo Checking InfluxDB (port 8086)...
|
||||
netstat -an | findstr :8086 >nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo ✅ InfluxDB is listening on port 8086
|
||||
) else (
|
||||
echo ❌ InfluxDB not responding on port 8086
|
||||
)
|
||||
|
||||
echo Checking Grafana (port 3000)...
|
||||
netstat -an | findstr :3000 >nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo ✅ Grafana is listening on port 3000
|
||||
) else (
|
||||
echo ❌ Grafana not responding on port 3000
|
||||
)
|
||||
|
||||
:end
|
||||
echo.
|
||||
echo ===============================================
|
||||
echo Services Started Successfully!
|
||||
echo ===============================================
|
||||
echo.
|
||||
echo Access Points:
|
||||
echo 🌐 Grafana Dashboard: http://localhost:3000 (admin/admin)
|
||||
echo 📊 InfluxDB UI: http://localhost:8086
|
||||
echo 📡 MQTT Broker: localhost:1883
|
||||
echo.
|
||||
echo You can now start the Smart Intersection Desktop App!
|
||||
echo.
|
||||
pause
|
||||
63
qt_app_pyside1/services/scripts/stop_services.bat
Normal file
63
qt_app_pyside1/services/scripts/stop_services.bat
Normal file
@@ -0,0 +1,63 @@
|
||||
@echo off
|
||||
REM Smart Intersection Services Stop Script
|
||||
REM Stops all running services
|
||||
|
||||
echo ===============================================
|
||||
echo Smart Intersection Services Shutdown
|
||||
echo ===============================================
|
||||
|
||||
REM Check if Docker Compose services are running
|
||||
docker-compose --version >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
cd /d "%~dp0..\docker"
|
||||
docker-compose ps >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo 🛑 Stopping Docker Compose services...
|
||||
docker-compose down
|
||||
echo ✅ Docker services stopped
|
||||
goto :end
|
||||
)
|
||||
)
|
||||
|
||||
echo 🛑 Stopping standalone services...
|
||||
|
||||
REM Stop Grafana (by finding and killing the process)
|
||||
echo Stopping Grafana...
|
||||
tasklist /FI "IMAGENAME eq grafana-server.exe" 2>NUL | find /I /N "grafana-server.exe">NUL
|
||||
if "%ERRORLEVEL%"=="0" (
|
||||
taskkill /F /IM grafana-server.exe >nul 2>&1
|
||||
echo ✅ Grafana stopped
|
||||
) else (
|
||||
echo ⚠️ Grafana was not running
|
||||
)
|
||||
|
||||
REM Stop InfluxDB
|
||||
echo Stopping InfluxDB...
|
||||
tasklist /FI "IMAGENAME eq influxd.exe" 2>NUL | find /I /N "influxd.exe">NUL
|
||||
if "%ERRORLEVEL%"=="0" (
|
||||
taskkill /F /IM influxd.exe >nul 2>&1
|
||||
echo ✅ InfluxDB stopped
|
||||
) else (
|
||||
echo ⚠️ InfluxDB was not running
|
||||
)
|
||||
|
||||
REM Stop Mosquitto MQTT
|
||||
echo Stopping Mosquitto MQTT...
|
||||
tasklist /FI "IMAGENAME eq mosquitto.exe" 2>NUL | find /I /N "mosquitto.exe">NUL
|
||||
if "%ERRORLEVEL%"=="0" (
|
||||
taskkill /F /IM mosquitto.exe >nul 2>&1
|
||||
echo ✅ Mosquitto MQTT stopped
|
||||
) else (
|
||||
echo ⚠️ Mosquitto MQTT was not running
|
||||
)
|
||||
|
||||
:end
|
||||
echo.
|
||||
echo ===============================================
|
||||
echo All Services Stopped
|
||||
echo ===============================================
|
||||
echo.
|
||||
echo 🛑 Smart Intersection services have been shut down
|
||||
echo You can now safely close this window
|
||||
echo.
|
||||
pause
|
||||
Reference in New Issue
Block a user