Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
53c4ddf6f5
|
|||
|
30b30c626c
|
|||
|
ca7547adcf
|
|||
|
817eda7d03
|
|||
|
56cd9d5323
|
|||
|
47f1490701
|
|||
| 33dac9c8e4 | |||
|
6cdfbedf9e
|
|||
|
e4d1e3eca4
|
|||
|
ad961dedc2
|
|||
|
289b6d5f59
|
|||
|
8881f6630f
|
|||
|
0a7f7f718e
|
|||
|
2685ee0212
|
|||
|
8f48bfaa5a
|
|||
|
2a722a7ae6
|
|||
|
cd3960b110
|
|||
|
bc7f7fe345
|
|||
|
7ba28dd02a
|
|||
|
d2feb67cef
|
|||
|
69749411cd
|
|||
|
364ada37c4
|
|||
|
89148d446e
|
|||
|
8865fe85ab
|
|||
|
2e0d3f4a6e
|
|||
|
e8e4cce689
|
|||
| 5ca7adc1f2 | |||
| a147162736 | |||
| 0c4b1e45be | |||
| de3e8fc68c | |||
| 2be89da897 | |||
| acd445a479 | |||
| 71a1cd32c3 |
@@ -20,6 +20,7 @@ telemetry_cache.json
|
|||||||
|
|
||||||
# Ignora log di sistema eventuali
|
# Ignora log di sistema eventuali
|
||||||
*.log
|
*.log
|
||||||
|
*.log.*
|
||||||
|
|
||||||
# Ignora le configurazioni reali dell'agente remoto
|
# Ignora le configurazioni reali dell'agente remoto
|
||||||
*.ini
|
*.ini
|
||||||
|
|||||||
@@ -4,12 +4,14 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
<a name="english"></a>
|
<a name="english"></a>
|
||||||
## 🇬🇧 English
|
## 🇬🇧 English
|
||||||
|
|
||||||
**Fleet Control Console** is a professional, real-time command and control (C2) dashboard designed for amateur radio repeater networks (MMDVM). This repository contains the **Server (Central Hub)** component, which provides a centralized web interface to monitor and manage a fleet of remote digital voice nodes (DMR, NXDN, YSF, P25).
|
**Fleet Control Console** is a professional, real-time command and control (C2) dashboard designed for amateur radio repeater networks (MMDVM). This repository contains the **Server (Central Hub)** component, which provides a centralized web interface to monitor and manage a fleet of remote digital voice nodes (DMR, NXDN, YSF, P25).
|
||||||
|
|
||||||
> ℹ️ **Note:** This is the Central Server repository. To monitor remote repeaters, you must install the [Fleet Control Agent](link_al_repo_agent_qui) on each node.
|
> ℹ️ **Note:** This is the Central Server repository. To monitor remote repeaters, you must install the [Fleet Control Agent](https://git.arifvg.it/iv3jdv/fleet-node-agent.git) on each node.
|
||||||
|
|
||||||
### 🏗️ Architecture & Technology Stack
|
### 🏗️ Architecture & Technology Stack
|
||||||
The server acts as the brain of the network:
|
The server acts as the brain of the network:
|
||||||
@@ -31,6 +33,10 @@ The server acts as the brain of the network:
|
|||||||
* A critical system daemon crashes (Auto-healing failed).
|
* A critical system daemon crashes (Auto-healing failed).
|
||||||
* Works securely even when the web app is closed or in the background.
|
* Works securely even when the web app is closed or in the background.
|
||||||
|
|
||||||
|
### ⚠️ Security Requirement: HTTPS
|
||||||
|
Please note that **Web Push Notifications** and the **Service Worker** require a secure connection to function. For security reasons, modern browsers only enable these features over **HTTPS**. If you are accessing the console via HTTP (outside of `localhost`), the "Push" button and notification registration will not work. We recommend using a reverse proxy (like Nginx, Caddy, or Traefik) with an SSL certificate.
|
||||||
|
**Pro Tip:** You can use Let's Encrypt to get a free SSL certificate for your domain.
|
||||||
|
|
||||||
#### 🛠️ Advanced Remote Control & Maintenance
|
#### 🛠️ Advanced Remote Control & Maintenance
|
||||||
* **Remote .INI Editor:** Edit daemon configuration files (e.g., MMDVMHost.ini) directly from the web interface without SSH access.
|
* **Remote .INI Editor:** Edit daemon configuration files (e.g., MMDVMHost.ini) directly from the web interface without SSH access.
|
||||||
* **Service Management:** Start, Stop, or Restart remote system daemons with a single click.
|
* **Service Management:** Start, Stop, or Restart remote system daemons with a single click.
|
||||||
@@ -49,17 +55,22 @@ The server acts as the brain of the network:
|
|||||||
|
|
||||||
### 🚀 Installation Guide
|
### 🚀 Installation Guide
|
||||||
|
|
||||||
#### 1. System Pre-requisites (Critical)
|
#### 0. Root Privileges
|
||||||
Before installing Python dependencies, install the necessary system compilers and pip/venv tools. On Debian/Ubuntu:
|
All installation steps must be executed as the `root` user. Before starting, elevate your privileges by running:
|
||||||
```bash
|
```bash
|
||||||
sudo apt update
|
apt update
|
||||||
sudo apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
||||||
|
```
|
||||||
|
#### 1. Clone Repository
|
||||||
|
Clone the repository into the /opt directory to ensure all systemd paths work correctly:
|
||||||
|
```bash
|
||||||
|
git clone https://tuo-gitea.com/utente/fleet-control-server.git /opt/fleet-control-server
|
||||||
|
cd /opt/fleet-control-server
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Virtual Environment Setup (Recommended)
|
#### 2. Virtual Environment Setup (Recommended)
|
||||||
To prevent conflicts with OS packages (PEP 668), create an isolated environment:
|
To prevent conflicts with OS packages (PEP 668), create an isolated environment:
|
||||||
```bash
|
```bash
|
||||||
cd /opt/web-control-server
|
cd /opt/fleet-control-server
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install --upgrade pip setuptools wheel
|
pip install --upgrade pip setuptools wheel
|
||||||
@@ -75,10 +86,10 @@ pip install -r requirements.txt
|
|||||||
#### 4. Running as a Service
|
#### 4. Running as a Service
|
||||||
To run the server continuously in production using Gunicorn:
|
To run the server continuously in production using Gunicorn:
|
||||||
```bash
|
```bash
|
||||||
sudo cp fleet-control.service /etc/systemd/system/
|
cp fleet-console.service /etc/systemd/system/
|
||||||
sudo systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
sudo systemctl enable fleet-control
|
systemctl enable fleet-console
|
||||||
sudo systemctl start fleet-control
|
systemctl start fleet-console
|
||||||
```
|
```
|
||||||
*(Ensure the `.service` file points to the `gunicorn` executable inside your `venv`)*.
|
*(Ensure the `.service` file points to the `gunicorn` executable inside your `venv`)*.
|
||||||
|
|
||||||
@@ -89,7 +100,7 @@ sudo systemctl start fleet-control
|
|||||||
|
|
||||||
**Fleet Control Console** è una dashboard di comando e controllo (C2) professionale in tempo reale, progettata per le reti di ripetitori radioamatoriali (MMDVM). Questo repository contiene il **Server (Central Hub)**, che fornisce un'interfaccia web centralizzata per monitorare e gestire una flotta di nodi digitali remoti (DMR, NXDN, YSF, P25).
|
**Fleet Control Console** è una dashboard di comando e controllo (C2) professionale in tempo reale, progettata per le reti di ripetitori radioamatoriali (MMDVM). Questo repository contiene il **Server (Central Hub)**, che fornisce un'interfaccia web centralizzata per monitorare e gestire una flotta di nodi digitali remoti (DMR, NXDN, YSF, P25).
|
||||||
|
|
||||||
> ℹ️ **Nota:** Questo è il repository del Server Centrale. Per monitorare i ripetitori, devi installare il [Fleet Control Agent](link_al_repo_agent_qui) su ciascun nodo remoto.
|
> ℹ️ **Nota:** Questo è il repository del Server Centrale. Per monitorare i ripetitori, devi installare il [Fleet Control Agent](https://git.arifvg.it/iv3jdv/fleet-node-agent.git) su ciascun nodo remoto.
|
||||||
|
|
||||||
### 🏗️ Architettura e Tecnologie
|
### 🏗️ Architettura e Tecnologie
|
||||||
Il server agisce da cervello della rete:
|
Il server agisce da cervello della rete:
|
||||||
@@ -111,6 +122,10 @@ Il server agisce da cervello della rete:
|
|||||||
* Un demone di sistema remoto si blocca (fallimento auto-healing).
|
* Un demone di sistema remoto si blocca (fallimento auto-healing).
|
||||||
* Funzionano in modo sicuro anche quando la web app è chiusa o in background.
|
* Funzionano in modo sicuro anche quando la web app è chiusa o in background.
|
||||||
|
|
||||||
|
### ⚠️ Sicurezza Richiesta: HTTPS
|
||||||
|
Si prega di notare che le **Notifiche Web Push** e il **Service Worker** richiedono una connessione sicura per funzionare. Per motivi di sicurezza, i browser moderni abilitano queste funzionalità solo su **HTTPS**. Se accedi alla console via HTTP (al di fuori di `localhost`), il tasto "Push" e la registrazione delle notifiche non saranno attivi. Si consiglia l'uso di un reverse proxy (come Nginx, Caddy o Traefik) con certificato SSL.
|
||||||
|
**Suggerimento:** Puoi usare Let's Encrypt per ottenere un certificato SSL gratuito per il tuo dominio.
|
||||||
|
|
||||||
#### 🛠️ Controllo Remoto & Manutenzione Avanzata
|
#### 🛠️ Controllo Remoto & Manutenzione Avanzata
|
||||||
* **Editor .INI Remoto:** Modifica i file di configurazione (es. MMDVMHost.ini) direttamente dal pannello web, senza bisogno di accessi SSH.
|
* **Editor .INI Remoto:** Modifica i file di configurazione (es. MMDVMHost.ini) direttamente dal pannello web, senza bisogno di accessi SSH.
|
||||||
* **Gestione Demoni:** Avvia, arresta o riavvia i servizi di sistema remoti con un clic.
|
* **Gestione Demoni:** Avvia, arresta o riavvia i servizi di sistema remoti con un clic.
|
||||||
@@ -129,17 +144,22 @@ Il server agisce da cervello della rete:
|
|||||||
|
|
||||||
### 🚀 Guida all'Installazione
|
### 🚀 Guida all'Installazione
|
||||||
|
|
||||||
#### 1. Requisiti di Sistema (Critici)
|
#### 0. Privilegi di Root
|
||||||
Prima di installare le dipendenze Python, installa i compilatori di base e gli strumenti per gli ambienti virtuali. Su Debian/Ubuntu:
|
Tutti i passaggi di installazione devono essere eseguiti come utente `root`. Prima di iniziare, eleva i tuoi privilegi eseguendo:
|
||||||
```bash
|
```bash
|
||||||
sudo apt update
|
apt update
|
||||||
sudo apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
||||||
|
```
|
||||||
|
#### 1. Clonazione dei Repository
|
||||||
|
Clona il repository nella cartella /opt per assicurarti che tutti i percorsi dei servizi systemd siano corretti:
|
||||||
|
```bash
|
||||||
|
git clone https://tuo-gitea.com/utente/fleet-control-server.git /opt/fleet-control-server
|
||||||
|
cd /opt/fleet-control-server
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Setup Ambiente Virtuale (Consigliato)
|
#### 2. Setup Ambiente Virtuale (Consigliato)
|
||||||
Per evitare conflitti con i pacchetti di sistema (regola PEP 668), crea una "bolla" isolata:
|
Per evitare conflitti con i pacchetti di sistema (regola PEP 668), crea una "bolla" isolata:
|
||||||
```bash
|
```bash
|
||||||
cd /opt/web-control-server
|
cd /opt/fleet-control-server
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install --upgrade pip setuptools wheel
|
pip install --upgrade pip setuptools wheel
|
||||||
@@ -155,10 +175,10 @@ pip install -r requirements.txt
|
|||||||
#### 4. Esecuzione come Servizio (systemd)
|
#### 4. Esecuzione come Servizio (systemd)
|
||||||
Per eseguire il server in produzione in modo continuo e stabile con Gunicorn:
|
Per eseguire il server in produzione in modo continuo e stabile con Gunicorn:
|
||||||
```bash
|
```bash
|
||||||
sudo cp fleet-control.service /etc/systemd/system/
|
cp fleet-console.service /etc/systemd/system/
|
||||||
sudo systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
sudo systemctl enable fleet-control
|
systemctl enable fleet-console
|
||||||
sudo systemctl start fleet-control
|
systemctl start fleet-console
|
||||||
```
|
```
|
||||||
*(Assicurati che il file `.service` punti all'eseguibile `gunicorn` situato all'interno della cartella `venv`).*
|
*(Assicurati che il file `.service` punti all'eseguibile `gunicorn` situato all'interno della cartella `venv`).*
|
||||||
|
|
||||||
|
|||||||
-144
@@ -1,144 +0,0 @@
|
|||||||
# 📡 Fleet Control Console (Server)
|
|
||||||
|
|
||||||
🌍 *[Read in English](#english) | 🇮🇹 [Leggi in Italiano](#italiano)*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<a name="english"></a>
|
|
||||||
## 🇬🇧 English
|
|
||||||
|
|
||||||
**Fleet Control Console** is a professional, real-time command and control (C2) dashboard designed for amateur radio repeater networks (MMDVM).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 🤖 Remote Agent
|
|
||||||
To monitor your remote nodes (Raspberry Pi), download the dedicated lightweight agent here:
|
|
||||||
`https://git.arifvg.it/iv3jdv/web-console-agent.git`
|
|
||||||
|
|
||||||
### ✨ Features
|
|
||||||
* **Zero-Latency Real-Time UI:** Powered by WebSockets (Socket.IO).
|
|
||||||
* **Web Push Notifications:** Instant alerts on desktop or mobile.
|
|
||||||
* **Centralized Telemetry & Service Management.**
|
|
||||||
* **Global Operations:** Switch profiles instantly.
|
|
||||||
|
|
||||||
### 🚀 Installation & Setup
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
1. PRE-REQUISITES (CRITICAL)
|
|
||||||
------------------------------------------------------------
|
|
||||||
Before installing Python dependencies, you must install
|
|
||||||
system compilers and development libraries.
|
|
||||||
|
|
||||||
Debian/Ubuntu:
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install build-essential python3-dev libssl-dev libffi-dev
|
|
||||||
|
|
||||||
Upgrade base pip tools:
|
|
||||||
pip install --upgrade pip setuptools wheel
|
|
||||||
|
|
||||||
Create a virtual environment (Recommended):
|
|
||||||
python3 -m venv venv
|
|
||||||
source venv/bin/activate
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
2. SERVER SETUP (CENTRAL HUB)
|
|
||||||
------------------------------------------------------------
|
|
||||||
The server handles the web interface and user permissions.
|
|
||||||
|
|
||||||
Steps:
|
|
||||||
1. Configure 'config.json' using 'config.example.json'.
|
|
||||||
2. Enter MQTT credentials and VAPID keys.
|
|
||||||
3. Define repeaters in the 'clients.json' file.
|
|
||||||
4. Install production WSGI server packages (if not in requirements):
|
|
||||||
pip install gunicorn gevent gevent-websocket
|
|
||||||
5. Start the production server:
|
|
||||||
gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" -w 1 --bind 0.0.0.0:9000 app:app
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
3. GENERATING VAPID KEYS (PUSH NOTIFICATIONS)
|
|
||||||
------------------------------------------------------------
|
|
||||||
⚠️ WARNING: Web Push Notifications strictly require the
|
|
||||||
dashboard to be accessed via a secure HTTPS connection.
|
|
||||||
They will NOT work over standard HTTP.
|
|
||||||
|
|
||||||
1. Go to https://vapidkeys.com/ and generate the keys.
|
|
||||||
2. Copy 'Public Key' and 'Private Key' into 'config.json'.
|
|
||||||
3. Set 'vapid_claim_email' (e.g., "mailto:your@email.com").
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
4. RUNNING AS A SERVICE (SYSTEMD)
|
|
||||||
------------------------------------------------------------
|
|
||||||
Configuration:
|
|
||||||
1. Copy .service file to '/etc/systemd/system/':
|
|
||||||
sudo cp fleet-console.service /etc/systemd/system/
|
|
||||||
2. Reload systemd: sudo systemctl daemon-reload
|
|
||||||
3. Enable on boot: sudo systemctl enable fleet-console
|
|
||||||
4. Start service: sudo systemctl start fleet-console
|
|
||||||
|
|
||||||
<a name="italiano"></a>
|
|
||||||
## 🇮🇹 Italiano
|
|
||||||
|
|
||||||
**Fleet Control Console** è una dashboard di comando e controllo (C2) professionale in tempo reale per le reti di ripetitori radioamatoriali (MMDVM).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 🤖 Agente Remoto
|
|
||||||
Per monitorare i tuoi nodi remoti (Raspberry Pi), scarica l'agente dedicato qui:
|
|
||||||
`https://git.arifvg.it/iv3jdv/web-console-agent.git`
|
|
||||||
|
|
||||||
### ✨ Funzionalità
|
|
||||||
* **Interfaccia Real-Time a Latenza Zero** tramite WebSockets.
|
|
||||||
* **Notifiche Push Web** per allarmi critici.
|
|
||||||
* **Telemetria Centralizzata e Gestione Servizi.**
|
|
||||||
* **Operazioni Globali** su tutta la rete.
|
|
||||||
|
|
||||||
### 🚀 Installazione
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
1. REQUISITI PRELIMINARI (CRITICI)
|
|
||||||
------------------------------------------------------------
|
|
||||||
Prima di installare le dipendenze Python, è necessario
|
|
||||||
installare i compilatori di sistema. Senza questi,
|
|
||||||
l'installazione di 'gevent' fallirà su VPS vergini.
|
|
||||||
|
|
||||||
Esegui su Debian/Ubuntu:
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install build-essential python3-dev libssl-dev libffi-dev
|
|
||||||
|
|
||||||
Aggiorna gli strumenti di base di pip:
|
|
||||||
pip install --upgrade pip setuptools wheel
|
|
||||||
|
|
||||||
Crea un ambiente virtuale (consigliato):
|
|
||||||
python3 -m venv venv
|
|
||||||
source venv/bin/activate
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
2. SETUP DEL SERVER
|
|
||||||
------------------------------------------------------------
|
|
||||||
Passaggi:
|
|
||||||
1. Configura 'config.json' partendo da 'config.example.json'.
|
|
||||||
2. Inserisci credenziali MQTT e chiavi VAPID.
|
|
||||||
3. Definisci i ripetitori in 'clients.json'.
|
|
||||||
4. Avvia il server di produzione:
|
|
||||||
gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" -w 1 --bind 0.0.0.0:9000 app:app
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
3. GENERAZIONE CHIAVI VAPID
|
|
||||||
------------------------------------------------------------
|
|
||||||
⚠️ ATTENZIONE: Le notifiche push richiedono HTTPS.
|
|
||||||
|
|
||||||
1. Vai su https://vapidkeys.com/ e genera le chiavi.
|
|
||||||
2. Copia 'Public Key' e 'Private Key' nel 'config.json'.
|
|
||||||
3. Imposta 'vapid_claim_email' (es. "mailto:tua@email.com").
|
|
||||||
|
|
||||||
------------------------------------------------------------
|
|
||||||
4. ESECUZIONE COME SERVIZIO
|
|
||||||
------------------------------------------------------------
|
|
||||||
1. sudo cp fleet-console.service /etc/systemd/system/
|
|
||||||
2. sudo systemctl daemon-reload
|
|
||||||
3. sudo systemctl enable fleet-console
|
|
||||||
4. sudo systemctl start fleet-console
|
|
||||||
---
|
|
||||||
*Created by IV3JDV @ ARIFVG - 2026*
|
|
||||||
@@ -151,6 +151,8 @@ last_notified_errors = {}
|
|||||||
device_health = {}
|
device_health = {}
|
||||||
last_seen_reflector = {}
|
last_seen_reflector = {}
|
||||||
network_mapping = {}
|
network_mapping = {}
|
||||||
|
node_info = {}
|
||||||
|
node_general = {}
|
||||||
|
|
||||||
if os.path.exists(CACHE_FILE):
|
if os.path.exists(CACHE_FILE):
|
||||||
try:
|
try:
|
||||||
@@ -170,18 +172,22 @@ def on_connect(client, userdata, flags, reason_code, properties=None):
|
|||||||
logger.info("✅ Successfully connected to MQTT Broker! Subscribing to topics...")
|
logger.info("✅ Successfully connected to MQTT Broker! Subscribing to topics...")
|
||||||
# Invia lo stato Online ai client web
|
# Invia lo stato Online ai client web
|
||||||
socketio.emit('mqtt_status', {'connected': True})
|
socketio.emit('mqtt_status', {'connected': True})
|
||||||
client.subscribe([
|
|
||||||
("servizi/+/stat", 0),
|
# --- LETTURA DINAMICA DEI TOPIC ---
|
||||||
("dmr-gateway/+/json", 0),
|
default_topics = [
|
||||||
("devices/+/services", 0),
|
"servizi/+/stat", "dmr-gateway/+/json", "devices/+/services",
|
||||||
("nxdn-gateway/+/json", 0),
|
"nxdn-gateway/+/json", "ysf-gateway/+/json", "p25-gateway/+/json",
|
||||||
("ysf-gateway/+/json", 0),
|
"dstar-gateway/+/json", "mmdvm/+/json", "devices/#", "data/#"
|
||||||
("p25-gateway/+/json", 0),
|
]
|
||||||
("dstar-gateway/+/json", 0),
|
|
||||||
("mmdvm/+/json", 0),
|
# Cerca la lista "topics" nel config.json, se non la trova usa quella di default
|
||||||
("devices/#", 0),
|
topics_list = config.get('mqtt', {}).get('topics', default_topics)
|
||||||
("data/#", 0)
|
|
||||||
])
|
# Converte la lista di stringhe nel formato richiesto da paho-mqtt: [(topic, qos), (topic, qos)...]
|
||||||
|
subscribe_list = [(topic, 0) for topic in topics_list]
|
||||||
|
|
||||||
|
client.subscribe(subscribe_list)
|
||||||
|
logger.info(f"Subscribed to {len(subscribe_list)} MQTT topics.")
|
||||||
else:
|
else:
|
||||||
mqtt_connected_status = False
|
mqtt_connected_status = False
|
||||||
socketio.emit('mqtt_status', {'connected': False})
|
socketio.emit('mqtt_status', {'connected': False})
|
||||||
@@ -205,10 +211,17 @@ def on_message(client, userdata, msg):
|
|||||||
payload = msg.payload.decode().strip()
|
payload = msg.payload.decode().strip()
|
||||||
parts = topic.split('/')
|
parts = topic.split('/')
|
||||||
if len(parts) < 2: return
|
if len(parts) < 2: return
|
||||||
|
|
||||||
cid = parts[1].lower()
|
cid = parts[1].lower()
|
||||||
|
|
||||||
|
# --- MAGIA DELLA NORMALIZZAZIONE ---
|
||||||
|
# Prendiamo i blocchi del topic, li facciamo minuscoli e togliamo i trattini
|
||||||
|
# Così "DMRGateway", "dmr-gateway" e "dmrgateway" diventano tutti identici per noi
|
||||||
|
p0 = parts[0].lower().replace('-', '')
|
||||||
|
p2 = parts[2].lower().replace('-', '') if len(parts) > 2 else ""
|
||||||
|
|
||||||
# --- CAPTURE FULL CONFIGURATIONS ---
|
# --- CAPTURE FULL CONFIGURATIONS ---
|
||||||
if parts[0] == 'data' and len(parts) >= 4 and parts[3] == 'full_config':
|
if p0 == 'data' and len(parts) >= 4 and parts[3].lower() == 'full_config':
|
||||||
cid_conf = parts[1].lower()
|
cid_conf = parts[1].lower()
|
||||||
svc_name = parts[2].lower()
|
svc_name = parts[2].lower()
|
||||||
if cid_conf not in device_configs:
|
if cid_conf not in device_configs:
|
||||||
@@ -220,7 +233,7 @@ def on_message(client, userdata, msg):
|
|||||||
logger.error(f"Error parsing config JSON: {e}")
|
logger.error(f"Error parsing config JSON: {e}")
|
||||||
|
|
||||||
# --- NODE AND SERVICE STATE MANAGEMENT ---
|
# --- NODE AND SERVICE STATE MANAGEMENT ---
|
||||||
elif parts[0] == 'servizi':
|
elif p0 == 'servizi':
|
||||||
client_states[cid] = payload
|
client_states[cid] = payload
|
||||||
socketio.emit('dati_aggiornati') # <--- WEBSOCKET
|
socketio.emit('dati_aggiornati') # <--- WEBSOCKET
|
||||||
|
|
||||||
@@ -241,7 +254,7 @@ def on_message(client, userdata, msg):
|
|||||||
save_cache(client_telemetry)
|
save_cache(client_telemetry)
|
||||||
|
|
||||||
# --- DEVICE HEALTH MANAGEMENT ---
|
# --- DEVICE HEALTH MANAGEMENT ---
|
||||||
elif parts[0] == 'devices' and len(parts) >= 3 and parts[2] == 'services':
|
elif p0 == 'devices' and len(parts) >= 3 and p2 == 'services':
|
||||||
try:
|
try:
|
||||||
data = json.loads(payload)
|
data = json.loads(payload)
|
||||||
device_health[cid] = {
|
device_health[cid] = {
|
||||||
@@ -275,7 +288,7 @@ def on_message(client, userdata, msg):
|
|||||||
logger.error(f"Error parsing health data: {e}")
|
logger.error(f"Error parsing health data: {e}")
|
||||||
|
|
||||||
# --- DMR GATEWAY MANAGEMENT ---
|
# --- DMR GATEWAY MANAGEMENT ---
|
||||||
elif len(parts) >= 4 and parts[0] == 'data' and parts[2].lower() == 'dmrgateway' and (parts[3].upper().startswith('NETWORK') or parts[3].upper().startswith('DMR NETWORK')):
|
elif len(parts) >= 4 and p0 == 'data' and p2 == 'dmrgateway' and (parts[3].upper().startswith('NETWORK') or parts[3].upper().startswith('DMR NETWORK')):
|
||||||
try:
|
try:
|
||||||
cid = parts[1].lower()
|
cid = parts[1].lower()
|
||||||
data = json.loads(payload)
|
data = json.loads(payload)
|
||||||
@@ -301,14 +314,58 @@ def on_message(client, userdata, msg):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error parsing DMRGateway for {cid}: {e}")
|
logger.error(f"Error parsing DMRGateway for {cid}: {e}")
|
||||||
|
|
||||||
|
# --- MMDVMHOST INFO MANAGEMENT (FREQUENZE & LOCATION) ---
|
||||||
|
elif len(parts) >= 4 and p0 == 'data' and p2 == 'mmdvmhost' and parts[3].lower() == 'info':
|
||||||
|
try:
|
||||||
|
cid = parts[1].lower()
|
||||||
|
data = json.loads(payload)
|
||||||
|
|
||||||
|
# Estrazione dati
|
||||||
|
tx = data.get("TXFrequency", "0")
|
||||||
|
rx = data.get("RXFrequency", "0")
|
||||||
|
lat = data.get("Latitude", "0.0")
|
||||||
|
lon = data.get("Longitude", "0.0")
|
||||||
|
loc = data.get("Location", "Sconosciuta")
|
||||||
|
|
||||||
|
def format_freq(f):
|
||||||
|
if str(f).isdigit() and int(f) > 0:
|
||||||
|
return f"{int(f)/1000000:.3f} MHz"
|
||||||
|
return str(f)
|
||||||
|
|
||||||
|
node_info[cid] = {
|
||||||
|
"tx": format_freq(tx),
|
||||||
|
"rx": format_freq(rx),
|
||||||
|
"lat": lat,
|
||||||
|
"lon": lon,
|
||||||
|
"loc": loc
|
||||||
|
}
|
||||||
|
socketio.emit('dati_aggiornati')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing MMDVMHost info for {cid}: {e}")
|
||||||
|
|
||||||
|
# --- MMDVMHOST GENERAL MANAGEMENT (CALLSIGN & ID & DUPLEX) ---
|
||||||
|
elif len(parts) >= 4 and p0 == 'data' and p2 == 'mmdvmhost' and parts[3].lower() == 'general':
|
||||||
|
try:
|
||||||
|
cid = parts[1].lower()
|
||||||
|
data = json.loads(payload)
|
||||||
|
callsign = data.get("Callsign", "")
|
||||||
|
radio_id = data.get("Id", "")
|
||||||
|
duplex = data.get("Duplex", "1") # 1 = Repeater, 0 = Simplex
|
||||||
|
|
||||||
|
if callsign:
|
||||||
|
node_general[cid] = {"callsign": callsign, "radio_id": radio_id, "duplex": str(duplex)}
|
||||||
|
socketio.emit('dati_aggiornati')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing MMDVMHost general for {cid}: {e}")
|
||||||
|
|
||||||
# --- OTHER GATEWAYS MANAGEMENT ---
|
# --- OTHER GATEWAYS MANAGEMENT ---
|
||||||
elif parts[0] in ['dmr-gateway', 'nxdn-gateway', 'ysf-gateway', 'p25-gateway', 'dstar-gateway']:
|
elif p0 in ['dmrgateway', 'nxdngateway', 'ysfgateway', 'p25gateway', 'dstargateway']:
|
||||||
data = json.loads(payload)
|
data = json.loads(payload)
|
||||||
proto = "DMR"
|
proto = "DMR"
|
||||||
if "nxdn" in parts[0]: proto = "NXDN"
|
if "nxdn" in p0: proto = "NXDN"
|
||||||
elif "ysf" in parts[0]: proto = "YSF"
|
elif "ysf" in p0: proto = "YSF"
|
||||||
elif "p25" in parts[0]: proto = "P25"
|
elif "p25" in p0: proto = "P25"
|
||||||
elif "dstar" in parts[0]: proto = "D-STAR"
|
elif "dstar" in p0: proto = "D-STAR"
|
||||||
|
|
||||||
m = ""
|
m = ""
|
||||||
if 'status' in data:
|
if 'status' in data:
|
||||||
@@ -430,7 +487,9 @@ def get_states():
|
|||||||
"states": client_states,
|
"states": client_states,
|
||||||
"telemetry": client_telemetry,
|
"telemetry": client_telemetry,
|
||||||
"health": device_health,
|
"health": device_health,
|
||||||
"networks": network_mapping
|
"networks": network_mapping,
|
||||||
|
"info": node_info,
|
||||||
|
"general": node_general
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.route('/api/stats')
|
@app.route('/api/stats')
|
||||||
@@ -670,37 +729,55 @@ def global_cmd():
|
|||||||
return jsonify({"success": True})
|
return jsonify({"success": True})
|
||||||
|
|
||||||
def auto_update_ids():
|
def auto_update_ids():
|
||||||
while True:
|
"""Versione corretta: controlla all'avvio e poi ogni notte."""
|
||||||
|
|
||||||
|
def download_logic():
|
||||||
try:
|
try:
|
||||||
|
# Usiamo esattamente i nomi e le URL definiti nel tuo config/codice
|
||||||
with open(CONFIG_PATH, 'r') as f:
|
with open(CONFIG_PATH, 'r') as f:
|
||||||
current_cfg = json.load(f)
|
current_cfg = json.load(f)
|
||||||
target_time = current_cfg.get("update_schedule", "03:00")
|
|
||||||
urls = current_cfg.get("id_urls", {
|
urls = current_cfg.get("id_urls", {
|
||||||
"dmr": "https://radioid.net/static/users.csv",
|
"dmr": "https://radioid.net/static/users.csv",
|
||||||
"nxdn": "https://radioid.net/static/nxdn.csv"
|
"nxdn": "https://radioid.net/static/nxdn.csv"
|
||||||
})
|
})
|
||||||
now = time.strftime("%H:%M")
|
|
||||||
if now == target_time:
|
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
|
||||||
logger.info(f">>> [AUTO-UPDATE] Scheduled time reached ({now}). Downloading...")
|
logger.info("📡 Inizio download database ID (DMR e NXDN)...")
|
||||||
|
|
||||||
|
# Download DMR -> dmrid.dat
|
||||||
|
req_dmr = urllib.request.Request(urls["dmr"], headers=headers)
|
||||||
|
with urllib.request.urlopen(req_dmr) as response, open(DMR_IDS_PATH, 'wb') as out_file:
|
||||||
|
out_file.write(response.read())
|
||||||
|
|
||||||
# Trucco: Camuffiamo Python da browser per bypassare i blocchi Cloudflare
|
# Download NXDN -> nxdn.csv
|
||||||
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'}
|
req_nxdn = urllib.request.Request(urls["nxdn"], headers=headers)
|
||||||
|
with urllib.request.urlopen(req_nxdn) as response, open(NXDN_IDS_PATH, 'wb') as out_file:
|
||||||
|
out_file.write(response.read())
|
||||||
|
|
||||||
# Download DMR
|
load_ids()
|
||||||
req_dmr = urllib.request.Request(urls["dmr"], headers=headers)
|
logger.info("✅ Aggiornamento completato con successo.")
|
||||||
with urllib.request.urlopen(req_dmr) as response, open(DMR_IDS_PATH, 'wb') as out_file:
|
|
||||||
out_file.write(response.read())
|
|
||||||
|
|
||||||
# Download NXDN
|
|
||||||
req_nxdn = urllib.request.Request(urls["nxdn"], headers=headers)
|
|
||||||
with urllib.request.urlopen(req_nxdn) as response, open(NXDN_IDS_PATH, 'wb') as out_file:
|
|
||||||
out_file.write(response.read())
|
|
||||||
|
|
||||||
load_ids()
|
|
||||||
logger.info(f">>> [AUTO-UPDATE] Completed successfully.")
|
|
||||||
time.sleep(65)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f">>> [AUTO-UPDATE] Error: {e}")
|
logger.error(f"❌ Errore durante il download: {e}")
|
||||||
|
|
||||||
|
# --- CONTROLLO INIZIALE ALL'AVVIO ---
|
||||||
|
if not os.path.exists(DMR_IDS_PATH) or not os.path.exists(NXDN_IDS_PATH):
|
||||||
|
logger.info("🔍 File ID mancanti. Avvio download immediato...")
|
||||||
|
download_logic()
|
||||||
|
|
||||||
|
# --- CICLO NOTTURNO ---
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
now = time.strftime("%H:%M")
|
||||||
|
with open(CONFIG_PATH, 'r') as f:
|
||||||
|
target_time = json.load(f).get("update_schedule", "03:00")
|
||||||
|
|
||||||
|
if now == target_time:
|
||||||
|
logger.info(f"⏰ Orario programmato ({target_time}) raggiunto. Aggiorno...")
|
||||||
|
download_logic()
|
||||||
|
time.sleep(65)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"⚠️ Errore nel thread update: {e}")
|
||||||
time.sleep(30)
|
time.sleep(30)
|
||||||
|
|
||||||
@app.route('/api/ui_config', methods=['GET'])
|
@app.route('/api/ui_config', methods=['GET'])
|
||||||
|
|||||||
+13
-1
@@ -4,7 +4,19 @@
|
|||||||
"port": 1883,
|
"port": 1883,
|
||||||
"user": "your_username",
|
"user": "your_username",
|
||||||
"password": "your_password",
|
"password": "your_password",
|
||||||
"client_id": "fleet_backend_prod"
|
"client_id": "fleet_backend_prod",
|
||||||
|
"topics": [
|
||||||
|
"servizi/+/stat",
|
||||||
|
"dmr-gateway/+/json",
|
||||||
|
"devices/+/services",
|
||||||
|
"nxdn-gateway/+/json",
|
||||||
|
"ysf-gateway/+/json",
|
||||||
|
"p25-gateway/+/json",
|
||||||
|
"dstar-gateway/+/json",
|
||||||
|
"mmdvm/+/json",
|
||||||
|
"devices/#",
|
||||||
|
"data/#"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"web_admin": {
|
"web_admin": {
|
||||||
"default_user": "admin",
|
"default_user": "admin",
|
||||||
|
|||||||
+30
-16
@@ -2,6 +2,13 @@
|
|||||||
INSTALLATION GUIDE - FLEET CONTROL CONSOLE (SERVER)
|
INSTALLATION GUIDE - FLEET CONTROL CONSOLE (SERVER)
|
||||||
============================================================
|
============================================================
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
0. IMPORTANT: ROOT PRIVILEGES
|
||||||
|
------------------------------------------------------------
|
||||||
|
All installation steps must be executed as the "root" user.
|
||||||
|
Before starting, elevate your privileges by running:
|
||||||
|
sudo su
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
1. PRE-REQUISITES (CRITICAL)
|
1. PRE-REQUISITES (CRITICAL)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
@@ -9,11 +16,11 @@ Before installing Python dependencies, you must install
|
|||||||
system compilers, development libraries, and pip/venv tools.
|
system compilers, development libraries, and pip/venv tools.
|
||||||
|
|
||||||
Debian/Ubuntu:
|
Debian/Ubuntu:
|
||||||
sudo apt update
|
apt update
|
||||||
sudo apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
||||||
|
|
||||||
Create and activate a virtual environment (CRITICAL on Debian 12+):
|
Create and activate a virtual environment (CRITICAL on Debian 12+):
|
||||||
cd /opt/web-control-server
|
cd /opt/fleet-control-server
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
|
|
||||||
@@ -56,20 +63,27 @@ open 'fleet-control.service' and ensure the paths point to your
|
|||||||
virtual environment.
|
virtual environment.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
WorkingDirectory=/opt/web-control-server
|
WorkingDirectory=/opt/fleet-control-server
|
||||||
ExecStart=/opt/web-control-server/venv/bin/gunicorn -k ...
|
ExecStart=/opt/fleet-control-server/venv/bin/gunicorn -k ...
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
1. Copy .service file to '/etc/systemd/system/':
|
1. Copy .service file to '/etc/systemd/system/':
|
||||||
sudo cp fleet-control.service /etc/systemd/system/
|
sudo cp fleet-console.service /etc/systemd/system/
|
||||||
2. Reload systemd: sudo systemctl daemon-reload
|
2. Reload systemd: systemctl daemon-reload
|
||||||
3. Enable on boot: sudo systemctl enable fleet-console
|
3. Enable on boot: systemctl enable fleet-console
|
||||||
4. Start service: sudo systemctl start fleet-console
|
4. Start service: systemctl start fleet-console
|
||||||
|
|
||||||
============================================================
|
============================================================
|
||||||
GUIDA ALL'INSTALLAZIONE - SERVER (ITALIANO)
|
GUIDA ALL'INSTALLAZIONE - SERVER (ITALIANO)
|
||||||
============================================================
|
============================================================
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
0. IMPORTANTE: PRIVILEGI DI ROOT
|
||||||
|
------------------------------------------------------------
|
||||||
|
Tutti i passaggi di installazione devono essere eseguiti
|
||||||
|
come utente "root". Prima di iniziare, eleva i tuoi privilegi:
|
||||||
|
sudo su
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
1. REQUISITI PRELIMINARI (CRITICI)
|
1. REQUISITI PRELIMINARI (CRITICI)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
@@ -82,7 +96,7 @@ Esegui su Debian/Ubuntu:
|
|||||||
sudo apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
sudo apt install build-essential python3-dev libssl-dev libffi-dev python3-pip python3-venv
|
||||||
|
|
||||||
Crea e attiva un ambiente virtuale (FONDAMENTALE su Debian 12+):
|
Crea e attiva un ambiente virtuale (FONDAMENTALE su Debian 12+):
|
||||||
cd /opt/web-control-server
|
cd /opt/fleet-control-server
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
|
|
||||||
@@ -118,11 +132,11 @@ servizio, apri 'fleet-control.service' e assicurati che i percorsi
|
|||||||
puntino al tuo ambiente virtuale!
|
puntino al tuo ambiente virtuale!
|
||||||
|
|
||||||
Esempio:
|
Esempio:
|
||||||
WorkingDirectory=/opt/web-control-server
|
WorkingDirectory=/opt/fleet-control-server
|
||||||
ExecStart=/opt/web-control-server/venv/bin/gunicorn -k ...
|
ExecStart=/opt/fleet-control-server/venv/bin/gunicorn -k ...
|
||||||
|
|
||||||
Passaggi:
|
Passaggi:
|
||||||
1. Copia il file in systemd: sudo cp fleet-control.service /etc/systemd/system/
|
1. Copia il file in systemd: sudo cp fleet-console.service /etc/systemd/system/
|
||||||
2. Ricarica la configurazione: sudo systemctl daemon-reload
|
2. Ricarica la configurazione: systemctl daemon-reload
|
||||||
3. Abilita all'avvio: sudo systemctl enable fleet-console
|
3. Abilita all'avvio: systemctl enable fleet-console
|
||||||
4. Avvia il servizio: sudo systemctl start fleet-console
|
4. Avvia il servizio: systemctl start fleet-console
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ After=network.target
|
|||||||
Type=simple
|
Type=simple
|
||||||
User=root
|
User=root
|
||||||
# Assicurati che questo sia il percorso esatto della cartella del tuo progetto
|
# Assicurati che questo sia il percorso esatto della cartella del tuo progetto
|
||||||
WorkingDirectory=/opt/web-control-server
|
WorkingDirectory=/opt/fleet-control-server
|
||||||
# PUNTA AL GUNICORN DENTRO IL VENV!
|
# PUNTA AL GUNICORN DENTRO IL VENV!
|
||||||
ExecStart=/opt/web-control-server/venv/bin/gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" -w 1 --graceful-timeout 2 --bind 0.0.0.0:9000 app:app
|
ExecStart=/opt/fleet-control-server/venv/bin/gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" -w 1 --graceful-timeout 2 --bind 0.0.0.0:9000 app:app
|
||||||
TimeoutStopSec=3
|
TimeoutStopSec=3
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
+333
-459
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user