Version 1.0 del totem en el sistema bascula autonoma

This commit is contained in:
2025-11-10 10:34:55 +01:00
parent 193f9d1812
commit f0ad04d4a3
40 changed files with 10416 additions and 1 deletions
+459
View File
@@ -0,0 +1,459 @@
/* global uibuilder */
uibuilder.start();
// Navegación entre pantallas
function mostrarPantalla(id) {
document.querySelectorAll('.pantalla').forEach(div =>
div.classList.toggle('visible', div.id === id)
);
}
let currentTopic = "";
let result = [];
//****************************************************RECEPCION DE MENSAJES DESDE NODE_RED ***************************************************************** */
uibuilder.onChange('msg', msg => {
if (msg.payload && msg.payload.pantalla) {
mostrarPantalla('pantalla-' + msg.payload.pantalla);
}
if (msg.semaforo) {
document.getElementById('estado').style.backgroundColor = msg.semaforo;
}
if (msg.payload && msg.payload.pantalla === 'snapshot') {
// Limpiar datos visibles
document.getElementById('camara').textContent = '---';
document.getElementById('placa').textContent = '----';
document.getElementById('fecha').textContent = '----';
// Mostrar imagen vacía
document.getElementById('foto').src = "";
}
if (msg.payload && msg.payload.pantalla === 'escuchar' && msg.buttons != true) {
// Limpiar datos visibles
document.getElementById('camara_escuchar').textContent = '---';
document.getElementById('placa_escuchar').textContent = '----';
document.getElementById('fecha_escuchar').textContent = '----';
document.getElementById('contador').textContent = `0 / 0`;
// Mostrar imagen vacía
document.getElementById('foto_escuchar').src = "";
}
if (msg.hasOwnProperty("picture")) {
//uibuilder.send({ payload: msg.payload.Picture.Plate.PlateNumber, topic: msg.payload.Picture.SnapInfo.DeviceID });
if (msg.snapshot_cam) {
document.getElementById('foto').src = msg.picture;
document.getElementById('placa').textContent = msg.plate || '----';
document.getElementById('fecha').textContent = msg.plate_time || '';
document.getElementById('camara').textContent = msg.camera_id || '---';
}
else if (msg.escuchar_cam){
document.getElementById('foto_escuchar').src = msg.picture;
document.getElementById('placa_escuchar').textContent = msg.plate || '----';
document.getElementById('fecha_escuchar').textContent = msg.plate_time || '';
document.getElementById('camara_escuchar').textContent = msg.camera_id || '---';
const total = msg.snapshot_indexmax || 0;
const actual = (msg.snapshot_readindex || 0) + 1;
document.getElementById('contador').textContent = `${actual} / ${total}`;
}
}
else if (msg.payload?.pantalla === 'tipos') {
mostrarPantalla('pantalla-tipos');
if (msg.payload.tiposDatos) {
tiposDatos = msg.payload.tiposDatos;
}
}
if (msg.barrera) {
mostrarBarreraAbierta()
}
else{
mostrarBarreraCerrada()
}
// tabla dinamica
if (msg.topic == "general" || msg.topic == "conexion" || msg.topic == "camara" || msg.topic == "sensores" || msg.topic == "mensajes" || msg.topic == "tiempos"){
if (!msg.tabla || !Array.isArray(msg.tabla)) return;
const tbody = document.getElementById('config-table-body');
tbody.innerHTML = '';
currentTopic = msg.topic || 'general';
msg.tabla.forEach(item => { //generamos dinamixamente la tabla que vera el usuario en el dispositivo (PC o smartphone).
// notese que la celda 2 de cada fila es del tipo editable.
const row = document.createElement('tr');
const cell1 = document.createElement('td');
cell1.textContent = item.parametro;
const cell2 = document.createElement('td');
cell2.contentEditable = true;
cell2.textContent = item.valor;
row.appendChild(cell1);
row.appendChild(cell2);
tbody.appendChild(row); // enviamos la tabla al elemento html (config-table-body)
});
}
// configuracion del pop up de notificaciones
if (msg.modalText) {
document.getElementById('modal-text').textContent = msg.modalText;
const btnSi = document.getElementById('btn-modal-si');
const btnNo = document.getElementById('btn-modal-no');
if (msg.modalType === "notificacion") {
btnSi.textContent = 'OK';
btnNo.style.display = 'none';
window.modalCallback = null;
} else {
btnSi.textContent = 'Sí';
btnNo.style.display = 'inline-block';
window.modalCallback = msg.modalCallback || null;
}
document.getElementById('confirm-modal').style.display = 'block';
}
// ======================================== Datos para la Pantalla Principal ========================================================================
if (msg.hasOwnProperty("peso")) document.getElementById('peso').textContent = msg.peso || 0;
//if (msg.semaforo_color) document.getElementById('semaforo-color').style.backgroundColor = msg.semaforo_color;
const led = document.getElementById('semaforo-color');
if (msg.semaforo_color) {
led.style.backgroundColor = msg.semaforo_color;
led.classList.add('led');
} else {
led.style.backgroundColor = '#ccc';
led.classList.remove('led');
}
if (msg.hasOwnProperty("matricula")) document.getElementById('matricula').textContent = msg.matricula;
if (msg.hasOwnProperty("peso_estable_flag")) document.getElementById('peso-estable').textContent = msg.peso_estable_flag ? 'SI' : 'NO';
if (msg.hasOwnProperty("vial")) document.getElementById('vial').textContent = msg.vial;
if (msg.hasOwnProperty("totem_id")) document.getElementById('totem-id').textContent = msg.totem_id;
if (msg.hasOwnProperty("iplan")) document.getElementById('iplan').textContent = msg.iplan;
if (msg.hasOwnProperty("ipsupervisor")) document.getElementById('ipsupervisor').textContent = msg.ipsupervisor;
});
//****************************************************************************************************************************************************** */
// Detectar clics en botones
document.addEventListener('click', ev => {
if (ev.target.matches('button[data-action]')) {
const action = ev.target.getAttribute('data-action');
console.log('⏺ Acción botón:', action);
uibuilder.send({ payload: { seccion: action } });
}
});
// Mostrar pantalla tipos
document.addEventListener('click', ev => {
if (ev.target.matches('button[data-action="tipos"]')) {
mostrarPantalla('pantalla-tipos');
}
});
document.getElementById('btn-add-param').addEventListener('click', agregarParametro);
document.getElementById('btn-guardar-tipo').addEventListener('click', () => {
window.modalCallback = guardarTipo;
document.getElementById('modal-text').textContent = `¿Deseas guardar los cambios de Tipo ${tipoActual}?`;
document.getElementById('confirm-modal').style.display = 'block';
});
document.getElementById('btn-cancelar-tipo').addEventListener('click', () => {
uibuilder.send({ payload: { accion: "cancelar_tipo", tipo: tipoActual } });
volverATipos();
});
document.getElementById('btn-volver-tipos').addEventListener('click', () => {
volverATipos(); // esto limpia la tabla con los parametros del Tipo en el que estemos
uibuilder.send({ payload: { seccion: "volver_tipos" } });
});
let tipoActual = null;
let tiposDatos = {}; // aquí se guarda la configuración temporal
//******************************************* FUNCIONES ******************************************************** */
// Acciones a partir de botones en html
function sendRow() {// Aqui llega cuando se hace click en el boton Actualizar de las tablas dinamicas
// recoge el contenido de la tabla en ese instante y saca Notificacion de guardar con opcion Si o No
const rows = document.querySelectorAll('#config-table-body tr');
result = []; // inicializamos result para poner exactamente la tabla actual
/*
rows.forEach(row => {
const parametro = row.cells[0].textContent.trim();
let valor = row.cells[1].textContent.trim();
// normaliza a booleano si corresponde
if (valor === "true") valor = true;
if (valor === "false") valor = false;
result.push({ parametro, valor });
});
*/
//Modificacion de la lectura de la tabla para asegurar que los numeros son mantenidos como numeros y no como texto
// al igual que los valores booleanos.
rows.forEach(row => {
const parametro = row.cells[0].textContent.trim();
let valor = row.cells[1].textContent.trim();
if (/^(true|false)$/i.test(valor)) {
valor = valor.toLowerCase() === "true";
} else if (/^-?\d+$/.test(valor)) {
valor = parseInt(valor, 10);
} else if (/^-?\d+\.\d+$/.test(valor)) {
valor = parseFloat(valor);
}
result.push({ parametro, valor });
});
// Mostramos directamente la confirmación y definimos la callback. "topic" contiene el nombre de la tabla donde se guardan los valores que vienen en "result"
mostrarConfirmacion("¿Deseas guardar los cambios?", () => {
uibuilder.send({
payload: { table: result, seccion: "respuesta" },
topic: currentTopic,
origen: "actualizar"
});
});
}
function sendCancel() { // boton Cancelar de las tablas dinamicas
uibuilder.send({ payload: {seccion:'cancel-table'}, topic: currentTopic });
}
function confirmGuardar() { // Respuesta Afirmativa del Modal de tabla dinamica
document.getElementById('confirm-modal').style.display = 'none';
if (typeof window.modalCallback === 'function') {
const cb = window.modalCallback;
window.modalCallback = null; // limpia para la siguiente vez
cb(); // ejecuta
} else if (typeof window.modalCallback === 'string') {
uibuilder.send({ payload: { table: result }, modalCallback: window.modalCallback, origen: "modal" });
}
}
/*
function confirmGuardar() { // Respuesta Afirmativa del Modal de tabla dinamica
uibuilder.send({ payload: { table: result }, topic: currentTopic, origen: "modal-tabla" });
document.getElementById('confirm-modal').style.display = 'none';
}
*/
function cancelarGuardar() { // Respuesta Negativa del Modal de tabla dinamica
result = [];
document.getElementById('confirm-modal').style.display = 'none';
}
// Funciones para icono barrera
function mostrarBarreraAbierta() {
const icono = document.getElementById('icono-barrera');
icono.src = "/barrera_abierta.png";
/*
setTimeout(() => {
mostrarBarreraCerrada();
}, 1000); // 1 segundo
*/
}
function mostrarBarreraCerrada() {
const icono = document.getElementById('icono-barrera');
icono.src = "/barrera_cerrada.png";
}
//************************* FUNCIONES DE TIPOS DE CONFIGURACION *****************************/
function seleccionarTipo(n) {
tipoActual = n;
document.getElementById('titulo-tipo').textContent = `TIPO ${n}`;
document.getElementById('tipo-configuracion').style.display = 'block';
const contenedor = document.getElementById('parametros');
contenedor.innerHTML = '';
const datos = tiposDatos[n] || []; // ya es un array
datos.forEach(d => {
const row = crearFilaParametro(d.parametro, d.valor);
contenedor.appendChild(row);
});
}
function agregarParametro() {
const contenedor = document.getElementById('parametros');
if (contenedor.children.length >= 8) return;
const row = crearFilaParametro('', '');
contenedor.appendChild(row);
}
function crearFilaParametro(parametro, valor) {
const div = document.createElement('div');
div.className = 'parametro-row';
// Input para el nombre del parámetro
const inputParam = document.createElement('input');
inputParam.placeholder = 'Parámetro';
inputParam.value = parametro;
// Input para el valor: checkbox o texto
let inputValor;
if (valor === true || valor === false || valor === "true" || valor === "false") {
inputValor = document.createElement('input');
inputValor.type = 'checkbox';
inputValor.checked = (valor === true || valor === "true");
} else {
inputValor = document.createElement('input');
inputValor.placeholder = 'Valor';
inputValor.value = valor;
}
inputParam.classList.add('parametro');
inputValor.classList.add('valor');
// Botón para eliminar la fila
const btnDelete = document.createElement('button');
btnDelete.textContent = '-';
btnDelete.className = 'btn rojo';
btnDelete.style.flex = '0 0 40px';
btnDelete.style.width = '40px';
btnDelete.style.height = '40px';
btnDelete.style.marginLeft = '4px';
btnDelete.style.zIndex = '1';
btnDelete.addEventListener('click', (e) => {
e.stopPropagation();
e.preventDefault();
window.modalCallback = () => div.remove();
document.getElementById('modal-text').textContent = '¿Deseas eliminar este parámetro?';
document.getElementById('confirm-modal').style.display = 'block';
return false;
});
// Añadir los elementos a la fila
div.appendChild(inputParam);
div.appendChild(inputValor);
div.appendChild(btnDelete);
return div;
}
function crearFilaParametro_viejo(parametro, valor) { // Crea un nuevo elemento en la tabla del Tipo que estemos trabajando con dos campos y un boton de borrado
const div = document.createElement('div');
div.className = 'parametro-row';
const inputParam = document.createElement('input');
inputParam.placeholder = 'Parámetro';
inputParam.value = parametro;
const inputValor = document.createElement('input');
inputValor.placeholder = 'Valor';
inputValor.value = valor;
const btnDelete = document.createElement('button');
btnDelete.textContent = '-';
btnDelete.className = 'btn rojo';
btnDelete.style.width = '40px';
btnDelete.style.height = '40px';
btnDelete.onclick = () => { // gestion de la alerta para el borrado, haciendo uso del modal que tenemos en html
window.modalCallback = () => div.remove();
document.getElementById('modal-text').textContent = '¿Deseas eliminar este parámetro?';
document.getElementById('confirm-modal').style.display = 'block';
};
div.appendChild(inputParam);
div.appendChild(inputValor);
div.appendChild(btnDelete);
return div;
}
function guardarTipo() {
const contenedor = document.getElementById('parametros');
const filas = contenedor.querySelectorAll('.parametro-row');
const datos = [];
filas.forEach(fila => {
const [param, val] = fila.querySelectorAll('input');
let valor;
if (val.type === 'checkbox') {
valor = val.checked;
} else {
valor = val.value.trim();
}
if (param.value.trim()) {
datos.push({ parametro: param.value.trim(), valor });
}
});
/*
filas.forEach(fila => {
const [param, val] = fila.querySelectorAll('input');
if (param.value.trim()) {
datos.push({ parametro: param.value.trim(), valor: val.value.trim() });
}
});
*/
tiposDatos[tipoActual] = datos;
uibuilder.send({ payload: { tipo: tipoActual, datos }, topic: "tipoconfig" }); // el topic nos sirve para seleccionar en el proximo nodo la funcion de guardar los datos
volverATipos(); // 🔷 salimos de la pantalla del tipo actual
}
function volverATipos() {
document.getElementById('tipo-configuracion').style.display = 'none';
tipoActual = null;
}
//==========================================FUNCIONES PARA MANEJAR LAS NOTIFICACIONES ======================================================
function mostrarConfirmacion(texto, callback) {
document.getElementById('modal-text').textContent = texto;
// Restablece estado normal del modal
const btnSi = document.getElementById('btn-modal-si');
const btnNo = document.getElementById('btn-modal-no');
btnSi.textContent = 'Sí';
btnNo.style.display = 'inline-block';
window.modalCallback = callback;
document.getElementById('confirm-modal').style.display = 'block';
}
function mostrarNotificacion(texto) {
document.getElementById('modal-text').textContent = texto;
const btnSi = document.getElementById('btn-modal-si');
const btnNo = document.getElementById('btn-modal-no');
btnSi.textContent = 'OK';
btnNo.style.display = 'none';
window.modalCallback = null;
document.getElementById('confirm-modal').style.display = 'block';
}
/*
Cuando necesitemos un modal clásico (Sí/No), por ejemplo para confirmar actualización:
mostrarConfirmacion("¿Deseas guardar los cambios?", () => {
uibuilder.send({ payload: { table: result, seccion:"respuesta" }, topic: currentTopic, origen: "actualizar" });
});
Cuando necesitemos simplemente notificar un error:
mostrarNotificacion("La IP introducida es inválida, por favor corrígela.");
*/