Acta de prueba de módem en batería (tablas de registro); tipo 'tabla' en el motor de actas; procedimiento 0.5.3

This commit is contained in:
Leopoldo Rodríguez 2026-09-25 14:43:34 -06:00
parent 0c90b517ba
commit 7561b5177a
8 changed files with 174 additions and 9 deletions

View File

@ -54,7 +54,7 @@ El backend la deduce sola de `ACTAS_DIR`; si prefieres fijarla, agrega a
Environment=ADJUNTOS_DIR=/srv/projects/drogheda/adjuntos
Environment=ADJUNTO_MAX_MB=8
Environment=ARCHIVO_MAX_MB=20
Environment=ACTAS_REPETIBLES=diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion
Environment=ACTAS_REPETIBLES=diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion,prueba-bateria-modem
```
### 4 · nginx — subida de fotos e identidad del técnico (una sola vez)

View File

@ -10,7 +10,7 @@ Environment=ACTAS_DIR=/srv/projects/drogheda/actas
Environment=ADJUNTOS_DIR=/srv/projects/drogheda/adjuntos
Environment=ADJUNTO_MAX_MB=8
Environment=ARCHIVO_MAX_MB=20
Environment=ACTAS_REPETIBLES=diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion
Environment=ACTAS_REPETIBLES=diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion,prueba-bateria-modem
Environment=ACTAS_ADMINS=leo
Environment=HTPASSWD_FILE=/etc/nginx/hbox-docs.htpasswd
Environment=PASS_MIN=8

View File

@ -36,7 +36,7 @@ ADMINS = {u.strip().lower() for u in os.environ.get("ACTAS_ADMINS", "leo").split
TIPOS_UNICOS = {"ensamble"} # tipos en los que un componente solo puede usarse en un acta real
# Tipos REPETIBLES: una misma serie puede tener varias actas (cada visita a campo es un evento nuevo).
TIPOS_REPETIBLES = {t.strip() for t in os.environ.get(
"ACTAS_REPETIBLES", "diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion").split(",") if t.strip()}
"ACTAS_REPETIBLES", "diagnostico-campo,diagnostico-banco,instalacion-campo,reflasheo,dano,envio,recepcion,prueba-bateria-modem").split(",") if t.strip()}
# Subida de fotos
MAX_MB = int(os.environ.get("ADJUNTO_MAX_MB", "8"))

View File

@ -135,3 +135,13 @@ input[type=date]{width:100%;padding:11px 12px;border:1px solid var(--linea);bord
.marca.danada{display:block;background:var(--aviso-bg);border:2px solid var(--aviso);color:#7a4a00}
.marca.baja{display:block;background:var(--falla-bg);border:2px solid var(--falla);color:#8a1a1a}
.marca a{color:inherit;font-weight:700}
/* tabla de registro (tipo 'tabla') */
.tabla-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;margin-top:6px}
.tabla{border-collapse:collapse;font-size:13px;min-width:100%}
.tabla th,.tabla td{border:1px solid #ddd;padding:4px 5px;text-align:left;vertical-align:middle;white-space:nowrap}
.tabla thead th{background:#f3f3f3;font-weight:600;font-size:12px}
.tabla tbody th{background:#fafafa;font-weight:600;font-variant-numeric:tabular-nums}
.tabla input,.tabla select{width:100%;min-width:64px;font-size:14px;padding:6px 6px;border:1px solid #ccc;border-radius:6px;box-sizing:border-box}
.tabla input[inputmode=decimal]{max-width:90px}
.enviada .tabla input,.enviada .tabla select{background:#f6f6f6}

View File

@ -229,6 +229,39 @@
box.append(g);
} else if (it.tipo === 'nota') {
box.append($('div', { class: 'nota-item', html: it.txt }));
} else if (it.tipo === 'tabla') {
// Tabla de registro: filas × columnas de entradas cortas. Cada celda es un campo propio
// (clave `<id>__<fila>__<col>`), así que se guarda, se relee y se bloquea igual que todo lo demás.
box.classList.add('tabla-item');
if (it.txt) box.append($('label', { html: it.txt }));
if (it.sub) box.append($('div', { class: 'nota-item', html: it.sub }));
const scroll = $('div', { class: 'tabla-scroll' });
const t = $('table', { class: 'tabla' });
const thead = $('thead'); const hr = $('tr'); hr.append($('th', { text: it.etiquetaFilas || '' }));
(it.columnas || []).forEach(c => { const th = $('th', { html: c.txt }); if (c.u) th.append($('small', { text: ' ' + c.u })); hr.append(th); });
thead.append(hr); t.append(thead);
const tbody = $('tbody');
(it.filas || []).forEach(f => {
const fk = String(f).replace(/[^A-Za-z0-9]+/g, '-');
const tr = $('tr'); tr.append($('th', { text: f }));
(it.columnas || []).forEach(c => {
const cid = `${id}__${fk}__${c.k}`;
const lbl = `${it.txt ? String(it.txt).replace(/<[^>]+>/g, '') + ' · ' : ''}${f} · ${String(c.txt).replace(/<[^>]+>/g, '')}`;
const td = $('td');
if (c.opciones) {
const sel = $('select', { 'data-id': cid, 'data-lbl': lbl });
sel.append($('option', { value: '', text: '—' }));
c.opciones.forEach(o => sel.append($('option', { value: o, text: o })));
td.append(sel);
} else {
td.append($('input', { type: 'text', inputmode: c.num ? 'decimal' : 'text', 'data-id': cid, 'data-lbl': lbl,
placeholder: c.ph || '', style: c.ancho ? `width:${c.ancho}` : '' }));
}
tr.append(td);
});
tbody.append(tr);
});
t.append(tbody); scroll.append(t); box.append(scroll);
} else if (it.tipo === 'fecha') {
box.append($('label', { html: it.txt }));
const hoy = new Date(); hoy.setMinutes(hoy.getMinutes() - hoy.getTimezoneOffset());

View File

@ -0,0 +1,121 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prueba de banco — módem 4G en batería</title>
<link rel="stylesheet" href="acta.css">
</head>
<body>
<div id="acta"></div>
<script src="flota.js"></script>
<script src="series.js"></script>
<script src="acta.js"></script>
<script>
const FILAS = ['0:15','0:30','0:45','1:00','1:30','2:00'];
const COLS = [
{ k:'main5v', txt:'MAIN5V mín', u:'V', num:true, ph:'4.7' },
{ k:'sw5v', txt:'SW_5V mín', u:'V', num:true, ph:'4.9' },
{ k:'ledm', txt:'LEDs módem', opciones:['apagados','encienden y se apagan','parpadeo rápido','parpadeo lento','fijo'] },
{ k:'uart', txt:'UART simpleRTK2B', opciones:['lento ~1/s','rápido','sin parpadeo'] },
{ k:'k1', txt:'K1', opciones:['sin clic','clic (MODOB ON)','clic repetido'] },
{ k:'vbat', txt:'vbat / vbatmin', ph:'3.92 / 3.71', ancho:'110px' },
{ k:'srv', txt:'¿Servidor?', opciones:['nada','$PHLG 🔋','posición','posición + $PHLG'] },
];
const tabla = (clave, txt, sub) => ({ tipo:'tabla', clave, txt, sub, etiquetaFilas:'t', filas:FILAS, columnas:COLS });
const dioA = v => String(v.c2_resultado || '').startsWith('Sigue igual');
const modemOn = v => String(v.c1_reposo_modem || '').startsWith('Sigue');
Acta.render({
tipo: 'prueba-bateria-modem',
repetible: true,
tecnicoDesdeUsuario: true,
nombres: { alan: 'Alán Basurto', daniel: 'Daniel Martínez', leo: 'Leo Rodríguez' },
unidades: window.SERIES,
etiquetaSerie: 'Unidad bajo prueba',
titulo: 'Prueba de banco — módem 4G en modo batería',
subtitulo: 'Helgen Technologies · fw 0.5.3 · el procedimiento completo está en Procedimientos → 🔋 Prueba: módem 4G en batería.',
textoOK: 'PRUEBA COMPLETA',
intro: '<b>Qué se busca.</b> En batería la unidad enciende la etapa 2 min cada 10, pero el módem no llega a transmitir. '
+ 'Esta acta separa dos causas: <b>A</b> el riel del boost se hunde con la ráfaga LTE, o <b>B</b> el camino por K1 (Modo B). '
+ 'Las lecturas de multímetro son <b>obligatorias</b>: en batería el firmware no ve el riel (<code>v5</code> mide el 5 V del vehículo). '
+ 'Puntos: <b>MAIN5V</b> = U6 pin 1 / C32 / P11 pin 1 · <b>SW_5V</b> = MOD1 pin 5 / C3 · negro a GND. '
+ 'Consola HBOX a 115200 grabando a archivo; teclas <code>s</code> STAT · <code>v</code> puntos · <code>B</code> Modo B.',
secciones: [
{ titulo: '0 · Configuración',
items: [
{ tipo:'fecha', clave:'fecha', req:true, guardaEn:'fecha', txt:'Fecha' },
{ tipo:'radio', clave:'tarjeta', req:true, guardaEn:'clasificacion', txt:'Tarjeta', opciones:['V5','V4','V3'] },
{ tipo:'texto', clave:'fw', rows:'1', req:true, txt:'Firmware (primera línea de la consola)', ph:'hbox-v5-bringup 0.5.3 built …' },
{ tipo:'texto', clave:'bateria', rows:'1', req:true, txt:'Batería: voltaje en vacío antes de empezar y temperatura ambiente', ph:'4.02 V · 27 °C' },
{ tipo:'radio', clave:'cielo', req:true, txt:'Antena GNSS', opciones:['Cielo abierto','Ventana','Interior (sin fix)'] },
{ tipo:'check', clave:'usb', req:true, txt:'<b>Ningún USB</b> conectado al simpleRTK2B ni al módem: solo el USB de consola del HBOX.' },
{ tipo:'check', clave:'monitor', req:true, txt:'Monitor abierto con la unidad localizada (para ver si recibe).' },
] },
{ titulo: 'Corrida 0 · Referencia con 24 V',
sub:'Todo funcionando con 24 V. Si esto no sale limpio, detenerse: el problema no es la batería.',
items: [
{ tipo:'medida', clave:'c0_main5v', req:true, txt:'MAIN5V', esperado:'5.2 – 5.4 V', u:'V' },
{ tipo:'medida', clave:'c0_sw5v', req:true, txt:'SW_5V', esperado:'≈ 5.0 V', u:'V' },
{ tipo:'texto', clave:'c0_ledm', rows:'1', req:true, txt:'LEDs del módem (cuál, color, patrón)', ph:'NET verde parpadeo lento; PWR rojo fijo' },
{ tipo:'radio', clave:'c0_uart', req:true, txt:'LEDs UART del simpleRTK2B', opciones:['rápido (correcciones + NMEA)','lento ~1/s','sin parpadeo'] },
{ tipo:'check', clave:'c0_k1', req:true, txt:'Clic de K1 a los ~13 s y consola <code>MODOB ON</code>.' },
{ tipo:'check', clave:'c0_srv', req:true, txt:'El monitor muestra la unidad con Salud (<code>$PHLG</code>) y ⚡.' },
{ tipo:'texto', clave:'c0_stat', rows:'2', txt:'Línea STAT (tecla s)', ph:'STAT stack=ON …' },
] },
{ titulo: 'Corrida 1 · Batería con Modo B (la falla)',
sub:'Quitar los 24 V y arrancar el cronómetro. La primera ventana arranca con el módem ya registrado; la que importa es el despertar #1 a los ~10 min.',
items: [
{ tipo:'texto', clave:'c1_perdida', rows:'2', req:true, txt:'Al quitar 24 V: ¿qué se oyó y qué hizo el módem en los primeros 30 s?', ph:'clic de K1 inmediato; el módem se apagó y reencendió / siguió igual' },
{ tipo:'medida', clave:'c1_reposo_sw5v', req:true, txt:'En reposo (tras DUTY reposo): SW_5V', esperado:'0 V', u:'V' },
{ tipo:'radio', clave:'c1_reposo_modem', req:true, txt:'En reposo: el módem…', opciones:['Se apagó (LEDs apagados)','Sigue encendido'],
aviso:'Si sigue encendido con SW_5V = 0 V, algo lo alimenta por otro lado. Anótalo y avisa antes de seguir.' },
{ tipo:'texto', clave:'c1_reposo_nota', si:modemOn, rows:'2', req:true, txt:'¿Qué está conectado al módem o al simpleRTK2B?' },
{ tipo:'texto', clave:'c1_wake', rows:'1', req:true, txt:'Hora del cronómetro en <code>DUTY despertar #1</code>', ph:'10:02' },
tabla('c1', 'Registro del despertar #1 (los 2 minutos de ventana)', 'Anota el valor <b>más bajo</b> que alcances a leer en cada intervalo. Tecla <code>s</code> a los 0:30 y 1:30 para vbat/vbatmin.'),
{ tipo:'texto', clave:'c1_fin', rows:'2', req:true, txt:'Cómo terminó la ventana (línea DUTY reposo y qué decía el monitor)', ph:'DUTY reposo: ventana 120s fix=RTK_FIX phlg_aire=4 … monitor: nada' },
{ tipo:'archivo', clave:'c1_log', etiqueta:'bateria-c1', txt:'Captura de consola de la corrida 1 (opcional aquí; la completa va al final)' },
] },
{ titulo: 'Corrida 2 · Batería SIN Modo B',
sub:'Con la unidad en reposo, teclear B → «MODO B BLOQUEADO por operador». Esperar el despertar #2 y registrar igual.',
items: [
{ tipo:'check', clave:'c2_b', req:true, txt:'La consola confirmó <code>MODO B BLOQUEADO por operador</code> antes del despertar.' },
{ tipo:'texto', clave:'c2_wake', rows:'1', req:true, txt:'Hora del cronómetro en <code>DUTY despertar #2</code>' },
tabla('c2', 'Registro del despertar #2 (Modo B bloqueado)'),
{ tipo:'radio', clave:'c2_resultado', req:true, txt:'Resultado de la corrida 2',
opciones:['El módem registró y el simpleRTK2B parpadea rápido → hipótesis B (camino por K1)',
'Sigue igual que la corrida 1 → hipótesis A (riel) → hacer corrida 3'] },
{ tipo:'check', clave:'c2_b_restore', txt:'Tecleé <code>B</code> otra vez para dejar Modo B permitido.' },
] },
{ titulo: 'Corrida 3 · Solo si la 2 dio A: fuente de banco en lugar de la batería',
si: dioA,
sub:'Con 24 V y la etapa apagada, desconectar la batería y poner la fuente de banco a 3.9 V, límite 3 A, en su conector (medir polaridad antes). Repetir la corrida 1.',
items: [
{ tipo:'medida', clave:'c3_vfuente', req:true, txt:'Fuente ajustada a', esperado:'3.90 V · límite 3 A', u:'V' },
{ tipo:'texto', clave:'c3_wake', rows:'1', req:true, txt:'Hora del cronómetro en el despertar' },
tabla('c3', 'Registro del despertar con fuente de banco'),
{ tipo:'radio', clave:'c3_resultado', req:true, txt:'Resultado de la corrida 3',
opciones:['Funciona con la fuente → es el paquete (resistencia interna)',
'Falla igual → es la etapa de boost (LTC4040 / MAIN5V) — hardware, no tocar nada'] },
{ tipo:'texto', clave:'c3_tbat', rows:'1', txt:'tbat que reportaba STAT con la batería puesta', ph:'38.5 °C' },
{ tipo:'check', clave:'c3_restore', req:true, txt:'Batería reconectada y fuente de banco retirada al terminar.' },
] },
{ titulo: 'Cierre',
items: [
{ tipo:'archivo', clave:'log', req:true, etiqueta:'bateria-consola', txt:'Archivo completo de la consola (toda la sesión)' },
{ tipo:'foto', clave:'f_banco', etiqueta:'banco', txt:'Foto del banco con los multímetros conectados (para saber dónde se midió).' },
{ tipo:'radio', clave:'accion', req:true, guardaEn:'accion', txt:'Conclusión',
opciones:['A — riel: el paquete','A — riel: etapa de boost','B — camino por K1 (firmware)','No concluyente (explica)'] },
{ tipo:'texto', clave:'observaciones', rows:'4', txt:'Observaciones (todo lo que no cupo arriba)' },
] },
]
});
</script>
</body>
</html>

View File

@ -1,6 +1,6 @@
# Prueba de banco — módem 4G en modo batería (fw 0.5.3)
**Versión:** 1.0 · 26-sep-2026 · **Para:** laboratorio QRO · **Dura:** ~45 min por unidad · **Registro:** [acta de banco](../formularios/banco.html) de la unidad, con la captura de consola adjunta.
**Versión:** 1.0 · 26-sep-2026 · **Para:** laboratorio QRO · **Dura:** ~45 min por unidad · **Registro:** [**▶ acta de esta prueba**](../formularios/prueba-bateria.html) — trae las tres tablas para llenar en el teléfono o la laptop, y ahí se adjunta la captura de consola.
## Qué se está buscando
@ -86,7 +86,11 @@ Si la corrida 0 no sale limpia, **detenerse**: el problema no es la batería.
- **Falla igual** → el problema es la **etapa de boost** (LTC4040 / capacitancia en MAIN5V). Es hardware; no cambiar nada,
reportar las lecturas.
## Tabla de registro (una por corrida)
## Registro
Todo se llena en el **[acta de la prueba](../formularios/prueba-bateria.html)**: configuración, corrida 0, la tabla de
2 minutos de cada despertar (una por corrida), el resultado A/B y el archivo completo de la consola. Queda en el
expediente de la unidad. Si no hay señal en el laboratorio, la misma tabla en papel y se captura después:
| t | MAIN5V (mín) | SW_5V (mín) | LEDs módem | LEDs UART simpleRTK2B | K1 | `vbat` / `vbatmin` | ¿Servidor recibió? |
|---|---|---|---|---|---|---|---|
@ -97,10 +101,6 @@ Si la corrida 0 no sale limpia, **detenerse**: el problema no es la batería.
| 1:30 | | | | | | | |
| 2:00 | | | | | | | |
Al terminar: guardar el archivo de la consola completo, registrar el **acta de banco** de la unidad con el archivo
adjunto y las tres tablas en el campo de observaciones (o una foto de la hoja), y avisar a ingeniería con el
resultado de la corrida 2 (**A** o **B**).
!!! danger "Lo que NO se hace"
No cambiar el firmware, no mover puentes (JP2/JP3/JP4), no tocar el trimmer del SD-15B. La prueba sirve porque
la unidad está **como en campo**. Y nunca conectar la fuente de banco a la batería con la batería puesta.

View File

@ -52,6 +52,7 @@ nav:
- "▶ Acta de diagnóstico en banco": formularios/banco.html
- "⚡ Reflasheo de unidades": procedimientos/reflasheo-unidades.md
- "🔋 Prueba: módem 4G en batería (0.5.3)": procedimientos/prueba-bateria-modem.md
- "▶ Acta: prueba de módem en batería": formularios/prueba-bateria.html
- "▶ Acta de reflasheo y configuración": formularios/reflasheo.html
- "🏭 Producción — lote v5":