#15 Send-Queue: Meshtastic-konformer Nachrichtenpuffer mit dynamischem Intervall #15

Open
opened 2026-02-21 08:53:32 +01:00 by ppfeiffer · 0 comments
Owner

Ziel

Alle ausgehenden Nachrichten laufen durch eine zentrale asyncio.Queue. Ein einziger Worker-Task sendet sequenziell und passt den Sendeabstand dynamisch an die gemessene Airtime (airUtilTx) des Geräts an.

Probleme im aktuellen Code (_send_text, bot.py:452)

  • Mehrere gleichzeitige Aufrufer (Scheduler, Web-API, MQTT-Subscriber, Bot-Befehle) können parallel senden – kein Mutex
  • 3 s Pause zwischen Teilen schützt nicht gegen konkurrierende Aufrufe
  • DB-Eintrag erfolgt vor sendText() – Nachricht steht als gesendet da, auch wenn sie nie übertragen wurde
  • Kein Rate-Limiting gegenüber dem LoRa-Funk

Architektur

 Scheduler ──►
 Web-API ────►  send_queue (asyncio.Queue, FIFO)  ──►  _send_worker  ──►  sendText()
 MQTT Sub ───►                                                 ↓
 Befehle ────►                                     DB + WS-Broadcast
                                               (nur nach erfolgreichem Send)

Entscheidungen

Thema Entscheidung
Queue voll Älteste Nachricht verdrängen (neuere Infos haben Vorrang)
Split-Fehler (Teil 2/3 schlägt fehl) Weitermachen – restliche Teile werden trotzdem versucht
DB-Eintrag bei Fehler Kein Eintrag – nur erfolgreiche Aussendungen
Sendeabstand Dynamisch aus airUtilTx berechnet

Dynamisches Intervall

Referenzwert: 2,5 % airUtilTx (gemessene lokale Netzauslastung).

interval = base + (airUtilTx / 2.5) * (max - base)  [gedeckelt auf max]

airUtilTx  →  Wartezeit
─────────────────────────
  0.0 %   →   5 s
  0.6 %   →  13 s
  1.25 %  →  22 s
  2.0 %   →  33 s
  2.5 %   →  40 s  (lokales Maximum)

Der zuletzt empfangene airUtilTx-Wert wird bereits aus dem Telemetrie-Stream gelesen (TELEMETRY_APP, bot.py:277) – er muss nur als self._air_util_tx: float im Bot gespeichert werden.

Konfiguration (config.yaml)

bot:
  send_interval_base: 5.0     # Sekunden bei 0 % Airtime
  send_interval_max: 40.0     # Sekunden bei >= 2.5 % Airtime
  send_interval_ref: 2.5      # Referenz-Airtime in % (lokales Netz)
  send_queue_size: 50         # Max. gepufferte Nachrichten

Aufgaben

  • self._send_queue, self._last_sent_at, self._air_util_tx in __init__ ergänzen
  • _send_worker()-Task implementieren (sequenzieller Loop, Queue-Drain)
  • _drain_item(): dynamisches Intervall berechnen, sendText() aufrufen, dann DB/Broadcast
  • Queue-voll-Logik: älteste Nachricht verdrängen (get_nowait + put_nowait)
  • airUtilTx aus Telemetrie-Handler in self._air_util_tx schreiben
  • send_message() auf put_nowait-Logik umstellen (kein await mehr nötig)
  • Worker-Task in main.py starten und bei Shutdown sauber canceln
  • config/config.example.yaml um bot.send_interval_*-Felder ergänzen

Akzeptanzkriterium

  • Scheduler + Web-API + Bot-Befehl gleichzeitig → keine überlappenden Sendungen
  • Airtime steigt → Intervall wächst automatisch
  • Fehlgeschlagene Sends hinterlassen keinen DB-Eintrag
  • Queue-Größe bleibt konstant bei Last (älteste Einträge fliegen raus)
## Ziel Alle ausgehenden Nachrichten laufen durch eine zentrale `asyncio.Queue`. Ein einziger Worker-Task sendet sequenziell und passt den Sendeabstand dynamisch an die gemessene Airtime (`airUtilTx`) des Geräts an. ## Probleme im aktuellen Code (`_send_text`, bot.py:452) - Mehrere gleichzeitige Aufrufer (Scheduler, Web-API, MQTT-Subscriber, Bot-Befehle) können parallel senden – kein Mutex - 3 s Pause zwischen Teilen schützt nicht gegen konkurrierende Aufrufe - DB-Eintrag erfolgt **vor** `sendText()` – Nachricht steht als gesendet da, auch wenn sie nie übertragen wurde - Kein Rate-Limiting gegenüber dem LoRa-Funk ## Architektur ``` Scheduler ──► Web-API ────► send_queue (asyncio.Queue, FIFO) ──► _send_worker ──► sendText() MQTT Sub ───► ↓ Befehle ────► DB + WS-Broadcast (nur nach erfolgreichem Send) ``` ## Entscheidungen | Thema | Entscheidung | |---|---| | Queue voll | Älteste Nachricht verdrängen (neuere Infos haben Vorrang) | | Split-Fehler (Teil 2/3 schlägt fehl) | Weitermachen – restliche Teile werden trotzdem versucht | | DB-Eintrag bei Fehler | Kein Eintrag – nur erfolgreiche Aussendungen | | Sendeabstand | Dynamisch aus `airUtilTx` berechnet | ## Dynamisches Intervall Referenzwert: **2,5 % airUtilTx** (gemessene lokale Netzauslastung). ``` interval = base + (airUtilTx / 2.5) * (max - base) [gedeckelt auf max] airUtilTx → Wartezeit ───────────────────────── 0.0 % → 5 s 0.6 % → 13 s 1.25 % → 22 s 2.0 % → 33 s 2.5 % → 40 s (lokales Maximum) ``` Der zuletzt empfangene `airUtilTx`-Wert wird bereits aus dem Telemetrie-Stream gelesen (TELEMETRY_APP, bot.py:277) – er muss nur als `self._air_util_tx: float` im Bot gespeichert werden. ## Konfiguration (config.yaml) ```yaml bot: send_interval_base: 5.0 # Sekunden bei 0 % Airtime send_interval_max: 40.0 # Sekunden bei >= 2.5 % Airtime send_interval_ref: 2.5 # Referenz-Airtime in % (lokales Netz) send_queue_size: 50 # Max. gepufferte Nachrichten ``` ## Aufgaben - [ ] `self._send_queue`, `self._last_sent_at`, `self._air_util_tx` in `__init__` ergänzen - [ ] `_send_worker()`-Task implementieren (sequenzieller Loop, Queue-Drain) - [ ] `_drain_item()`: dynamisches Intervall berechnen, `sendText()` aufrufen, dann DB/Broadcast - [ ] Queue-voll-Logik: älteste Nachricht verdrängen (`get_nowait` + `put_nowait`) - [ ] `airUtilTx` aus Telemetrie-Handler in `self._air_util_tx` schreiben - [ ] `send_message()` auf `put_nowait`-Logik umstellen (kein `await` mehr nötig) - [ ] Worker-Task in `main.py` starten und bei Shutdown sauber canceln - [ ] `config/config.example.yaml` um `bot.send_interval_*`-Felder ergänzen ## Akzeptanzkriterium - Scheduler + Web-API + Bot-Befehl gleichzeitig → keine überlappenden Sendungen - Airtime steigt → Intervall wächst automatisch - Fehlgeschlagene Sends hinterlassen keinen DB-Eintrag - Queue-Größe bleibt konstant bei Last (älteste Einträge fliegen raus)
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ppfeiffer/MeshDD-Bot#15
No description provided.