mirror of
https://github.com/smittix/intercept.git
synced 2026-05-29 21:59:27 -07:00
Fix APRS crash on large station count and station list overflow
- Fix infinite loop in updateAprsStationList: querySelectorAll returns a static NodeList so the while(cards.length > 50) loop never terminated, crashing the page. Use live childElementCount instead. - Fix station list pushing map off-screen by adding overflow:hidden and min-height:0 to flex containers so only the station list scrolls. - Cap backend aprs_stations dict at 500 entries with oldest-eviction to prevent unbounded memory growth. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,8 @@ from flask import Blueprint, jsonify, request, Response
|
||||
import app as app_module
|
||||
from utils.logging import sensor_logger as logger
|
||||
from utils.validation import validate_device_index, validate_gain, validate_ppm
|
||||
from utils.sse import format_sse
|
||||
from utils.event_pipeline import process_event
|
||||
from utils.sse import format_sse
|
||||
from utils.event_pipeline import process_event
|
||||
from utils.constants import (
|
||||
PROCESS_TERMINATE_TIMEOUT,
|
||||
SSE_KEEPALIVE_INTERVAL,
|
||||
@@ -53,6 +53,7 @@ aprs_packet_count = 0
|
||||
aprs_station_count = 0
|
||||
aprs_last_packet_time = None
|
||||
aprs_stations = {} # callsign -> station data
|
||||
APRS_MAX_STATIONS = 500 # Limit tracked stations to prevent memory growth
|
||||
|
||||
# Meter rate limiting
|
||||
_last_meter_time = 0.0
|
||||
@@ -1371,6 +1372,13 @@ def stream_aprs_output(rtl_process: subprocess.Popen, decoder_process: subproces
|
||||
'last_seen': packet.get('timestamp'),
|
||||
'packet_type': packet.get('packet_type'),
|
||||
}
|
||||
# Evict oldest stations when limit is exceeded
|
||||
if len(aprs_stations) > APRS_MAX_STATIONS:
|
||||
oldest = min(
|
||||
aprs_stations,
|
||||
key=lambda k: aprs_stations[k].get('last_seen', ''),
|
||||
)
|
||||
del aprs_stations[oldest]
|
||||
|
||||
app_module.aprs_queue.put(packet)
|
||||
|
||||
@@ -1726,13 +1734,13 @@ def stream_aprs() -> Response:
|
||||
|
||||
while True:
|
||||
try:
|
||||
msg = app_module.aprs_queue.get(timeout=SSE_QUEUE_TIMEOUT)
|
||||
last_keepalive = time.time()
|
||||
try:
|
||||
process_event('aprs', msg, msg.get('type'))
|
||||
except Exception:
|
||||
pass
|
||||
yield format_sse(msg)
|
||||
msg = app_module.aprs_queue.get(timeout=SSE_QUEUE_TIMEOUT)
|
||||
last_keepalive = time.time()
|
||||
try:
|
||||
process_event('aprs', msg, msg.get('type'))
|
||||
except Exception:
|
||||
pass
|
||||
yield format_sse(msg)
|
||||
except queue.Empty:
|
||||
now = time.time()
|
||||
if now - last_keepalive >= SSE_KEEPALIVE_INTERVAL:
|
||||
|
||||
@@ -905,7 +905,7 @@
|
||||
</div>
|
||||
|
||||
<!-- APRS Visualizations -->
|
||||
<div id="aprsVisuals" style="display: none; flex-direction: column; gap: 10px; flex: 1; padding: 10px;">
|
||||
<div id="aprsVisuals" style="display: none; flex-direction: column; gap: 10px; flex: 1; padding: 10px; overflow: hidden; min-height: 0;">
|
||||
<!-- APRS Function Bar -->
|
||||
<div class="aprs-strip">
|
||||
<div class="aprs-strip-inner">
|
||||
@@ -980,7 +980,7 @@
|
||||
<div style="display: flex; gap: 10px; flex: 1; min-height: 0;">
|
||||
<!-- Map Panel (larger) -->
|
||||
<div class="wifi-visual-panel"
|
||||
style="flex: 2; display: flex; flex-direction: column; min-width: 0;">
|
||||
style="flex: 2; display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden;">
|
||||
<h5
|
||||
style="color: var(--accent-cyan); text-shadow: 0 0 10px var(--accent-cyan); padding: 0 10px; margin-bottom: 8px;">
|
||||
APRS STATION MAP</h5>
|
||||
@@ -999,12 +999,12 @@
|
||||
|
||||
<!-- Station List Panel -->
|
||||
<div class="wifi-visual-panel"
|
||||
style="flex: 1; min-width: 300px; max-width: 400px; display: flex; flex-direction: column;">
|
||||
style="flex: 1; min-width: 300px; max-width: 400px; display: flex; flex-direction: column; min-height: 0; overflow: hidden;">
|
||||
<h5
|
||||
style="color: var(--accent-green); text-shadow: 0 0 10px var(--accent-green); margin-bottom: 8px;">
|
||||
style="color: var(--accent-green); text-shadow: 0 0 10px var(--accent-green); margin-bottom: 8px; flex-shrink: 0;">
|
||||
STATION LIST</h5>
|
||||
<div id="aprsFilterBarContainer"></div>
|
||||
<div id="aprsStationList" class="signal-cards-container" style="flex: 1; overflow-y: auto; font-size: 11px; gap: 8px;">
|
||||
<div id="aprsFilterBarContainer" style="flex-shrink: 0;"></div>
|
||||
<div id="aprsStationList" class="signal-cards-container" style="flex: 1; overflow-y: auto; font-size: 11px; gap: 8px; min-height: 0;">
|
||||
<div class="signal-cards-placeholder" style="padding: 20px; text-align: center; color: var(--text-muted);">
|
||||
No stations received yet
|
||||
</div>
|
||||
@@ -1013,7 +1013,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Bottom row: Packet Log -->
|
||||
<div class="wifi-visual-panel" style="display: flex; flex-direction: column; max-height: 200px;">
|
||||
<div class="wifi-visual-panel" style="display: flex; flex-direction: column; max-height: 200px; flex-shrink: 0;">
|
||||
<h5
|
||||
style="color: var(--accent-orange); text-shadow: 0 0 10px var(--accent-orange); margin-bottom: 8px;">
|
||||
PACKET LOG</h5>
|
||||
@@ -9353,10 +9353,10 @@
|
||||
listEl.insertBefore(newCard, listEl.firstChild);
|
||||
}
|
||||
|
||||
// Keep list manageable
|
||||
const cards = listEl.querySelectorAll('.signal-card');
|
||||
while (cards.length > 50) {
|
||||
listEl.removeChild(listEl.lastChild);
|
||||
// Keep list manageable (use live childElementCount, not static NodeList)
|
||||
const MAX_APRS_STATION_CARDS = 200;
|
||||
while (listEl.childElementCount > MAX_APRS_STATION_CARDS && listEl.lastElementChild) {
|
||||
listEl.removeChild(listEl.lastElementChild);
|
||||
}
|
||||
|
||||
// Update filter counts if filter bar exists
|
||||
|
||||
Reference in New Issue
Block a user