370ae5c414
habilitada en la configuracion tomara una imagen en el momento en que el camion este completamente en la bascula, para ello hemos introducido un codigo en el nodo "estable" que es donde se hace la deteccion del camion completamente en la bascula. Posteriormente en el nodo "Analisis del modo de Operacion" es donde verificamos que la imagen fue tomada y la colocamos en el registro que se esta haciendo para generar el movimiento correspondiente. El Grupo "CAMARA CONTEXTO" posee los nodos de solicitud de imagen y de recepcion de la misma. La configuracion de IP de la camara se hace en la pantalla de configuracion al igual que su habilitacion en el sistema. - Como herramienta de ayuda para saber si el Totem esta en capacidad de solicitar y de recibir la imagen de contexto enviada por la camara se ha incluido una funcion en el menu de configuracion -> Pruebas del sistema -> Pedir Snapshot de contexto de forma que el usuario pueda visualizar la imagen actual que la camara esta enviando y pueda de esa forma confirmar el correcto funcionamiento de la conexión y de los equipos.
495 lines
18 KiB
JavaScript
495 lines
18 KiB
JavaScript
/* 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 === 'contexto') {
|
|
// Limpiar datos visibles
|
|
document.getElementById('camara_contexto').textContent = '---';
|
|
document.getElementById('fecha_contexto').textContent = '----';
|
|
// Mostrar imagen vacía
|
|
document.getElementById('foto_contexto').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 = "";
|
|
}
|
|
// nuevo paara camara de contexto
|
|
if (msg.hasOwnProperty("picture_contexto")) {
|
|
if (msg.context_cam) {
|
|
document.getElementById('foto_contexto').src = msg.picture_contexto;
|
|
document.getElementById('fecha_contexto').textContent = msg.fecha_contexto || '';
|
|
document.getElementById('camara_contexto').textContent = "Contexto" || '---';
|
|
}
|
|
}
|
|
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;
|
|
|
|
// ======== Sensor de distancia añadido por david========
|
|
if (msg.hasOwnProperty("distance")) {
|
|
const dist = msg.distance;
|
|
const dmin = msg.distance_min;
|
|
const maxRange = 4000;
|
|
|
|
document.getElementById('sensor-dist').textContent =
|
|
(dist !== null && dist !== undefined) ? dist + ' mm' : '--- mm';
|
|
|
|
if (dmin !== null && dmin !== undefined) {
|
|
document.getElementById('sensor-dmin').textContent = dmin + ' mm';
|
|
}
|
|
|
|
if (dist !== null && dist !== undefined) {
|
|
const fillPct = Math.min((dist / maxRange) * 100, 100);
|
|
const fill = document.getElementById('sensor-fill');
|
|
fill.style.width = fillPct + '%';
|
|
}
|
|
}
|
|
});
|
|
|
|
//****************************************************************************************************************************************************** */
|
|
// 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.");
|
|
|
|
|
|
|
|
|
|
|
|
*/ |