── Voz que a veces no suena o se corta ── Dos causas nuevas, ambas intermitentes por naturaleza: 1. Defecto conocido de Chrome: si nada referencia la SpeechSynthesisUtterance, el recolector de basura puede llevársela A MITAD DE FRASE. El audio se corta en seco y 'end' no llega. Depende de cuándo pase el recolector, así que nunca se reproduce a voluntad. Cada locución queda anclada en un Set hasta terminar (_uttAncladas). 2. Tras un cancel(), en Windows y ChromeOS el motor puede quedar en pausa: speak() encola y jamás suena, sin error alguno. resume() antes de cada speak() lo destranca, y sobre un motor sano no hace nada. Y porque lo intermitente no se depura mirando la pantalla, queda una bitácora: cada intento de hablar registra voz elegida, si es local, cuánto tardó en arrancar y cómo terminó — en localStorage.tvVozLog (últimos 200) y en la tabla turnero_tv_log vía sendBeacon (endpoint log_tv.php, insert-only, eventos de lista cerrada). La próxima vez que reporten "ayer a las 10 no sonó", se consulta la tabla y se ve qué pasó exactamente. ── Servicios de RIPS que "se demoran" ── Medido en datos, no en hipótesis: la facturación en el sistema del laboratorio ocurre MIENTRAS la recepcionista atiende. La consulta a RIPS se hacía UNA sola vez, al vincular al paciente — y en 7 días, 547 de 549 registros llegaron DESPUÉS de esa única consulta. Nadie volvía a preguntar: eso es lo que se percibía como lentitud de RIPS. (El scheduler en sí tarda segundos; de paso: el reloj del equipo legado está ~1 hora atrasado, visible en hora_recepcion.) Ahora la ficha sondea cada 10 s hasta 10 minutos y se detiene al encontrar, al limpiar la ficha o al agotar los intentos. Si la recepcionista ya marcó exámenes a mano, no se le pisan: se le ofrece el banner en vez de autocargar. ── Pruebas (22, todas contra el código real extraído de las vistas) ── scripts/test_tv_voz.js 9 anclaje GC, resume(), bloqueo, voz lenta, bitácora scripts/test_rips_sondeo.js 7 reintentos, paradas, tope, sin duplicar scripts/test_rips_ventana.php 6 ventana SQL en tabla TEMPORARY, sin tocar datos Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
4.1 KiB
JavaScript
104 lines
4.1 KiB
JavaScript
/**
|
|
* scripts/test_rips_sondeo.js — node scripts/test_rips_sondeo.js
|
|
*
|
|
* Pruebas del sondeo RIPS contra el código real de recepcion.php.
|
|
*
|
|
* El defecto que esto vigila: la consulta a RIPS se hacía UNA vez al vincular
|
|
* al paciente, y en 7 días 547 de 549 registros llegaron después de esa única
|
|
* consulta. El sondeo reintenta cada 10 s hasta 10 minutos y debe detenerse
|
|
* al encontrar, al limpiar la ficha, o al agotar los intentos.
|
|
*/
|
|
const fs = require('fs');
|
|
let fallos = 0;
|
|
const ok = (d, c) => { console.log((c ? ' ok ' : ' FALLA ') + d); if (!c) fallos++; };
|
|
|
|
const src = fs.readFileSync(__dirname + '/../modules/turnero/views/recepcion.php', 'utf8')
|
|
.replace(/<\?(php|=)[\s\S]*?\?>/g, '"PHP"');
|
|
|
|
// Se extraen exactamente las tres piezas del control de sondeo
|
|
const m = src.match(/let _ripsSondeoId[\s\S]*?function detenerSondeoRips\(\) \{[\s\S]*?\n\}/);
|
|
if (!m) { console.log(' FALLA no se encontró el bloque de sondeo'); process.exit(1); }
|
|
|
|
function armar() {
|
|
let consultas = 0;
|
|
const intervalos = new Map();
|
|
let proximoId = 1;
|
|
const setIntervalFake = (fn, ms) => { const id = proximoId++; intervalos.set(id, { fn, ms }); return id; };
|
|
const clearIntervalFake = (id) => intervalos.delete(id);
|
|
|
|
const api = new Function(
|
|
'setInterval', 'clearInterval', 'INTERVALOS', 'CONTADOR',
|
|
'let _ripsData = null;' +
|
|
'const consultarExamenesRips = (c) => { CONTADOR.n++; };' +
|
|
m[0] + `;
|
|
return {
|
|
iniciar: iniciarSondeoRips,
|
|
detener: detenerSondeoRips,
|
|
tic() { for (const v of [...INTERVALOS.values()]) v.fn(); },
|
|
activo() { return _ripsSondeoId !== null; },
|
|
encontrado() { _ripsData = { encontrados: [1] }; },
|
|
};`
|
|
)(setIntervalFake, clearIntervalFake, intervalos, (globalThis.__c = { n: 0 }));
|
|
return { api, contador: globalThis.__c, intervalos };
|
|
}
|
|
|
|
// ── 1. Consulta de inmediato y reintenta ────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
ok('consulta de inmediato al vincular', contador.n === 1);
|
|
api.tic(); api.tic(); api.tic();
|
|
ok('reintenta en cada ciclo mientras no encuentra', contador.n === 4);
|
|
}
|
|
|
|
// ── 2. Al encontrar, deja de sondear ────────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
api.tic();
|
|
api.encontrado();
|
|
api.tic(); // este ciclo detecta _ripsData y se apaga
|
|
const n = contador.n;
|
|
api.tic(); api.tic();
|
|
ok('al encontrar los exámenes se apaga y no consulta más',
|
|
contador.n === n && !api.activo());
|
|
}
|
|
|
|
// ── 3. Limpiar la ficha lo detiene ──────────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
api.detener();
|
|
const n = contador.n;
|
|
api.tic(); api.tic();
|
|
ok('limpiar la ficha detiene el sondeo en seco', contador.n === n && !api.activo());
|
|
}
|
|
|
|
// ── 4. Tope de intentos: no sondea para siempre ─────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
for (let i = 0; i < 80; i++) api.tic();
|
|
ok('se rinde tras ~60 intentos (10 min), no queda sondeando eternamente',
|
|
contador.n <= 61 && !api.activo());
|
|
}
|
|
|
|
// ── 5. Vincular a otro paciente reinicia sin duplicar intervalos ────────
|
|
{
|
|
const { api, contador, intervalos } = armar();
|
|
api.iniciar('11111111');
|
|
api.iniciar('22222222');
|
|
ok('re-vincular no deja dos sondeos corriendo a la vez', intervalos.size === 1);
|
|
}
|
|
|
|
// ── 6. Sin cédula no arranca ────────────────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('');
|
|
ok('sin cédula no consulta ni deja intervalos', contador.n === 0 && !api.activo());
|
|
}
|
|
|
|
console.log();
|
|
console.log(fallos === 0 ? 'Todo correcto.' : fallos + ' pruebas fallaron.');
|
|
process.exit(fallos === 0 ? 0 : 1);
|