Pantalla TV en ventana de PC: despertador SSE, y el SSE estaba mudo
El televisor no es un equipo dedicado: es una ventana de un computador de uso mixto. Si esa ventana queda tapada o minimizada, Chrome congela sus relojes a ~1 tic por minuto, el sondeo se paraliza y los llamados intermedios no suenan (reproducido y documentado en la bitácora el 23/08 a las 12:11). Los eventos de red no se congelan. La pantalla se suscribe ahora al canal SSE que ya existía —todos los caminos de llamado actualizan sse_ping_at: llamar, rellamar, cambiar_estado y llamar_desde_espera— y cada cola_update dispara un refresco inmediato, aunque la pestaña esté de fondo. El sondeo de 1 s queda de respaldo, y el vigía de congelamiento anota en la bitácora si ambos fallan. Al verificar en producción (sin dar por hecho que el canal servía), el SSE resultó estar MUDO: cero bytes en 17 segundos, ni el keep-alive. Causa: el arranque del ERP deja un búfer de salida activo —los jsonOk() de _helpers hacen ob_clean(), que lo confirma— y sse_turno.php usaba flush() a secas, que no lo atraviesa: todo lo emitido quedaba atrapado. Se drena el búfer al iniciar el stream (ob_end_clean + ob_implicit_flush). Verificado ejecutándolo contra la base real: emite cola_update de inmediato. La suite vigila que la suscripción exista (17 verificaciones de voz). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
95547bc4f3
commit
c9b3ebc179
@@ -32,6 +32,13 @@ header('Cache-Control: no-store, no-cache');
|
|||||||
header('X-Accel-Buffering: no'); // Nginx: deshabilitar buffering
|
header('X-Accel-Buffering: no'); // Nginx: deshabilitar buffering
|
||||||
header('Connection: keep-alive');
|
header('Connection: keep-alive');
|
||||||
|
|
||||||
|
// El arranque del ERP deja un búfer de salida activo (los jsonOk() de
|
||||||
|
// _helpers hacen ob_clean(), que lo confirma). flush() a secas no lo
|
||||||
|
// atraviesa: TODO lo emitido quedaba atrapado y el cliente recibía cero
|
||||||
|
// bytes, para siempre. Por eso este endpoint parecía muerto en producción.
|
||||||
|
while (ob_get_level() > 0) { @ob_end_clean(); }
|
||||||
|
@ob_implicit_flush(true);
|
||||||
|
|
||||||
// ── Parámetros ────────────────────────────────────────────────
|
// ── Parámetros ────────────────────────────────────────────────
|
||||||
$area = isset($_GET['area']) ? trim($_GET['area']) : 'recepcion';
|
$area = isset($_GET['area']) ? trim($_GET['area']) : 'recepcion';
|
||||||
$lugarId = isset($_GET['lugar_id']) ? (int) $_GET['lugar_id'] : null;
|
$lugarId = isset($_GET['lugar_id']) ? (int) $_GET['lugar_id'] : null;
|
||||||
|
|||||||
@@ -1042,6 +1042,23 @@ cargarSnapshot();
|
|||||||
// 1 s en vez de 2: el llamado salía con hasta 2 s de retraso desde el clic
|
// 1 s en vez de 2: el llamado salía con hasta 2 s de retraso desde el clic
|
||||||
setInterval(cargarSnapshot, 1000);
|
setInterval(cargarSnapshot, 1000);
|
||||||
|
|
||||||
|
// ── Despertador SSE ──
|
||||||
|
// Esta pantalla corre en la ventana de un computador de uso mixto: puede
|
||||||
|
// quedar tapada o minimizada, y Chrome congela los relojes de las pestañas
|
||||||
|
// ocultas (~1 tic/min). El sondeo de arriba se paraliza y los llamados no
|
||||||
|
// suenan. Los eventos de red NO se congelan: el servidor emite cola_update en
|
||||||
|
// cada llamado y rellamado (sse_ping_at), y ese mensaje despierta la pantalla
|
||||||
|
// aunque esté de fondo. El sondeo queda como respaldo si el SSE se cae; si
|
||||||
|
// ambos fallan, el vigía de congelamiento lo deja anotado en la bitácora.
|
||||||
|
(function conectarSSE() {
|
||||||
|
if (!('EventSource' in window)) return;
|
||||||
|
try {
|
||||||
|
const es = new EventSource(BASE_API + 'sse_turno.php?area=recepcion');
|
||||||
|
es.addEventListener('cola_update', () => { try { cargarSnapshot(); } catch (_) {} });
|
||||||
|
// EventSource se reconecta solo tras un error; no hay nada que hacer aquí
|
||||||
|
} catch (_) {}
|
||||||
|
})();
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', () => { if (!document.hidden) cargarSnapshot(); });
|
document.addEventListener('visibilitychange', () => { if (!document.hidden) cargarSnapshot(); });
|
||||||
window.addEventListener('focus', cargarSnapshot);
|
window.addEventListener('focus', cargarSnapshot);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ ok('el código extraído contiene el anclaje anti-recolector', cuerpo.includes('
|
|||||||
ok('resume() se llama antes de cada speak()',
|
ok('resume() se llama antes de cada speak()',
|
||||||
/resume\(\);\s*\n\s*window\.speechSynthesis\.speak\(utt\)/.test(cuerpo));
|
/resume\(\);\s*\n\s*window\.speechSynthesis\.speak\(utt\)/.test(cuerpo));
|
||||||
|
|
||||||
|
ok('el despertador SSE está conectado (los llamados despiertan a la pestaña oculta)',
|
||||||
|
js.includes("sse_turno.php?area=recepcion") && js.includes("addEventListener('cola_update'"));
|
||||||
|
|
||||||
// ── Armazón de simulación ───────────────────────────────────────────────
|
// ── Armazón de simulación ───────────────────────────────────────────────
|
||||||
function escenario({ bloqueado = false, msArranque = 20, caida = false } = {}) {
|
function escenario({ bloqueado = false, msArranque = 20, caida = false } = {}) {
|
||||||
let t = 0; const cola = [];
|
let t = 0; const cola = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user