Improve Dashboard functionality

This commit is contained in:
2026-08-29 18:39:48 +02:00
parent 3db3168a8a
commit d0c3ea03a5
+14 -17
View File
@@ -98,7 +98,7 @@ def carica_rubrica():
UTENTI_SIP_DMR = carica_rubrica() UTENTI_SIP_DMR = carica_rubrica()
# ==================================================================== # ====================================================================
# DASHBOARD MANAGER (Motore Dati) # DASHBOARD MANAGER (Motore Dati - Modificato Sincrono)
# ==================================================================== # ====================================================================
class DashboardManager: class DashboardManager:
def __init__(self): def __init__(self):
@@ -112,7 +112,7 @@ class DashboardManager:
try: await ws.send_str(msg_str) try: await ws.send_str(msg_str)
except Exception: self.ws_clients.discard(ws) except Exception: self.ws_clients.discard(ws)
async def inizia_chiamata(self, id_chiamata, direzione, mittente, destinatario): def inizia_chiamata(self, id_chiamata, direzione, mittente, destinatario):
nom_mittente = get_nominativo(mittente) nom_mittente = get_nominativo(mittente)
nom_dest = get_nominativo(destinatario) nom_dest = get_nominativo(destinatario)
@@ -126,20 +126,20 @@ class DashboardManager:
"stato": "ON" "stato": "ON"
} }
self.chiamate_attive[id_chiamata] = dati self.chiamate_attive[id_chiamata] = dati
await self.broadcast({"evento": "chiamata_iniziata", "dati": dati}) asyncio.create_task(self.broadcast({"evento": "chiamata_iniziata", "dati": dati}))
async def aggiorna_rx(self, id_chiamata): def aggiorna_rx(self, id_chiamata):
if id_chiamata in self.chiamate_attive: if id_chiamata in self.chiamate_attive:
self.chiamate_attive[id_chiamata]["ultimo_frame"] = time.time() self.chiamate_attive[id_chiamata]["ultimo_frame"] = time.time()
async def termina_chiamata(self, id_chiamata): def termina_chiamata(self, id_chiamata):
if id_chiamata in self.chiamate_attive: if id_chiamata in self.chiamate_attive:
dati = self.chiamate_attive.pop(id_chiamata) dati = self.chiamate_attive.pop(id_chiamata)
dati["fine"] = time.time() dati["fine"] = time.time()
dati["durata"] = round(dati["fine"] - dati["inizio"], 1) dati["durata"] = round(dati["fine"] - dati["inizio"], 1)
dati["stato"] = "OFF" dati["stato"] = "OFF"
self.storico.appendleft(dati) self.storico.appendleft(dati)
await self.broadcast({"evento": "chiamata_terminata", "dati": dati}) asyncio.create_task(self.broadcast({"evento": "chiamata_terminata", "dati": dati}))
async def watchdog_rx(self): async def watchdog_rx(self):
while True: while True:
@@ -147,7 +147,7 @@ class DashboardManager:
ora = time.time() ora = time.time()
for id_chiamata, dati in list(self.chiamate_attive.items()): for id_chiamata, dati in list(self.chiamate_attive.items()):
if dati["direzione"] == "RX" and ora - dati.get("ultimo_frame", ora) > 1.5: if dati["direzione"] == "RX" and ora - dati.get("ultimo_frame", ora) > 1.5:
await self.termina_chiamata(id_chiamata) self.termina_chiamata(id_chiamata)
# ==================================================================== # ====================================================================
# ENDPOINT WEB (aiohttp) # ENDPOINT WEB (aiohttp)
@@ -353,10 +353,10 @@ class SessionManager:
self.gestisci_ptt(sess, False) self.gestisci_ptt(sess, False)
self.vocoder.rilascia_porta(f"SIP_{call_id}") self.vocoder.rilascia_porta(f"SIP_{call_id}")
# PULIZIA FORZATA: Chiudiamo eventuali righe RX rimaste appese per questo interno # Chiusura sincrona per evitare code
for id_dash in list(dashboard_app.chiamate_attive.keys()): for id_dash in list(dashboard_app.chiamate_attive.keys()):
if id_dash.startswith("RX_") and id_dash.endswith(f"_{call_id}"): if id_dash.startswith("RX_") and id_dash.endswith(f"_{call_id}"):
asyncio.create_task(dashboard_app.termina_chiamata(id_dash)) dashboard_app.termina_chiamata(id_dash)
del self.sessioni[call_id] del self.sessioni[call_id]
asyncio.create_task(dashboard_app.broadcast({"evento": "estensioni_update"})) asyncio.create_task(dashboard_app.broadcast({"evento": "estensioni_update"}))
@@ -372,11 +372,11 @@ class SessionManager:
nome_base = get_nominativo(sess.dmr_id) nome_base = get_nominativo(sess.dmr_id)
mittente_display = f"{nome_base} (Est. {sess.chiamante})" mittente_display = f"{nome_base} (Est. {sess.chiamante})"
asyncio.create_task(dashboard_app.inizia_chiamata(id_dash_tx, "TX", mittente_display, sess.tg_destinazione)) dashboard_app.inizia_chiamata(id_dash_tx, "TX", mittente_display, sess.tg_destinazione)
elif stato is False and sess.ptt_attivo: elif stato is False and sess.ptt_attivo:
sess.ptt_attivo = False sess.ptt_attivo = False
if self.openbridge: self.openbridge.chiudi_flusso_chiamata(sess) if self.openbridge: self.openbridge.chiudi_flusso_chiamata(sess)
asyncio.create_task(dashboard_app.termina_chiamata(id_dash_tx)) dashboard_app.termina_chiamata(id_dash_tx)
def gestisci_dtmf_digit(self, sess, digit): def gestisci_dtmf_digit(self, sess, digit):
if sess.ptt_attivo: return if sess.ptt_attivo: return
@@ -424,9 +424,9 @@ class SessionManager:
if id_dash_rx not in dashboard_app.chiamate_attive: if id_dash_rx not in dashboard_app.chiamate_attive:
dest_display = f"{sess.chiamante} (TG {tg_arrivo})" dest_display = f"{sess.chiamante} (TG {tg_arrivo})"
asyncio.create_task(dashboard_app.inizia_chiamata(id_dash_rx, "RX", src_id, dest_display)) dashboard_app.inizia_chiamata(id_dash_rx, "RX", src_id, dest_display)
else: else:
asyncio.create_task(dashboard_app.aggiorna_rx(id_dash_rx)) dashboard_app.aggiorna_rx(id_dash_rx)
if sess.ultimo_src_id != src_id: if sess.ultimo_src_id != src_id:
sess.ultimo_src_id = src_id sess.ultimo_src_id = src_id
@@ -540,9 +540,7 @@ class SipProtocol(asyncio.DatagramProtocol):
def connection_made(self, transport): self.manager.sip_transport = transport def connection_made(self, transport): self.manager.sip_transport = transport
def datagram_received(self, data, addr): def datagram_received(self, data, addr):
if addr[0] != FREEPBX_IP and FREEPBX_IP != "0.0.0.0": return if addr[0] != FREEPBX_IP and FREEPBX_IP != "0.0.0.0": return
msg = data.decode('utf-8', errors='ignore') msg = data.decode('utf-8', errors='ignore').lstrip()
# FIX: splitlines() digerisce correttamente sia \r\n che \n
lines = msg.splitlines() lines = msg.splitlines()
headers = {line.split(":", 1)[0].strip().upper(): line.split(":", 1)[1].strip() for line in lines[1:] if ":" in line} headers = {line.split(":", 1)[0].strip().upper(): line.split(":", 1)[1].strip() for line in lines[1:] if ":" in line}
call_id = headers.get('CALL-ID', '') call_id = headers.get('CALL-ID', '')
@@ -585,7 +583,6 @@ class SipProtocol(asyncio.DatagramProtocol):
f"Max-Forwards: 70\r\nFrom: {headers.get('FROM', '')}\r\nTo: {headers.get('TO', '')}\r\n" f"Max-Forwards: 70\r\nFrom: {headers.get('FROM', '')}\r\nTo: {headers.get('TO', '')}\r\n"
f"Call-ID: {call_id}\r\nCSeq: {headers.get('CSEQ', '').split()[0]} ACK\r\nContent-Length: 0\r\n\r\n") f"Call-ID: {call_id}\r\nCSeq: {headers.get('CSEQ', '').split()[0]} ACK\r\nContent-Length: 0\r\n\r\n")
self.manager.sip_transport.sendto(ack.encode('utf-8'), addr) self.manager.sip_transport.sendto(ack.encode('utf-8'), addr)
# FIX: Aggiunto supporto al CANCEL e chiusura forzata in dashboard
elif msg.startswith("BYE") or msg.startswith("CANCEL"): elif msg.startswith("BYE") or msg.startswith("CANCEL"):
self.manager.termina_chiamata(call_id) self.manager.termina_chiamata(call_id)
resp = (f"SIP/2.0 200 OK\r\nVia: {headers.get('VIA', '')}\r\nFrom: {headers.get('FROM', '')}\r\nTo: {headers.get('TO', '')}\r\n" resp = (f"SIP/2.0 200 OK\r\nVia: {headers.get('VIA', '')}\r\nFrom: {headers.get('FROM', '')}\r\nTo: {headers.get('TO', '')}\r\n"