diff --git a/modules/turnero/api/sse_turno.php b/modules/turnero/api/sse_turno.php index 713f09e..b1761ab 100644 --- a/modules/turnero/api/sse_turno.php +++ b/modules/turnero/api/sse_turno.php @@ -32,6 +32,13 @@ header('Cache-Control: no-store, no-cache'); header('X-Accel-Buffering: no'); // Nginx: deshabilitar buffering 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 ──────────────────────────────────────────────── $area = isset($_GET['area']) ? trim($_GET['area']) : 'recepcion'; $lugarId = isset($_GET['lugar_id']) ? (int) $_GET['lugar_id'] : null; diff --git a/modules/turnero/views/display_global.php b/modules/turnero/views/display_global.php index 70214c2..1ea176a 100644 --- a/modules/turnero/views/display_global.php +++ b/modules/turnero/views/display_global.php @@ -1042,6 +1042,23 @@ cargarSnapshot(); // 1 s en vez de 2: el llamado salía con hasta 2 s de retraso desde el clic 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(); }); window.addEventListener('focus', cargarSnapshot); diff --git a/scripts/test_tv_voz.js b/scripts/test_tv_voz.js index dc3d14e..a952606 100644 --- a/scripts/test_tv_voz.js +++ b/scripts/test_tv_voz.js @@ -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()', /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 ─────────────────────────────────────────────── function escenario({ bloqueado = false, msArranque = 20, caida = false } = {}) { let t = 0; const cola = [];