mirror of
https://github.com/smittix/intercept.git
synced 2026-07-28 10:38:12 -07:00
Fix WeFax start/stop/SSE reliability
- Replace blocking stdout.read() with select()-based non-blocking reads so the decode thread responds to stop within 0.5s - Make stop() non-blocking by releasing the lock before terminating the process and removing the redundant wait() - Move initial scanning SSE event from start() into the decode thread so it fires after the frontend EventSource connects - Update frontend stop() to give immediate UI feedback before the fetch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -205,16 +205,20 @@ var WeFax = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stop() {
|
function stop() {
|
||||||
|
// Immediate UI feedback before waiting for backend response
|
||||||
|
state.running = false;
|
||||||
|
updateButtons(false);
|
||||||
|
setStatus('Stopping...');
|
||||||
|
disconnectSSE();
|
||||||
|
|
||||||
fetch('/wefax/stop', { method: 'POST' })
|
fetch('/wefax/stop', { method: 'POST' })
|
||||||
.then(function (r) { return r.json(); })
|
.then(function (r) { return r.json(); })
|
||||||
.then(function () {
|
.then(function () {
|
||||||
state.running = false;
|
|
||||||
updateButtons(false);
|
|
||||||
setStatus('Stopped');
|
setStatus('Stopped');
|
||||||
disconnectSSE();
|
|
||||||
loadImages();
|
loadImages();
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
|
setStatus('Stopped');
|
||||||
console.error('WeFax stop error:', err);
|
console.error('WeFax stop error:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-16
@@ -15,6 +15,8 @@ import base64
|
|||||||
import contextlib
|
import contextlib
|
||||||
import io
|
import io
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
|
import select
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@@ -297,11 +299,6 @@ class WeFaxDecoder:
|
|||||||
f"WeFax decoder started: {frequency_khz} kHz, "
|
f"WeFax decoder started: {frequency_khz} kHz, "
|
||||||
f"station={station}, IOC={ioc}, LPM={lpm}"
|
f"station={station}, IOC={ioc}, LPM={lpm}"
|
||||||
)
|
)
|
||||||
self._emit_progress(WeFaxProgress(
|
|
||||||
status='scanning',
|
|
||||||
station=station,
|
|
||||||
message=f'Scanning {frequency_khz} kHz for WeFax start tone...',
|
|
||||||
))
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -395,9 +392,29 @@ class WeFaxDecoder:
|
|||||||
f"samples/line={samples_per_line}"
|
f"samples/line={samples_per_line}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Emit initial scanning progress here (not in start()) so the
|
||||||
|
# frontend SSE connection is established before this event fires.
|
||||||
|
time.sleep(0.1)
|
||||||
|
self._emit_progress(WeFaxProgress(
|
||||||
|
status='scanning',
|
||||||
|
station=self._station,
|
||||||
|
message=f'Scanning {self._frequency_khz} kHz for WeFax start tone...',
|
||||||
|
))
|
||||||
|
|
||||||
while self._running and self._rtl_process:
|
while self._running and self._rtl_process:
|
||||||
try:
|
try:
|
||||||
raw_data = self._rtl_process.stdout.read(chunk_bytes)
|
proc = self._rtl_process
|
||||||
|
if not proc or not proc.stdout:
|
||||||
|
break
|
||||||
|
# Non-blocking read via select() — allows checking _running
|
||||||
|
# on timeout instead of blocking indefinitely in read().
|
||||||
|
fd = proc.stdout.fileno()
|
||||||
|
ready, _, _ = select.select([fd], [], [], 0.5)
|
||||||
|
if not ready:
|
||||||
|
if not self._running:
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
raw_data = os.read(fd, chunk_bytes)
|
||||||
if not raw_data:
|
if not raw_data:
|
||||||
if self._running:
|
if self._running:
|
||||||
stderr_msg = ''
|
stderr_msg = ''
|
||||||
@@ -667,20 +684,22 @@ class WeFaxDecoder:
|
|||||||
))
|
))
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""Stop WeFax decoder."""
|
"""Stop WeFax decoder.
|
||||||
|
|
||||||
|
Non-blocking: sets _running=False and terminates the process,
|
||||||
|
then returns immediately. The decode thread handles cleanup
|
||||||
|
when its read() returns empty.
|
||||||
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._running = False
|
self._running = False
|
||||||
|
proc = self._rtl_process
|
||||||
|
self._rtl_process = None
|
||||||
|
|
||||||
if self._rtl_process:
|
if proc:
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
self._rtl_process.terminate()
|
proc.terminate()
|
||||||
self._rtl_process.wait(timeout=5)
|
|
||||||
except Exception:
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
self._rtl_process.kill()
|
|
||||||
self._rtl_process = None
|
|
||||||
|
|
||||||
logger.info("WeFax decoder stopped")
|
logger.info("WeFax decoder stopped")
|
||||||
|
|
||||||
def get_images(self) -> list[WeFaxImage]:
|
def get_images(self) -> list[WeFaxImage]:
|
||||||
"""Get list of decoded images."""
|
"""Get list of decoded images."""
|
||||||
|
|||||||
Reference in New Issue
Block a user