diff --git a/bascula_autonoma_1.json b/bascula_autonoma_1.json index 1df6aa0..c64154f 100644 --- a/bascula_autonoma_1.json +++ b/bascula_autonoma_1.json @@ -290,11 +290,13 @@ "1b1ee13b380e32b6", "31e0432a8e9a2f00", "f3676f905e4ade55", - "ff3913749bd9e2f3" + "ff3913749bd9e2f3", + "6af84585bb71f167", + "a0f7956de8f33a0b" ], - "x": 134, + "x": 54, "y": 893, - "w": 2252, + "w": 2342, "h": 814 }, { @@ -555,7 +557,7 @@ "2c73d02c99e64b37", "cc25836d7a7314ad" ], - "x": 794, + "x": 714, "y": 1459, "w": 432, "h": 222 @@ -599,7 +601,7 @@ "c79815b5a79ef60a", "077ea8c7ce8cec99" ], - "x": 994, + "x": 914, "y": 919, "w": 572, "h": 510 @@ -630,7 +632,7 @@ "a395ed21d7049d5d", "6ebdceb73c4cfbf0" ], - "x": 1324, + "x": 1244, "y": 1459, "w": 942, "h": 222 @@ -656,7 +658,7 @@ "a0aa0f96fe081912", "730bbe3df0ab62db" ], - "x": 1584, + "x": 1504, "y": 1319, "w": 562, "h": 122 @@ -794,7 +796,7 @@ { "id": "be0e1690e4e13e0d", "type": "websocket-client", - "path": "ws://servidor.com:25009", + "path": "ws://servidor.com:1880", "tls": "", "wholemsg": "false", "hb": "20", @@ -2161,7 +2163,6 @@ "id": "0bc82cd2485c7679", "type": "inject", "z": "953e8740401d175c", - "d": true, "g": "6443f177eec330e4", "name": "distance ID1", "props": [ @@ -2743,7 +2744,7 @@ "z": "953e8740401d175c", "g": "0e2b148022a7af13", "name": " envia matricula", - "func": "if (!flow.get(\"plate_sent\") && !flow.get(\"peso_bajo_flag\")) {\n var timestamp = new Date().getTime()\n var plate = RED.util.cloneMessage(flow.get(\"last_plate\"))\n var plate_number = plate.payload.Picture.Plate.PlateNumber\n var plate_time = plate.payload.Picture.SnapInfo.SnapTime\n //node.send([{payload:plate.payload.Picture.NormalPic.Content, filename:\"/home/trypton/.node-red/dahua_images/\" + plate.payload.Picture.NormalPic.PicName}, null])\n flow.set(\"matricula\", plate_number)\n flow.set(\"matricula_image\", plate.payload.Picture.NormalPic.Content) // Recuperamos y guardamos foto del vehiculo\n flow.set(\"plate_sent\", true) // marcamos matricula enviada\n if (!flow.get(\"supervisor\")) {\n //node.warn(\"matricula leida\")\n flow.set(\"matricula_leida_flag\", true)\n //flow.set(\"nombre_imagen\", plate.payload.Picture.NormalPic.PicName)\n let now = new Date()\n let nowdate = now.toLocaleString(\"sv-SE\").replace(/[- :]/g, \"\")\n flow.set(\"nombre_imagen\", flow.get(\"matricula\") + \"-\" + nowdate + \".jpg\")\n if (plate_number == \"NOT FOUND\") {\n flow.set(\"matricula_formato_flag\", false)\n }\n else {\n let checkmatricula = checkPlateFormat(plate_number)\n flow.set(\"matricula_formato_flag\", checkmatricula.isValid)\n flow.set(\"matricula\", checkmatricula.formattedText)\n }\n\n msg = {}\n msg.payload = 1\n msg.topic = \"matricula_leida\"\n msg.matricula = flow.get(\"matricula\")\n\n node.send([null, msg, null]);\n }\n\n else { // caso supervisor = true\n if (!flow.get(\"peso_bajo_flag\")) {\n let msg_json = {}\n msg_json.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), \"matricula\": flow.get(\"matricula\"), \"foto_matricula\": flow.get(\"matricula_image\") }\n node.send([null, { payload: true }, msg_json]);\n }\n }\n}\nelse{//\n if(!flow.get(\"plate_sent\") && flow.get(\"anpr1_flag\")) {\n //Si estamos aqui es porque tenemos imagen tomada por request del detector pero la bascula no ha sido activada\n \n }\n}\nreturn\n\n/*******************************************************************************************\n * FUNCION DE CHEQUEO DEL FORMATO DE LA MATRICULA\n *******************************************************************************************\n*/\nfunction checkPlateFormat(plate) {\n\n let formatoPais = flow.get(\"formatoPais\")\n let match\n\n // Seleccion del formato\n if (formatoPais === \"es\") {\n //ESPAÑA\n const regex1 = /^(\\d{4}[A-Z]{3})(.*)$/i;\n const regex2 = /^([A-Z]{1,2}\\d{4}[A-Z]{2})(.*)$/i;\n match = plate.match(regex1) || plate.match(regex2);\n } if (formatoPais === \"co\") {\n //COLOMBIA\n const regexCargaGeneral = /^[A-Z]\\d{4}$/;\n const regexCargaT = /^T\\d{4}$/;\n const regexDiplomaticos = /^[A-Z]{2}\\d{4}$/;\n const regexParticulares = /^[A-Z]{3}\\d{3}$/;\n match = plate.match(regexCargaGeneral) || plate.match(regexCargaT) || plate.match(regexDiplomaticos) || plate.match(regexParticulares);\n }\n // Aplicacion del formato seleccionado\n flow.set(\"match\", match)\n if (match) {\n return {\n isValid: true,\n formattedText: match[0] // Solo tomamos el primer grupo de captura (la matrícula válida)\n };\n } else {\n return {\n isValid: false,\n formattedText: plate\n };\n }\n}\n\n\n\n\n\n\n\n\n\n\n/*******************************************************************************************\n * FUNCION DE CHEQUEO DEL FORMATO DE LA MATRICULA\n *******************************************************************************************\n * Esta funcion chequea:\n * 1.- si el formato de la matricula se corresponde con 4 numeros y 3 letras o bien con 1 o 2 letras, 4 nuemros y 2 letras\n * 2.- si tiene letras adicionales al final de la matricula (como SP), las elimina \n * 3.- devuelve validez del formato y contenidos de la matricula\n*/\n/*\nfunction checkPlateFormat(plate) {\n // Expresiones regulares para los dos formatos, capturando cualquier cosa extra al final\n // En el formato 1 el primer grupo de captura contiene los caracteres permitidos de la matricula (\\d{4}[A-Z]{3}) y el segundo grupo de captura (.*) los caracteres que no queremos\n // En el formato 2 el primer grupo de captura contiene los caracteres permitidos de la matricula ([A-Z]{1,2}\\d{4}[A-Z]{2}) y el segundo grupo de captura (.*) los caracteres que no queremos\n const regex1 = /^(\\d{4}[A-Z]{3})(.*)$/i;\n const regex2 = /^([A-Z]{1,2}\\d{4}[A-Z]{2})(.*)$/i;\n\n let match = plate.match(regex1) || plate.match(regex2);\n flow.set(\"match\", match)\n if (match) {\n return {\n isValid: true,\n formattedText: match[1] // Solo tomamos el primer grupo de captura (la matrícula válida)\n };\n } else {\n return {\n isValid: false,\n formattedText: plate\n };\n }\n}\n*/", + "func": "if (!flow.get(\"plate_sent\") && !flow.get(\"peso_bajo_flag\")) {\n var timestamp = new Date().getTime()\n var plate = RED.util.cloneMessage(flow.get(\"last_plate\"))\n var plate_number = plate.payload.Picture.Plate.PlateNumber\n var plate_time = plate.payload.Picture.SnapInfo.SnapTime\n //node.send([{payload:plate.payload.Picture.NormalPic.Content, filename:\"/home/trypton/.node-red/dahua_images/\" + plate.payload.Picture.NormalPic.PicName}, null])\n flow.set(\"matricula\", plate_number)\n flow.set(\"matricula_image\", plate.payload.Picture.NormalPic.Content) // Recuperamos y guardamos foto del vehiculo\n flow.set(\"plate_sent\", true) // marcamos matricula enviada\n if (!flow.get(\"supervisor\")) {\n //node.warn(\"matricula leida\")\n flow.set(\"matricula_leida_flag\", true)\n //flow.set(\"nombre_imagen\", plate.payload.Picture.NormalPic.PicName)\n let now = new Date()\n let nowdate = now.toLocaleString(\"sv-SE\").replace(/[- :]/g, \"\")\n flow.set(\"nombre_imagen\", flow.get(\"matricula\") + \"-\" + nowdate + \".jpg\")\n if (plate_number == \"NOT FOUND\") {\n flow.set(\"matricula_formato_flag\", false)\n }\n else {\n let checkmatricula = checkPlateFormat(plate_number)\n flow.set(\"matricula_formato_flag\", checkmatricula.isValid)\n flow.set(\"matricula\", checkmatricula.formattedText)\n }\n\n msg = {}\n msg.payload = 1\n msg.topic = \"matricula_leida\"\n msg.matricula = flow.get(\"matricula\")\n // verificamos si tenemos que enviar la matricula y la imagen al supervisor\n let newmsg = flow.get(\"envia_mat_img\")\n ? {\n payload: { device: \"supervisor\", accion: \"lastplate\", data: {numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), matricula: flow.get(\"matricula\"), imagen: flow.get(\"matricula_image\")}}\n }\n : null;\n\n node.send([null, msg, newmsg]);\n }\n\n else { // caso supervisor = true\n if (!flow.get(\"peso_bajo_flag\")) {\n let msg_json = {}\n msg_json.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), \"matricula\": flow.get(\"matricula\"), \"foto_matricula\": flow.get(\"matricula_image\") }\n node.send([null, { payload: true }, msg_json]);\n }\n }\n}\nelse{//\n if(!flow.get(\"plate_sent\") && flow.get(\"anpr1_flag\")) {\n //Si estamos aqui es porque tenemos imagen tomada por request del detector pero la bascula no ha sido activada\n \n }\n}\nreturn\n\n/*******************************************************************************************\n * FUNCION DE CHEQUEO DEL FORMATO DE LA MATRICULA\n *******************************************************************************************\n*/\nfunction checkPlateFormat(plate) {\n\n let formatoPais = flow.get(\"formatoPais\")\n let match\n\n // Seleccion del formato\n if (formatoPais === \"es\") {\n //ESPAÑA\n const regex1 = /^(\\d{4}[A-Z]{3})(.*)$/i;\n const regex2 = /^([A-Z]{1,2}\\d{4}[A-Z]{2})(.*)$/i;\n match = plate.match(regex1) || plate.match(regex2);\n } if (formatoPais === \"co\") {\n //COLOMBIA\n const regexCargaGeneral = /^[A-Z]\\d{4}$/;\n const regexCargaT = /^T\\d{4}$/;\n const regexDiplomaticos = /^[A-Z]{2}\\d{4}$/;\n const regexParticulares = /^[A-Z]{3}\\d{3}$/;\n match = plate.match(regexCargaGeneral) || plate.match(regexCargaT) || plate.match(regexDiplomaticos) || plate.match(regexParticulares);\n }\n // Aplicacion del formato seleccionado\n flow.set(\"match\", match)\n if (match) {\n return {\n isValid: true,\n formattedText: match[0] // Solo tomamos el primer grupo de captura (la matrícula válida)\n };\n } else {\n return {\n isValid: false,\n formattedText: plate\n };\n }\n}\n\n\n\n\n\n\n\n\n\n\n/*******************************************************************************************\n * FUNCION DE CHEQUEO DEL FORMATO DE LA MATRICULA\n *******************************************************************************************\n * Esta funcion chequea:\n * 1.- si el formato de la matricula se corresponde con 4 numeros y 3 letras o bien con 1 o 2 letras, 4 nuemros y 2 letras\n * 2.- si tiene letras adicionales al final de la matricula (como SP), las elimina \n * 3.- devuelve validez del formato y contenidos de la matricula\n*/\n/*\nfunction checkPlateFormat(plate) {\n // Expresiones regulares para los dos formatos, capturando cualquier cosa extra al final\n // En el formato 1 el primer grupo de captura contiene los caracteres permitidos de la matricula (\\d{4}[A-Z]{3}) y el segundo grupo de captura (.*) los caracteres que no queremos\n // En el formato 2 el primer grupo de captura contiene los caracteres permitidos de la matricula ([A-Z]{1,2}\\d{4}[A-Z]{2}) y el segundo grupo de captura (.*) los caracteres que no queremos\n const regex1 = /^(\\d{4}[A-Z]{3})(.*)$/i;\n const regex2 = /^([A-Z]{1,2}\\d{4}[A-Z]{2})(.*)$/i;\n\n let match = plate.match(regex1) || plate.match(regex2);\n flow.set(\"match\", match)\n if (match) {\n return {\n isValid: true,\n formattedText: match[1] // Solo tomamos el primer grupo de captura (la matrícula válida)\n };\n } else {\n return {\n isValid: false,\n formattedText: plate\n };\n }\n}\n*/", "outputs": 3, "timeout": 0, "noerr": 0, @@ -2755,11 +2756,11 @@ "wires": [ [], [ - "dba345c891c1fb51", - "4e1d349cc2c30e66" + "dba345c891c1fb51" ], [ - "41cb4ca9c6c75818" + "41cb4ca9c6c75818", + "4e1d349cc2c30e66" ] ] }, @@ -3490,7 +3491,7 @@ "name": "servidor", "server": "", "client": "be0e1690e4e13e0d", - "x": 230, + "x": 150, "y": 1160, "wires": [ [ @@ -3506,7 +3507,7 @@ "name": "ws/test", "server": "eb652223.5293e", "client": "", - "x": 230, + "x": 150, "y": 1240, "wires": [ [ @@ -3523,7 +3524,7 @@ "property": "payload", "action": "", "pretty": false, - "x": 410, + "x": 330, "y": 1200, "wires": [ [ @@ -3546,7 +3547,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 620, + "x": 540, "y": 1200, "wires": [ [ @@ -3577,7 +3578,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1970, + "x": 1890, "y": 1160, "wires": [ [ @@ -3592,7 +3593,8 @@ "a862be7fbfd8827a" ], [ - "422897281c1c5ef6" + "422897281c1c5ef6", + "6af84585bb71f167" ], [ "6530f7fdb887dbf1", @@ -3623,7 +3625,7 @@ "1c98099a9069bb98", "077ea8c7ce8cec99" ], - "x": 705, + "x": 625, "y": 1120, "wires": [ [ @@ -3644,7 +3646,7 @@ "freq": "", "out": "out", "bcm": true, - "x": 2280, + "x": 2200, "y": 1240, "wires": [] }, @@ -3670,7 +3672,7 @@ "topic": "", "payload": "1", "payloadType": "num", - "x": 230, + "x": 150, "y": 1100, "wires": [ [ @@ -3691,7 +3693,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 390, + "x": 310, "y": 1100, "wires": [ [ @@ -3709,7 +3711,7 @@ "links": [ "da672c95637f91bd" ], - "x": 815, + "x": 735, "y": 1340, "wires": [] }, @@ -3722,11 +3724,12 @@ "links": [ "cd806a312b8e7e6e" ], - "x": 2165, + "x": 2085, "y": 1260, "wires": [ [ - "422897281c1c5ef6" + "422897281c1c5ef6", + "6af84585bb71f167" ] ] }, @@ -3742,7 +3745,7 @@ "timer": "", "oldrc": false, "name": "Screen ON/OFF", - "x": 1120, + "x": 1040, "y": 1580, "wires": [ [], @@ -3772,7 +3775,7 @@ "topic": "", "payload": "echo 1 | sudo tee /sys/class/backlight/0-0045/bl_power", "payloadType": "str", - "x": 900, + "x": 820, "y": 1560, "wires": [ [ @@ -3802,7 +3805,7 @@ "topic": "", "payload": "echo 0 | sudo tee /sys/class/backlight/0-0045/bl_power", "payloadType": "str", - "x": 900, + "x": 820, "y": 1520, "wires": [ [ @@ -3832,7 +3835,7 @@ "topic": "", "payload": "sudo shutdown -r now", "payloadType": "str", - "x": 890, + "x": 810, "y": 1600, "wires": [ [ @@ -3862,7 +3865,7 @@ "topic": "", "payload": "sudo poweroff", "payloadType": "str", - "x": 900, + "x": 820, "y": 1640, "wires": [ [ @@ -3880,7 +3883,7 @@ "07212ec8d039ced0", "7ae4043cc5314f0b" ], - "x": 995, + "x": 915, "y": 1500, "wires": [ [ @@ -3899,7 +3902,7 @@ "2b68b52db6cc1683", "9667a055f9c967a7" ], - "x": 805, + "x": 725, "y": 1060, "wires": [] }, @@ -3913,8 +3916,8 @@ "links": [ "f47c019e5baa0206" ], - "x": 2225, - "y": 1380, + "x": 2155, + "y": 1420, "wires": [] }, { @@ -3927,7 +3930,7 @@ "links": [ "cc25836d7a7314ad" ], - "x": 815, + "x": 735, "y": 1300, "wires": [] }, @@ -3938,7 +3941,7 @@ "g": "646bdfb9988a1708", "name": "Codiciones Operacion", "info": "Este sistema puede funcionar con un Totem o con dos Totems (bascula bidireccional).\nAl entrar el vehiculo en la bascula, este es detectado por el evento bascula, al superar el peso minimo,\ny/o por el detector de presencia de vehiculos situado en el Totem (remoto) por donde entra el vehiculo. Es de notar que \nsi hemos configurado la modalidad con dos Totem, el evento bascula no inicia el request del snapshot, en ese caso lo hace el detector.\nEstos eventos hacen que el Totem correspondiente genere un request de snapshot a la camara para leer la matricula del vehiculo entrante. \nSimultaneamente el evento bascula (peso > pmin) genera un UUID para abrir un movimiento en la bascula y lo rellena\ncon los parametros hasta ese momento conocidos como hora de entrada, nombre del totem, modo_Totem, UUID...\nAl llegar el resultado del snapshot, el RPi revisa si la matricula corresponde a una matricula bajo el formato correcto\nen cuyo caso se da por valida. En caso que NO sea valida, se revisa si el Totem ha sido habilitado para editar matriculas, si ese \nfuera el caso se saca en pantalla el editor de matriculas. En caso contrario se saca un mensaje en pantalla indicando que tiene que \ndar retroceso y volver a entrar. En caso que el conductor presente una tarjeta rfid autorizada se puede continuar el proceso\nsin matricula.\nUna vez que la matricula esta dentro del formato, revisamos si el Totem ha sido habilitado para mostrar el boton en pantalla \nde Validacion (o de salida), de manera que el conductor una vez frente al Totem pueda dar por concluida la pesada. \nEn este punto el Totem espera o un QR o un RFID o un Detector de vehiculo o un Boton de Validacion para poder seguir. Una vez detectado algunos de Estos\ncondiciones, el peso en ese momento en la bascula se recoge como peso estable. Para dar por finalizada tenemos que revisar si hay algunos\nparametros configurados como obligatorio por ejemplo es obligatorio presentar el QR y/o clicar el boton de Validacion, de manera que hasta \nque no se cumplan los obligatorios junto con las condiciones anteriores no se puede continuar. Una vez recibido el ultimo parametro\nobligatorio damos por finalizada la pesada y se pone el semaforo en Verde para que el vehiculo abandone la bascula.\nAl momento de poner el semaforo en verde se envia el registro al supervisor para que lo registre en BD, incluyendo ahora los datos recogidos como matricula, \nhora de salida, firma, etc. \n\n", - "x": 660, + "x": 580, "y": 1440, "wires": [] }, @@ -3955,7 +3958,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 870, + "x": 790, "y": 1180, "wires": [ [ @@ -3996,7 +3999,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1730, + "x": 1650, "y": 1140, "wires": [ [ @@ -4026,7 +4029,7 @@ "links": [ "8356acc61776cae7" ], - "x": 1925, + "x": 1845, "y": 1020, "wires": [] }, @@ -4040,7 +4043,7 @@ "links": [ "f47c019e5baa0206" ], - "x": 1925, + "x": 1845, "y": 1060, "wires": [] }, @@ -4056,7 +4059,7 @@ "freq": "", "out": "out", "bcm": true, - "x": 2300, + "x": 2220, "y": 1180, "wires": [] }, @@ -4070,7 +4073,7 @@ "links": [ "0df085dfc5184645" ], - "x": 1515, + "x": 1435, "y": 1240, "wires": [] }, @@ -4089,7 +4092,7 @@ "param_3": "", "type_param_3": "Namespace", "f_use_debug": false, - "x": 1070, + "x": 990, "y": 960, "wires": [ [ @@ -4117,7 +4120,7 @@ "from": "", "to": "", "reg": false, - "x": 1200, + "x": 1120, "y": 960, "wires": [ [] @@ -4133,7 +4136,7 @@ "links": [ "d3223fe2481ad802" ], - "x": 1045, + "x": 965, "y": 1000, "wires": [] }, @@ -4147,7 +4150,7 @@ "links": [ "f47c019e5baa0206" ], - "x": 1215, + "x": 1135, "y": 1120, "wires": [] }, @@ -4167,7 +4170,7 @@ "className": "", "topic": "", "name": "edit plate", - "x": 1400, + "x": 1320, "y": 1240, "wires": [ [ @@ -4185,7 +4188,7 @@ "links": [ "8356acc61776cae7" ], - "x": 1045, + "x": 965, "y": 1380, "wires": [] }, @@ -4201,7 +4204,7 @@ "createDir": true, "overwriteFile": "true", "encoding": "none", - "x": 1230, + "x": 1150, "y": 1040, "wires": [ [] @@ -4220,7 +4223,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1080, + "x": 1000, "y": 1140, "wires": [ [ @@ -4251,7 +4254,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1070, + "x": 990, "y": 1080, "wires": [ [ @@ -4272,7 +4275,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1090, + "x": 1010, "y": 1320, "wires": [ [ @@ -4302,7 +4305,7 @@ "links": [ "0df0c819dae1658d" ], - "x": 1265, + "x": 1185, "y": 1300, "wires": [] }, @@ -4316,7 +4319,7 @@ "links": [ "0df0c819dae1658d" ], - "x": 1265, + "x": 1185, "y": 1260, "wires": [] }, @@ -4330,7 +4333,7 @@ "links": [ "0df0c819dae1658d" ], - "x": 1265, + "x": 1185, "y": 1340, "wires": [] }, @@ -4344,7 +4347,7 @@ "links": [ "656a6ccd2e2d8874" ], - "x": 2105, + "x": 2025, "y": 1040, "wires": [] }, @@ -4357,7 +4360,7 @@ "links": [ "41b4309de63a6bba" ], - "x": 415, + "x": 335, "y": 1260, "wires": [ [ @@ -4375,7 +4378,7 @@ "6fca6204952f0b67", "cf7b0c00631682b2" ], - "x": 2145, + "x": 2065, "y": 1200, "wires": [ [ @@ -5070,7 +5073,8 @@ "5a4bb45aef7a1433", "f75c248d56207759", "b6a2b0d90858876e", - "42499939644932e1" + "42499939644932e1", + "a0f7956de8f33a0b" ], "x": 1725, "y": 820, @@ -5151,7 +5155,7 @@ "z": "953e8740401d175c", "g": "e3bc37824e09b774", "name": "buttons \\n decoder", - "func": "\nif (msg.topic === \"tipoconfig\"){\n let tiposDatosGlobal = flow.get(\"tiposDatosGlobal\") || {\"1\": [], \"2\": [], \"3\": [], \"4\": [], \"5\": [], \"6\": []};\n\nconst tipo = msg.payload.tipo; // ej: 2\nconst datos = msg.payload.datos; // array con los parámetros\n\ntiposDatosGlobal[tipo] = datos;\n\nflow.set(\"tiposDatosGlobal\", tiposDatosGlobal);\n\nreturn ;\n}\nelse if (msg.payload.hasOwnProperty(\"seccion\")) {\n\n\n /* Los diferentes \"case\" corresponden a la pulsacion de botones de los diferentes menus de configuracion\n Cada menu solicitado mediante el click de un boton devuelve el nombre del \"div\" en el archivo html que debemos habilitar\n para que se muestre en pantalla.\n Si la entrada no consigue ningun case que se ajuste entonces caemos en el \"default\" y ahi buscamos primero si hay\n algun mensaje en \"origen\", lo que corresponde a la actualizacion de alguna de las tablas dinamicas que se generan\n para modificar cada tipo de parametro. Luego se revisan los \"modalCallback\" los cuales no indican que la respuesta\n de la notificacion de alguna accion importante ha sido positiva (\"SI\").\n \n */\n\n switch (msg.payload.seccion) {\n\n case \"menu-principal\": // boton de configuracion en pantalla principal para acceder al menu principal \n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", false)\n return { payload: { pantalla: \"menu-principal\" } }\n\n case \"editar\": // boton para acceder al menu de edicion de la configuracion\n return { payload: { pantalla: \"editar\" } }\n\n case \"pruebas\": // boton para acceder al menu de pruebas del sistema\n return { payload: { pantalla: \"pruebas\" } }\n\n case \"semaforo\": // boton para acceder al menu prueba del semaforo\n return { payload: { pantalla: \"semaforo\" } }\n\n case \"otras\": // boton para acceder al menu de otras acciones\n return { payload: { pantalla: \"otras\" } }\n\n case \"regresar\": // boton para regresar al menu principal desde otras acciones y desde menu de pruebas del sistema\n return { payload: { pantalla: \"menu-principal\" } }\n\n case \"salvar\": // boton para almacenar los cambios en el archivo de configuracion en el menu de edicion de la configuracion\n return ([{ payload: { pantalla: \"editar\" }, modalText: \"Desea MODIFICAR el archivo de CONFIGURACION ?\", modalCallback: \"modificar\" }, null, null, null])\n\n case \"cancelar\": // boton para cancelar y salir del menu de editar configuracion\n return ([{ payload: { pantalla: \"menu-principal\" } }, null])\n\n case \"semaforo_on\": // boton para poner el semaforo en rojo\n flow.set(\"semaforo\", \"red\")\n return ([{ payload: { pantalla: \"semaforo\" }, semaforo: \"firebrick\" }, null, { payload: 0 }])\n\n case \"semaforo_off\": // boton para poner el semaforo en verde\n flow.set(\"semaforo\", \"green\")\n return ([{ payload: { pantalla: \"semaforo\" }, semaforo: \"green\" }, null, { payload: 1 }])\n\n case \"salir-semaforo\": // boton de salida de la pantalla de prueba del semaforo\n return ({ payload: { pantalla: \"pruebas\" } })\n\n case \"barrera\": // boton de activar barrera\n return ({ payload: { pantalla: \"barrera\" }, barrera: false })\n\n case \"general\": // boton para ver / modificar tabla con los parametros generales\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"general\", tabla: flow.get(\"config_general\") }, null, null])\n\n case \"camara\": // boton para ver / modificar tabla con los parametros de la camara\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"camara\", tabla: flow.get(\"config_camara\") }, null, null])\n\n case \"conexion\": // boton para ver / modificar tabla con los parametros de conexion\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"conexion\", tabla: flow.get(\"config_conex\") }, null, null])\n\n case \"sensores\": // boton para ver / modificar tabla con los parametros de sensores\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"sensores\", tabla: flow.get(\"config_sensor\") }, null, null])\n\n case \"mensajes\": // boton para ver / modificar tabla con los parametros de los mensajes\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"mensajes\", tabla: flow.get(\"config_mensajes\") }, null, null])\n\n case \"tiempos\": // boton para ver / modificar tabla con los parametros del tiempo\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"tiempos\", tabla: flow.get(\"config_tiempos\") }, null, null])\n\n case \"aplicar\": // boton para aplicar la configuracion al RPi, en el menu-principal\n return ([{ payload: { pantalla: \"menu-principal\" }, topic: \"menu-principal\", modalText: \"Desea Recargar la CONFIGURACION ?\", modalCallback: \"recargar\" }, null, null])\n\n case \"reboot\": // boton para resetear el RPi en el menu de otras acciones\n return ([{ payload: { pantalla: \"otras\" }, topic: \"otras\", modalText: \"Desea Reiniciar el TOTEM ?\", modalCallback: \"reiniciar\" }, null, null])\n\n case \"apagar\": // boton para apagar el RPi en el menu de otras acciones\n return ([{ payload: { pantalla: \"otras\" }, topic: \"otras\", modalText: \"Desea APAGAR el TOTEM ?\", modalCallback: \"apagar\" }, null, null])\n\n case \"snapshot\": // boton para pedir snapshot\n flow.set(\"snapshot_cam\", true)\n flow.set(\"escuchar_cam\", false)\n //flow.set(\"context_cam\", false)\n let msgrqt = {}\n msgrqt.payload = {}\n msgrqt.url = \"http://\" + flow.get(\"camara_IP\") + \"/cgi-bin/trafficSnap.cgi?action=manSnap&channel=1\"\n msgrqt.topic = \"snapshot_rqtd\"\n return ([{ payload: { pantalla: \"snapshot\" }, topic: \"snapshot\" }, null, null, null, null, msgrqt])\n case \"repetir\":\n case \"contexto\" : // boton para pedir snapshot de camara de contexto\n flow.set(\"context_cam\", true)\n flow.set(\"escuchar_cam\", false)\n //flow.set(\"snapshot_cam\", false)\n let msgrqt2 = {}\n msgrqt2.payload = {}\n msgrqt2.url = \"http://\" + flow.get(\"camara_contexto_IP\") + \"/cgi-bin/snapshot.cgi?channel=1\"\n msgrqt2.topic = \"context_rqtd\"\n return ([{ payload: { pantalla: \"contexto\" }, topic: \"contexto\" }, null, null, null, null, null, null, null, msgrqt2])\n\n case \"escuchar\": // boton para escuchar camara ( recibir todas la imagenes que envie)\n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", true)\n //flow.set(\"context_cam\", false)\n return ([{ payload: { pantalla: \"escuchar\" }, topic: \"escuchar\" }, null, null])\n\n case \"left\": // Boton para ir a la imagen anterior en escuchar camara\n flow.set(\"getfromarray\", \"left\")\n return ([{ payload: { pantalla: \"escuchar\" }, topic: \"escuchar\" }, null, null])\n\n case \"right\": // Boton para ir a la siguiente imagen en escuchar camara\n flow.set(\"getfromarray\", \"right\")\n return ([{ payload: { pantalla: \"escuchar\" }, topic: \"escuchar\" }, null, null])\n\n case \"salir-snapshot\": // boton de salida de la pantalla de ver snapshot\n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", false)\n flow.set(\"context_cam\", false)\n return ({ payload: { pantalla: \"pruebas\" } })\n\n case \"salir-barrera\": // boton de salida de la barrera\n\n return ({ payload: { pantalla: \"pruebas\" }, barrera: false })\n\n case \"salir-escuchar\": // boton de salida de la pantalla de escuchar la camara\n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", false)\n flow.set(\"snapshot_array\", [])\n flow.set(\"snapshot_readindex\", 0)\n flow.set(\"snapshot_saveindex\", 0)\n flow.set(\"snapshot_indexmax\", 0)\n flow.set(\"getfromarray\", \"\")\n return ({ payload: { pantalla: \"pruebas\" } })\n\n case \"activar-barrera\": // boton de activar barrera\n node.send([{ payload: { pantalla: \"barrera\" }, barrera: true }, null, null, null, null, null, null, { payload: 0 }])\n setTimeout(function () {\n node.send([null, null, null, null, null, null, null, { payload: 1 }])\n }, flow.get(\"relay2_pulse\"));\n return\n\n case \"cancel-table\": // boton de CANCEL de las tablas de configuracion de parametros\n return ({ payload: { pantalla: \"editar\" } })\n\n case \"salir\": // boton de salida del menu principal\n return ({ payload: { pantalla: \"principal\" } })\n\n case \"tipos\": // boton de salida al menu editar\n msg.payload = {pantalla: \"tipos\", tiposDatos: flow.get(\"tiposDatosGlobal\") || {\"1\": [], \"2\": [], \"3\": [], \"4\": [], \"5\": [], \"6\": []}};\n return (msg)\n\n case \"volver_tipos\": // boton de salida al menu editar\n return ({ payload: { pantalla: \"editar\" } })\n\n case \"ayuda\": // boton de salida al menu editar\n return ({ payload: { pantalla: \"ayuda\" } })\n\n case \"salir-ayuda\": // boton de salida al menu editar\n return ({ payload: { pantalla: \"editar\" } })\n\n //------------------Añadido por david--------------------------\n case \"sensor\": // boton para acceder al menu de prueba del sensor\n flow.set(\"display_detector\", true)\n return ({ payload: { pantalla: \"sensor\" } })\n\n case \"salir-sensor\": // boton de salida de la pantalla de prueba del sensor\n flow.set(\"display_detector\", false)\n return ({ payload: { pantalla: \"pruebas\" } })\n //---------------Fin añadido por david-------------\n \n default: // chequeamos si tenemos acciones a realizar para lo cual revisamos el \"origen\" (actualizar en memoria sin preguntar usuario) y/o el \"modalCallback\" con esto sabemos de que tabla es el mensaje enviado por el MODAL\n node.send([null, null, null, null, null, null, msg])\n if (msg.origen == \"actualizar\") {// salvar en memoria la configuracion que ha sido modificada (el Topic nos dice cual configuracion)\n let myconfig = msg.payload.table // table contiene el resultado de leer la tabla con todos los valores normalizados en uibuilder\n \n switch (msg.topic) { // guardamos cambios en el tipo de configuracion correspondiente\n case \"general\":\n flow.set(\"config_general\", myconfig)\n break;\n case \"conexion\":\n // ==================================== VALIDAMOS todas las IP de la configuracion ==================================================\n //node.warn(\"valor 1 = \" + myconfig[1].valor)\n \n if (validarIP(myconfig[1].valor)){\n //node.warn(\"valor 1 = \" + myconfig[1].valor)\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del DISPOSITIVO NO es correcta ?\", modalType:\"notificacion\" }, null, null])\n }\n \n if (validarIP(myconfig[2].valor)) {\n //node.warn(\"valor 2 = \" + myconfig[2].valor)\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del ROUTER NO es correcta ?\", modalType: \"notificacion\" }, null, null])\n }\n \n if (validarIP(myconfig[5].valor)) {\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del WEBSOCKET NO es correcta ?\", modalType: \"notificacion\" }, null, null])\n }\n \n if (validarIP(myconfig[12].valor)) {\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del TOTEM pareja NO es correcta ?\", modalType: \"notificacion\" }, null, null])\n }\n \n break;\n case \"camara\":\n flow.set(\"config_camara\", myconfig)\n break;\n case \"sensores\":\n flow.set(\"config_sensor\", myconfig)\n break;\n case \"tiempos\":\n flow.set(\"config_tiempos\", myconfig)\n break;\n case \"mensajes\":\n flow.set(\"config_mensajes\", myconfig)\n break;\n }\n return { payload: { pantalla: \"editar\" } }\n }\n\n \n //msg.hello = \"hello 3\"\n //node.send([null, null, null, null, null, null, msg])\n return null\n\n }\n} // final del SWITCH\nelse if (msg.modalCallback == \"modificar\") {\n /*\n let config_totem = []\n config_totem = flow.get(\"config_general\").concat(flow.get(\"config_camara\"), flow.get(\"config_conex\"), flow.get(\"config_sensor\"), flow.get(\"config_mensajes\"), flow.get(\"config_tiempos\"), flow.get(\"tiposDatosGlobal\"))\n flow.set(\"config_totem\", config_totem)\n let jsontest = toJson(flow.get(\"config_totem\"));\n */\n let config_totem = {\n general: flow.get(\"config_general\") || [],\n camara: flow.get(\"config_camara\") || [],\n conexion: flow.get(\"config_conex\") || [],\n sensores: flow.get(\"config_sensor\") || [],\n mensajes: flow.get(\"config_mensajes\") || [],\n tiempos: flow.get(\"config_tiempos\") || [],\n tipos: flow.get(\"tiposDatosGlobal\") || {}\n };\n\n flow.set(\"config_totem\", config_totem);\n\n msg.payload = JSON.stringify(config_totem, null, 2) + \"\\n\\r\";\n //msg.payload = JSON.stringify(jsontest, null, 2) + \"\\n\\r\"; // el factor 2 agrega 2 espacio al inicio de cada elemento\n //y agrega tambien una linea lo cual es interesante para la legibilidad del formato y para que los clientes SFTP puedan\n //leer bien el archivo, como es el caso de Mobaxterm.\n\n return ([{ payload: { pantalla: \"menu-principal\" } }, msg, null])\n}\nelse if (msg.modalCallback == \"recargar\") {\n return ([null, null, null, { payload: 1 }])\n}\n\nelse if (msg.modalCallback == \"reiniciar\") {\n return ([null, null, null, null, { payload: \"sudo shutdown -r now\" }])\n}\n\nelse if (msg.modalCallback == \"apagar\") {\n return ([null, null, null, null, { payload: \"sudo poweroff\" }])\n}\n\n//============================================== FUNCIONES ==============================================================\n//esta funcion convierte un array con elementos [parametro, valor] en un objeto Json respetando los tipos de parametro (numerico, booleano, texto)\nfunction toJson(data) {\n let newJson = {}\n /*\n data.forEach(item => {\n let param = item.parametro;\n let value = item.valor;\n */\n for (let i = 0; i < data.length; i++) {\n let param = data[i].parametro;\n let value = data[i].valor;\n\n // Parseo de tipos igual que antes\n if (typeof value === 'string') {\n if (value.toLowerCase() === \"true\") {\n newJson[param] = true;\n } else if (value.toLowerCase() === \"false\") {\n newJson[param] = false;\n } else if (!isNaN(value) && value.trim() !== \"\") {\n newJson[param] = Number(value);\n } else {\n newJson[param] = value;\n }\n } else {\n // Por si el value ya viene como booleano o número real\n newJson[param] = value;\n }\n }\n return (newJson)\n}\n\n//=======================================================================================================================================\n\nfunction validarIP(ip) {\n const regex = /^(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)$/;\n return regex.test(ip);\n}", + "func": "\nif (msg.topic === \"tipoconfig\"){\n let tiposDatosGlobal = flow.get(\"tiposDatosGlobal\") || {\"1\": [], \"2\": [], \"3\": [], \"4\": [], \"5\": [], \"6\": []};\n\nconst tipo = msg.payload.tipo; // ej: 2\nconst datos = msg.payload.datos; // array con los parámetros\n\ntiposDatosGlobal[tipo] = datos;\n\nflow.set(\"tiposDatosGlobal\", tiposDatosGlobal);\n\nreturn ;\n}\nelse if (msg.payload.hasOwnProperty(\"seccion\")) {\n\n\n /* Los diferentes \"case\" corresponden a la pulsacion de botones de los diferentes menus de configuracion\n Cada menu solicitado mediante el click de un boton devuelve el nombre del \"div\" en el archivo html que debemos habilitar\n para que se muestre en pantalla.\n Si la entrada no consigue ningun case que se ajuste entonces caemos en el \"default\" y ahi buscamos primero si hay\n algun mensaje en \"origen\", lo que corresponde a la actualizacion de alguna de las tablas dinamicas que se generan\n para modificar cada tipo de parametro. Luego se revisan los \"modalCallback\" los cuales no indican que la respuesta\n de la notificacion de alguna accion importante ha sido positiva (\"SI\").\n \n */\n\n switch (msg.payload.seccion) {\n\n case \"menu-principal\": // boton de configuracion en pantalla principal para acceder al menu principal \n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", false)\n return { payload: { pantalla: \"menu-principal\" } }\n\n case \"editar\": // boton para acceder al menu de edicion de la configuracion\n return { payload: { pantalla: \"editar\" } }\n\n case \"pruebas\": // boton para acceder al menu de pruebas del sistema\n return { payload: { pantalla: \"pruebas\" } }\n\n case \"semaforo\": // boton para acceder al menu prueba del semaforo\n return { payload: { pantalla: \"semaforo\" } }\n\n case \"otras\": // boton para acceder al menu de otras acciones\n return { payload: { pantalla: \"otras\" } }\n\n case \"regresar\": // boton para regresar al menu principal desde otras acciones y desde menu de pruebas del sistema\n return { payload: { pantalla: \"menu-principal\" } }\n\n case \"salvar\": // boton para almacenar los cambios en el archivo de configuracion en el menu de edicion de la configuracion\n return ([{ payload: { pantalla: \"editar\" }, modalText: \"Desea MODIFICAR el archivo de CONFIGURACION ?\", modalCallback: \"modificar\" }, null, null, null])\n\n case \"cancelar\": // boton para cancelar y salir del menu de editar configuracion\n return ([{ payload: { pantalla: \"menu-principal\" } }, null])\n\n case \"semaforo_on\": // boton para poner el semaforo en rojo\n flow.set(\"semaforo\", \"red\")\n return ([{ payload: { pantalla: \"semaforo\" }, semaforo: \"firebrick\" }, null, { payload: 0 }])\n\n case \"semaforo_off\": // boton para poner el semaforo en verde\n flow.set(\"semaforo\", \"green\")\n return ([{ payload: { pantalla: \"semaforo\" }, semaforo: \"green\" }, null, { payload: 1 }])\n\n case \"salir-semaforo\": // boton de salida de la pantalla de prueba del semaforo\n return ({ payload: { pantalla: \"pruebas\" } })\n\n case \"barrera\": // boton de activar barrera\n return ({ payload: { pantalla: \"barrera\" }, barrera: false })\n\n case \"general\": // boton para ver / modificar tabla con los parametros generales\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"general\", tabla: flow.get(\"config_general\") }, null, null])\n\n case \"camara\": // boton para ver / modificar tabla con los parametros de la camara\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"camara\", tabla: flow.get(\"config_camara\") }, null, null])\n\n case \"conexion\": // boton para ver / modificar tabla con los parametros de conexion\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"conexion\", tabla: flow.get(\"config_conex\") }, null, null])\n\n case \"sensores\": // boton para ver / modificar tabla con los parametros de sensores\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"sensores\", tabla: flow.get(\"config_sensor\") }, null, null])\n\n case \"mensajes\": // boton para ver / modificar tabla con los parametros de los mensajes\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"mensajes\", tabla: flow.get(\"config_mensajes\") }, null, null])\n\n case \"tiempos\": // boton para ver / modificar tabla con los parametros del tiempo\n return ([{ payload: { pantalla: \"tabla-general\" }, topic: \"tiempos\", tabla: flow.get(\"config_tiempos\") }, null, null])\n\n case \"aplicar\": // boton para aplicar la configuracion al RPi, en el menu-principal\n return ([{ payload: { pantalla: \"menu-principal\" }, topic: \"menu-principal\", modalText: \"Desea Recargar la CONFIGURACION ?\", modalCallback: \"recargar\" }, null, null])\n\n case \"reboot\": // boton para resetear el RPi en el menu de otras acciones\n return ([{ payload: { pantalla: \"otras\" }, topic: \"otras\", modalText: \"Desea Reiniciar el TOTEM ?\", modalCallback: \"reiniciar\" }, null, null])\n\n case \"apagar\": // boton para apagar el RPi en el menu de otras acciones\n return ([{ payload: { pantalla: \"otras\" }, topic: \"otras\", modalText: \"Desea APAGAR el TOTEM ?\", modalCallback: \"apagar\" }, null, null])\n\n case \"snapshot\": // boton para pedir snapshot\n flow.set(\"snapshot_cam\", true)\n flow.set(\"escuchar_cam\", false)\n //flow.set(\"context_cam\", false)\n let msgrqt = {}\n msgrqt.payload = {}\n msgrqt.url = \"http://\" + flow.get(\"camara_IP\") + \"/cgi-bin/trafficSnap.cgi?action=manSnap&channel=1\"\n msgrqt.topic = \"snapshot_rqtd\"\n return ([{ payload: { pantalla: \"snapshot\" }, topic: \"snapshot\" }, null, null, null, null, msgrqt])\n case \"repetir\":\n case \"contexto\" : // boton para pedir snapshot de camara de contexto\n flow.set(\"context_cam\", true)\n flow.set(\"escuchar_cam\", false)\n //flow.set(\"snapshot_cam\", false)\n let msgrqt2 = {}\n msgrqt2.payload = {}\n msgrqt2.url = \"http://\" + flow.get(\"camara_contexto_IP\") + \"/cgi-bin/snapshot.cgi?channel=1\"\n msgrqt2.topic = \"context_rqtd\"\n return ([{ payload: { pantalla: \"contexto\" }, topic: \"contexto\" }, null, null, null, null, null, null, null, msgrqt2])\n\n case \"escuchar\": // boton para escuchar camara ( recibir todas la imagenes que envie)\n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", true)\n //flow.set(\"context_cam\", false)\n return ([{ payload: { pantalla: \"escuchar\" }, topic: \"escuchar\" }, null, null])\n\n case \"left\": // Boton para ir a la imagen anterior en escuchar camara\n flow.set(\"getfromarray\", \"left\")\n return ([{ payload: { pantalla: \"escuchar\" }, topic: \"escuchar\" }, null, null])\n\n case \"right\": // Boton para ir a la siguiente imagen en escuchar camara\n flow.set(\"getfromarray\", \"right\")\n return ([{ payload: { pantalla: \"escuchar\" }, topic: \"escuchar\" }, null, null])\n\n case \"salir-snapshot\": // boton de salida de la pantalla de ver snapshot\n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", false)\n flow.set(\"context_cam\", false)\n return ({ payload: { pantalla: \"pruebas\" } })\n\n case \"salir-barrera\": // boton de salida de la barrera\n\n return ({ payload: { pantalla: \"pruebas\" }, barrera: false })\n\n case \"salir-escuchar\": // boton de salida de la pantalla de escuchar la camara\n flow.set(\"snapshot_cam\", false)\n flow.set(\"escuchar_cam\", false)\n flow.set(\"snapshot_array\", [])\n flow.set(\"snapshot_readindex\", 0)\n flow.set(\"snapshot_saveindex\", 0)\n flow.set(\"snapshot_indexmax\", 0)\n flow.set(\"getfromarray\", \"\")\n return ({ payload: { pantalla: \"pruebas\" } })\n\n case \"activar-barrera\": // boton de activar barrera\n node.send([{ payload: { pantalla: \"barrera\" }, barrera: true }, null, null, null, null, null, null, { payload: 0 }])\n setTimeout(function () {\n node.send([null, null, null, null, null, null, null, { payload: 1 }])\n }, flow.get(\"relay2_pulse\"));\n return\n\n case \"cancel-table\": // boton de CANCEL de las tablas de configuracion de parametros\n return ({ payload: { pantalla: \"editar\" } })\n\n case \"salir\": // boton de salida del menu principal\n return ({ payload: { pantalla: \"principal\" } })\n\n case \"tipos\": // boton de salida al menu editar\n msg.payload = {pantalla: \"tipos\", tiposDatos: flow.get(\"tiposDatosGlobal\") || {\"1\": [], \"2\": [], \"3\": [], \"4\": [], \"5\": [], \"6\": []}};\n return (msg)\n\n case \"volver_tipos\": // boton de salida al menu editar\n return ({ payload: { pantalla: \"editar\" } })\n\n case \"ayuda\": // boton de salida al menu editar\n return ({ payload: { pantalla: \"ayuda\" } })\n\n case \"salir-ayuda\": // boton de salida al menu editar\n return ({ payload: { pantalla: \"editar\" } })\n\n //------------------Añadido por david--------------------------\n case \"sensor\": // boton para acceder al menu de prueba del sensor\n flow.set(\"display_detector\", true)\n return ({ payload: { pantalla: \"sensor\" } })\n\n case \"salir-sensor\": // boton de salida de la pantalla de prueba del sensor\n flow.set(\"display_detector\", false)\n return ({ payload: { pantalla: \"pruebas\" } })\n //---------------Fin añadido por david-------------\n \n default: // chequeamos si tenemos acciones a realizar para lo cual revisamos el \"origen\" (actualizar en memoria sin preguntar usuario) y/o el \"modalCallback\" con esto sabemos de que tabla es el mensaje enviado por el MODAL\n node.send([null, null, null, null, null, null, msg])\n if (msg.origen == \"actualizar\") {// salvar en memoria la configuracion que ha sido modificada (el Topic nos dice cual configuracion)\n let myconfig = msg.payload.table // table contiene el resultado de leer la tabla con todos los valores normalizados en uibuilder\n \n switch (msg.topic) { // guardamos cambios en el tipo de configuracion correspondiente\n case \"general\":\n flow.set(\"config_general\", myconfig)\n break;\n case \"conexion\":\n // ==================================== VALIDAMOS todas las IP de la configuracion ==================================================\n //node.warn(\"valor 1 = \" + myconfig[1].valor)\n \n if (validarIP(myconfig[1].valor)){\n //node.warn(\"valor 1 = \" + myconfig[1].valor)\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del DISPOSITIVO NO es correcta ?\", modalType:\"notificacion\" }, null, null])\n }\n \n if (validarIP(myconfig[2].valor)) {\n //node.warn(\"valor 2 = \" + myconfig[2].valor)\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del ROUTER NO es correcta ?\", modalType: \"notificacion\" }, null, null])\n }\n \n if (validarIP(myconfig[5].valor)) {\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del WEBSOCKET NO es correcta ?\", modalType: \"notificacion\" }, null, null])\n }\n \n if (validarIP(myconfig[13].valor)) {\n flow.set(\"config_conex\", myconfig)\n }\n else {\n return ([{ payload: { pantalla: \"editar\" }, topic: \"error\", modalText: \"La IP del TOTEM pareja NO es correcta ?\", modalType: \"notificacion\" }, null, null])\n }\n \n break;\n case \"camara\":\n flow.set(\"config_camara\", myconfig)\n break;\n case \"sensores\":\n flow.set(\"config_sensor\", myconfig)\n break;\n case \"tiempos\":\n flow.set(\"config_tiempos\", myconfig)\n break;\n case \"mensajes\":\n flow.set(\"config_mensajes\", myconfig)\n break;\n }\n return { payload: { pantalla: \"editar\" } }\n }\n\n \n //msg.hello = \"hello 3\"\n //node.send([null, null, null, null, null, null, msg])\n return null\n\n }\n} // final del SWITCH\nelse if (msg.modalCallback == \"modificar\") {\n /*\n let config_totem = []\n config_totem = flow.get(\"config_general\").concat(flow.get(\"config_camara\"), flow.get(\"config_conex\"), flow.get(\"config_sensor\"), flow.get(\"config_mensajes\"), flow.get(\"config_tiempos\"), flow.get(\"tiposDatosGlobal\"))\n flow.set(\"config_totem\", config_totem)\n let jsontest = toJson(flow.get(\"config_totem\"));\n */\n let config_totem = {\n general: flow.get(\"config_general\") || [],\n camara: flow.get(\"config_camara\") || [],\n conexion: flow.get(\"config_conex\") || [],\n sensores: flow.get(\"config_sensor\") || [],\n mensajes: flow.get(\"config_mensajes\") || [],\n tiempos: flow.get(\"config_tiempos\") || [],\n tipos: flow.get(\"tiposDatosGlobal\") || {}\n };\n\n flow.set(\"config_totem\", config_totem);\n\n msg.payload = JSON.stringify(config_totem, null, 2) + \"\\n\\r\";\n //msg.payload = JSON.stringify(jsontest, null, 2) + \"\\n\\r\"; // el factor 2 agrega 2 espacio al inicio de cada elemento\n //y agrega tambien una linea lo cual es interesante para la legibilidad del formato y para que los clientes SFTP puedan\n //leer bien el archivo, como es el caso de Mobaxterm.\n\n return ([{ payload: { pantalla: \"menu-principal\" } }, msg, null])\n}\nelse if (msg.modalCallback == \"recargar\") {\n return ([null, null, null, { payload: 1 }])\n}\n\nelse if (msg.modalCallback == \"reiniciar\") {\n return ([null, null, null, null, { payload: \"sudo shutdown -r now\" }])\n}\n\nelse if (msg.modalCallback == \"apagar\") {\n return ([null, null, null, null, { payload: \"sudo poweroff\" }])\n}\n\n//============================================== FUNCIONES ==============================================================\n//esta funcion convierte un array con elementos [parametro, valor] en un objeto Json respetando los tipos de parametro (numerico, booleano, texto)\nfunction toJson(data) {\n let newJson = {}\n /*\n data.forEach(item => {\n let param = item.parametro;\n let value = item.valor;\n */\n for (let i = 0; i < data.length; i++) {\n let param = data[i].parametro;\n let value = data[i].valor;\n\n // Parseo de tipos igual que antes\n if (typeof value === 'string') {\n if (value.toLowerCase() === \"true\") {\n newJson[param] = true;\n } else if (value.toLowerCase() === \"false\") {\n newJson[param] = false;\n } else if (!isNaN(value) && value.trim() !== \"\") {\n newJson[param] = Number(value);\n } else {\n newJson[param] = value;\n }\n } else {\n // Por si el value ya viene como booleano o número real\n newJson[param] = value;\n }\n }\n return (newJson)\n}\n\n//=======================================================================================================================================\n\nfunction validarIP(ip) {\n const regex = /^(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)$/;\n return regex.test(ip);\n}", "outputs": 9, "timeout": 0, "noerr": 0, @@ -5783,7 +5787,7 @@ "z": "953e8740401d175c", "g": "595fb0f89af85c42", "name": "config edit", - "func": "const config = JSON.parse(msg.payload);\nmsg ={}; // clear msg object\nlet read;\nlet config_general = []\nlet config_tiempos = []\nlet config_mensajes = []\nlet config_sensor = []\nlet config_camara = []\nlet config_conex = []\nlet config_tipos = []\n\nconfig.general = toObject(config.general);\nconfig.camara = toObject(config.camara);\nconfig.conexion = toObject(config.conexion);\nconfig.sensores = toObject(config.sensores);\nconfig.mensajes = toObject(config.mensajes);\nconfig.tiempos = toObject(config.tiempos);\n\nconst tiposOriginales = config.tipos || {};\n\n\n// Aseguramos que cada tipo esté como array vacío si no tiene nada\nfor (let i = 1; i <= 6; i++) {\n if (!Array.isArray(tiposOriginales[i])) {\n tiposOriginales[i] = [];\n }\n}\nflow.set(\"tiposDatosGlobal\", tiposOriginales);\n\nconfig.tipos = tiposOriginales;\n\n\n\n//**************************** Parametros de Validacion OBLIGATORIOS para PESADA FINALIZADA ***************************************\n// Estos parametros pueden ser: QR / RFID / Boton Validacion / Detector Vehiculo \n//*********************************************************************************************************************************\n/*read = config.QR_obligatorio;\nconfig_general.push({ \"parametro\": \"QR_obligatorio\", \"valor\": read })\nflow.set(\"QR_obligatorio\", read);\nread = config.RFID_obligatorio;\nconfig_general.push({ \"parametro\": \"RFID_obligatorio\", \"valor\": read })\nflow.set(\"RFID_obligatorio\", read);\n\nread = config.general.funcion_totem;\nconfig_general.push({ \"parametro\": \"funcion_totem\", \"valor\": read })\nflow.set(\"funcion_totem\", read);\n\nread = config.general.identificacion_esp;\nconfig_general.push({ \"parametro\": \"identificacion_esp\", \"valor\": read })\nflow.set(\"identificacion_esp\", read);\n\nread = config.tiempos.timer1;\nconfig_tiempos.push({ \"parametro\": \"timer1\", \"valor\": read })\nflow.set(\"timer1\", read);\n\n*/\n\nread = config.general.Tipo_Configuracion;\nconfig_general.push({ \"parametro\": \"Tipo_Configuracion\", \"valor\": read })\nflow.set(\"Tipo_Configuracion\", read);\n\nread = config.general.ID_obligatorio;\nconfig_general.push({ \"parametro\": \"ID_obligatorio\", \"valor\": read })\nflow.set(\"ID_obligatorio\", read);\n\nread = config.general.salida_ID_obligatorio;\nconfig_general.push({ \"parametro\": \"salida_ID_obligatorio\", \"valor\": read })\nflow.set(\"salida_ID_obligatorio\", read);\n\nread = config.general.detector_obligatorio;\nconfig_general.push({ \"parametro\": \"detector_obligatorio\", \"valor\": read })\nflow.set(\"detector_obligatorio\", read);\n\nread = config.general.boton_obligatorio;\nconfig_general.push({ \"parametro\": \"boton_obligatorio\", \"valor\": read })\nflow.set(\"boton_obligatorio\", read);\nread = config.general.firma_obligatoria;\nconfig_general.push({ \"parametro\": \"firma_obligatoria\", \"valor\": read })\nflow.set(\"firma_obligatoria\", read);\nread = config.general.matricula_obligatoria;\nconfig_general.push({ \"parametro\": \"matricula_obligatoria\", \"valor\": read })\nflow.set(\"matricula_obligatoria\", read);\n\n\n\n//==================================================================================================================================\nread = config.general.edicion_matricula;\nconfig_general.push({ \"parametro\": \"edicion_matricula\", \"valor\": read })\nflow.set(\"edicion_matricula\", read); // Habilita la edicion de matricula\n\nread = config.general.botonsalida_activo;\nconfig_general.push({ \"parametro\": \"botonsalida_activo\", \"valor\": read })\nflow.set(\"botonsalida_activo\", read); // Permite poner boton de salida\n\nread = config.general.menu_activo;\nconfig_general.push({ \"parametro\": \"menu_activo\", \"valor\": read })\nflow.set(\"menu_activo\", read); // Permite poner los menus de contratacion\n\n/*\nread = config.general.firma_habilitada;\nconfig_general.push({ \"parametro\": \"firma_habilitada\", \"valor\": read })\nflow.set(\"firma_habilitada\", read); // Habilita la edicion de firma\n*/\n\nread = config.general.barrera_habilitada;\nconfig_general.push({ \"parametro\": \"barrera_habilitada\", \"valor\": read })\nflow.set(\"barrera_habilitada\", read); // Habilita el uso de barrera\n\nread = config.general.mostrar_ubicacion\nconfig_general.push({ \"parametro\": \"mostrar_ubicacion\", \"valor\": read })\nflow.set(\"mostrar_ubicacion\", read);\n\nread = config.general.switch_2_Totem_page;\nflow.set(\"switch_2_Totem_page\", read); // si igual a true cambia a pagina principal del totem siempre que se active la bascula\n// Con esto aseguramos que si queda abierta una ventana de configuracion se pueda quitar automaticamente\nconfig_general.push({ \"parametro\": \"switch_2_Totem_page\", \"valor\": read })\n\n//flow.set(\"peso\", read); // crea variable para recibir peso del supervisor\n\nread = parseInt(config.general.peso_minimo); // peso minimo para utilizado para indicar cuando hay vehiculo en la balanza o no\nconfig_general.push({ \"parametro\": \"peso_minimo\", \"valor\": read })\nflow.set(\"peso_minimo\", read);\n\nread = config.general.formatoPais; // peso minimo para utilizado para indicar cuando hay vehiculo en la balanza o no\nconfig_general.push({ \"parametro\": \"formatoPais\", \"valor\": read })\nflow.set(\"formatoPais\", read);\n\n//------------------- TOTEM MASTER tiempo de confirmacion -------------------------------------\nread = config.tiempos.confirm_master_time;\nconfig_tiempos.push({ \"parametro\": \"confirm_master_time\", \"valor\": read })\nflow.set(\"confirm_master_time\", read);\n//---------------------------------------------------------------------------------------------\n\n\nread = config.general.audio_level;\nconfig_general.push({ \"parametro\": \"audio_level\", \"valor\": read })\nflow.set(\"audio_level\", read);\n//-------------------------------------------------------\nread = config.general.mante_code_RFID; //codigo RFID para pasar a pantalla de mantenimiento\nconfig_general.push({ \"parametro\": \"mante_code_RFID\", \"valor\": read })\nflow.set(\"mante_code_RFID\", read)\nread = config.general.mante_code_QR; //codigo QR para pasar a pantalla de mantenimiento\nconfig_general.push({ \"parametro\": \"mante_code_QR\", \"valor\": read })\nflow.set(\"mante_code_QR\", read)\n\n//------------------------------- MENSAJES -------------------------\nread = config.tiempos.tiempo_mensaje;\nconfig_tiempos.push({ \"parametro\": \"tiempo_mensaje\", \"valor\": read })\nflow.set(\"tiempo_mensaje\", read);\n\nread = config.tiempos.relay2_pulse // duración del pulso generado en el relay 2 al terminar el pesaje para abrir barrera\nconfig_tiempos.push({ \"parametro\": \"relay2_pulse\", \"valor\": read })\nflow.set(\"relay2_pulse\", read);\n\nread = config.mensajes.mensaje2 // no se usa\nconfig_mensajes.push({ \"parametro\": \"mensaje2\", \"valor\": read })\nflow.set(\"mensaje2\", read);\nread = config.mensajes.mensaje1 // no se usa\nconfig_mensajes.push({ \"parametro\": \"mensaje1\", \"valor\": read })\nflow.set(\"mensaje1\", read);\nread = config.general.init_screen\nconfig_general.push({ \"parametro\": \"init_screen\", \"valor\": read })\nflow.set(\"init_screen\", read);\n\n//-------------------------------------------------------\nread = config.general.totem_serial // seriall asignado a este dispositivo\nconfig_general.push({ \"parametro\": \"totem_serial\", \"valor\": read })\nflow.set(\"totem_serial\", read);\n\n//---------------------------------------- Parametros por defecto del boton de salida para VALIDACION -----------------------------------------------------------\nread = config.mensajes.botonsalida_txt_def\nconfig_mensajes.push({ \"parametro\": \"botonsalida_txt_def\", \"valor\": read })\nflow.set(\"botonsalida_txt_def\", read)\nread = config.general.botonsalida_color_def\nconfig_general.push({ \"parametro\": \"botonsalida_color_def\", \"valor\": read })\nflow.set(\"botonsalida_color_def\", read)\nread = config.general.botonsalida_txt_color_def\nconfig_general.push({ \"parametro\": \"botonsalida_txt_color_def\", \"valor\": read })\nflow.set(\"botonsalida_txt_color_def\", read)\nread = config.mensajes.botonsalida_mensaje1\nconfig_mensajes.push({ \"parametro\": \"botonsalida_mensaje1\", \"valor\": read })\nflow.set(\"botonsalida_mensaje1\", read)\nread = config.mensajes.botonsalida_mensaje1_def\nconfig_mensajes.push({ \"parametro\": \"botonsalida_mensaje1_def\", \"valor\": read })\nflow.set(\"botonsalida_mensaje1_def\", read)\n\n//----------------------------------------- Textos Predefinidos -------------------------------------------------------------------------\nread = config.mensajes.men_pesaje_listo;\nconfig_mensajes.push({ \"parametro\": \"men_pesaje_listo\", \"valor\": read })\nflow.set(\"men_pesaje_listo\", read);\nread = config.mensajes.men_peso_estable;\nconfig_mensajes.push({ \"parametro\": \"men_peso_estable\", \"valor\": read })\nflow.set(\"men_peso_estable\", read);\nread = config.mensajes.men_identificacion;\nconfig_mensajes.push({ \"parametro\": \"men_identificacion\", \"valor\": read })\nflow.set(\"men_identificacion\", read);\nread = config.mensajes.men_salida;\nconfig_mensajes.push({ \"parametro\": \"men_salida\", \"valor\": read })\nflow.set(\"men_salida\", read);\nread = config.mensajes.id_titulo_matricula;\nconfig_mensajes.push({ \"parametro\": \"id_titulo_matricula\", \"valor\": read })\nflow.set(\"id_titulo_matricula\", read);\nread = config.mensajes.id_titulo_id;\nconfig_mensajes.push({ \"parametro\": \"id_titulo_id\", \"valor\": read })\nflow.set(\"id_titulo_id\", read);\nread = config.mensajes.id_leido;\nconfig_mensajes.push({ \"parametro\": \"id_leido\", \"valor\": read })\nflow.set(\"id_leido\", read);\nread = config.mensajes.id_no_leido;\nconfig_mensajes.push({ \"parametro\": \"id_no_leido\", \"valor\": read })\nflow.set(\"id_no_leido\", read);\n\n//===================================================CONEXION / CAMARA / SENSOR =====================================================\n//====================================================================================================================================\n\n\nread = config.conexion.modo_totem;\nconfig_conex.push({ \"parametro\": \"modo_totem\", \"valor\": read })\nflow.set(\"modo_totem\", read); // Modo de funcionamiento del totem\n\n\n//-----------------------------Gestion de IP fija / IP DHCP------------------------------------------------------\nvar ipdevice_flag = false // variables temporales para saber si hay cambios en las ip\nvar iprouter_flag = false\nread = config.conexion.ipdevice // direccion IP del dispositivo\nconfig_conex.push({ \"parametro\": \"ipdevice\", \"valor\": read })\nif (flow.get(\"ipdevice\") != read) {\n flow.set(\"ipdevice\", read);\n ipdevice_flag = true;\n}\nread = config.conexion.iprouter // direccion IP del router\nconfig_conex.push({ \"parametro\": \"iprouter\", \"valor\": read })\nif (flow.get(\"iprouter\") != read) {\n flow.set(\"iprouter\", read);\n iprouter_flag = true;\n}\nread = config.conexion.dhcp_flag // flag del DHCP\nconfig_conex.push({ \"parametro\": \"dhcp_flag\", \"valor\": read })\nif (flow.get(\"dhcp_flag\") != read) {\n flow.set(\"dhcp_flag\", read); // el dhcp_flag ha cambiado\n if (flow.get(\"dhcp_flag\")) {\n node.send([null, { payload: \". /home/trypton/scripts/enable_dhcp.sh\" }])\n }\n else {\n node.send([null, { payload: \". /home/trypton/scripts/enable_ipstatic.sh \" + flow.get(\"ipdevice\") + \"/24 \" + flow.get(\"iprouter\") }])\n }\n}\nelse { // el dhcp_flag no ha cambiado\n if (!flow.get(\"dhcp_flag\")) {\n if (ipdevice_flag || iprouter_flag) {\n node.send([null, { payload: \". /home/trypton/scripts/enable_ipstatic.sh \" + flow.get(\"ipdevice\") + \"/24 \" + flow.get(\"iprouter\") }])\n }\n }\n else {\n node.send([null, { payload: \". /home/trypton/scripts/enable_dhcp.sh\" }])\n }\n}\nflow.set(\"dhcpname_on\", \"IP Fija\") // texto que sale en el boton de la pantalla de mantenimiento\nflow.set(\"dhcpname_off\", \"DHCP\")\n//-----------------------------------Websocket IP--------------------------------------------------------------------------------------\nread = config.conexion.websocket_puerto // direccion IP del websocket del supervisor\nconfig_conex.push({ \"parametro\": \"websocket_puerto\", \"valor\": read })\nflow.set(\"websocket_puerto\", read);\nread = config.conexion.websocket_url\nconfig_conex.push({ \"parametro\": \"websocket_url\", \"valor\": read })\nif (flow.get(\"websocket_url\") != read) {\n flow.set(\"websocket_url\", read);\n node.send([{ payload: read + \" servidor.com\" }, null])\n}\n//----------------------------------- ENVIO DE MOVIMIENTOS --------------------------------------------------------------------------------------\nread = config.conexion.movimientosxmqtt;\nconfig_conex.push({ \"parametro\": \"movimientosxmqtt\", \"valor\": read })\nflow.set(\"movimientosxmqtt\", read); // Modo de envio de movimientos\n\nread = config.conexion.movimientosxapi;\nconfig_conex.push({ \"parametro\": \"movimientosxapi\", \"valor\": read })\nflow.set(\"movimientosxapi\", read); // Modo de envio de movimientos\n\n//----------------------------------- DEFINICION DEL BROKER DE MQTT --------------------------------------------------------------------------------------\n/*\nread = config.conexion.broker_url;\nconfig_conex.push({ \"parametro\": \"broker_url\", \"valor\": read })\nflow.set(\"broker_url\", read); // direccion del broker\n\nread = config.conexion.broker_port;\nconfig_conex.push({ \"parametro\": \"broker_port\", \"valor\": read })\nflow.set(\"broker_port\", read); // puerto del broker\n\nread = config.conexion.broker_username;\nconfig_conex.push({ \"parametro\": \"broker_username\", \"valor\": read })\nflow.set(\"broker_username\", read); // username del broker\n\nread = config.conexion.broker_pass;\nconfig_conex.push({ \"parametro\": \"broker_pass\", \"valor\": read })\nflow.set(\"broker_pass\", read); // password del broker\n*/\n/*\n//-------------------------------------------------- URL de la API de URBASER(Trypton) ------------------------------------------------------------------------------\nread = config.conexion.url_API;\nconfig_conex.push({ \"parametro\": \"url_API\", \"valor\": read })\nflow.set(\"url_API\", read); // Modo de funcionamiento del totem\n*/\nflow.set(\"semaforo\", \"green\")\n\n//------------------------------------Lector de QR / RF----------------------------------------------------------\nread = config.sensores.tipo_de_lector\nconfig_sensor.push({ \"parametro\": \"tipo_de_lector\", \"valor\": read })\nflow.set(\"tipo_de_lector\", read); //tipo de lector QR / RF\nread = config.sensores.rfid_invertido\nconfig_sensor.push({ \"parametro\": \"rfid_invertido\", \"valor\": read })\nflow.set(\"rfid_invertido\", read);\nflow.set(\"lector_activo\", true) // activamos el lector\nflow.set(\"ID_enviado\", false) // variable para controlar un solo envio de RFID / QR\n\n//-------------------------------------sensor de distancia-------------------------------------------------------\nread = config.sensores.sensor_distancia_local\nconfig_sensor.push({ \"parametro\": \"sensor_distancia_local\", \"valor\": read })\nflow.set(\"sensor_distancia_local\", read);\nread = config.sensores.sensor_distancia_remoto\nconfig_sensor.push({ \"parametro\": \"sensor_distancia_remoto\", \"valor\": read })\nflow.set(\"sensor_distancia_remoto\", read);\nflow.set(\"distance_flag_remote\", false)\nread = config.sensores.distance_min\nconfig_sensor.push({ \"parametro\": \"distance_min\", \"valor\": read })\nflow.set(\"distance_min\", read);\nread = config.sensores.min_cnt_in_ok\nconfig_sensor.push({ \"parametro\": \"min_cnt_in_ok\", \"valor\": read })\nflow.set(\"min_cnt_in_ok\", read);\nread = config.sensores.min_cnt_out_ok\nconfig_sensor.push({ \"parametro\": \"min_cnt_out_ok\", \"valor\": read })\nflow.set(\"min_cnt_out_ok\", read);\nflow.set(\"dist_in_counter\", 0);\nflow.set(\"dist_out_counter\", 0);\n\n//---------------------------------------Totems y Camara------------------------------------------------------------------------------\nread = config.conexion.totem_qty\nconfig_conex.push({ \"parametro\": \"totem_qty\", \"valor\": read })\nflow.set(\"totem_qty\", read);\nread = config.conexion.totem_id_remoto\nconfig_conex.push({ \"parametro\": \"totem_id_remoto\", \"valor\": read })\nflow.set(\"totem_id_remoto\", read);\nread = config.conexion.totem_id\nconfig_conex.push({ \"parametro\": \"totem_id\", \"valor\": read })\nflow.set(\"totem_id\", read);\nread = config.conexion.numero_vial;\nconfig_conex.push({ \"parametro\": \"numero_vial\", \"valor\": read })\nflow.set(\"numero_vial\", read);\nread = config.conexion.ip_totem_pareja\nconfig_conex.push({ \"parametro\": \"ip_totem_pareja\", \"valor\": read })\nflow.set(\"ip_totem_pareja\", read);\nread = config.camara.camara_matricula\nconfig_camara.push({ \"parametro\": \"camara_matricula\", \"valor\": read })\nflow.set(\"camara_matricula\", read);\nread = config.camara.camaraID\nconfig_camara.push({ \"parametro\": \"camaraID\", \"valor\": read })\nflow.set(\"camaraID\", read);\nread = config.camara.camara_IP\nconfig_camara.push({ \"parametro\": \"camara_IP\", \"valor\": read })\nflow.set(\"camara_IP\", read);\nread = config.camara.camara_contexto\nconfig_camara.push({ \"parametro\": \"camara_contexto\", \"valor\": read })\nflow.set(\"camara_contexto\", read);\nread = config.camara.camara_contexto_IP\nconfig_camara.push({ \"parametro\": \"camara_contexto_IP\", \"valor\": read })\nflow.set(\"camara_contexto_IP\", read);\nread = config.tiempos.camara_contexto_delay1\nconfig_tiempos.push({ \"parametro\": \"camara_contexto_delay1\", \"valor\": read })\nflow.set(\"camara_contexto_delay1\", read);\nread = config.tiempos.camara_contexto_delay2\nconfig_tiempos.push({ \"parametro\": \"camara_contexto_delay2\", \"valor\": read })\nflow.set(\"camara_contexto_delay2\", read);\n\n//---------------------------------------Lectura de Matricula-----------------------------------------------------------------------\nread = config.tiempos.time_max_2_bascula\nconfig_tiempos.push({ \"parametro\": \"time_max_2_bascula\", \"valor\": read })\nflow.set(\"time_max_2_bascula\", read);\nread = config.tiempos.wait_time_to_send_plate\nconfig_tiempos.push({ \"parametro\": \"wait_time_to_send_plate\", \"valor\": read })\nflow.set(\"wait_time_to_send_plate\", read);\nread = config.tiempos.max_wait_time_to_send_plate\nconfig_tiempos.push({ \"parametro\": \"max_wait_time_to_send_plate\", \"valor\": read })\nflow.set(\"max_wait_time_to_send_plate\", read);\nread = config.tiempos.max_time_bascula_sensor\nconfig_tiempos.push({ \"parametro\": \"max_time_bascula_sensor\", \"valor\": read })\nflow.set(\"max_time_bascula_sensor\", read);\nread = config.tiempos.time_to_snapshot\nconfig_tiempos.push({ \"parametro\": \"time_to_snapshot\", \"valor\": read })\nflow.set(\"time_to_snapshot\", read);\nflow.set(\"plate_sent\", false);\nflow.set(\"anpr0_flag\", false);\n\n//----------------------------------------Flags evento bascula---------------------------------------------------------------------------------------------\nflow.set(\"peso_trigger_OFF\", true);\nflow.set(\"peso_trigger_ON\", false);\n\n//-------------------------------------------------------------------------------------------------------------------------------------\n\nread = config.conexion.supervisor\nconfig_conex.push({ \"parametro\": \"supervisor\", \"valor\": read })\nflow.set(\"supervisor\", read);\n\n//---------------------------------------- Flags edicion de matricula ---------------------------------------------------------------------------------------------\nflow.set(\"badplate\", false) // inicializa flag de solicitu de modificación de matricula\nflow.set(\"prechar\", \"*\") // inicializa variable con caracter de prefijo para el $watch del nodo display de edicion de matricula\n\nflow.set(\"config_general\", config_general)\nflow.set(\"config_tiempos\", config_tiempos)\nflow.set(\"config_mensajes\", config_mensajes)\nflow.set(\"config_conex\", config_conex)\nflow.set(\"config_camara\", config_camara)\nflow.set(\"config_sensor\", config_sensor)\n\nflow.set(\"tiposDatosGlobal\", tiposOriginales) // Pasamos los datos de cada tipo a una variable de flow\n//notese que estos datos son leidos del archivo, sin hacerles transformacion a objeto.\n\n// Vamos a ver si tenemos que aplicar algun tipo de configuracion particular al tipo que se ha seleccionado en la configuracion (Tipo_Configuracion)\nconst tipo = parseInt(config.general.Tipo_Configuracion, 10); // asegura que el numero es integer\n\nif (tipo > 0 && tipo <= 9) {\n const tipoDatos = config.tipos[tipo] || [];\n\n tipoDatos.forEach(item => {\n let valor = item.valor;\n\n if (valor === \"true\") valor = true;\n if (valor === \"false\") valor = false;\n\n flow.set(item.parametro, valor); // sobre escribimos el valor configurado con la configuracion que este definida en el tipo.\n });\n}\n\n\n\n//Esto es exclusivo para el dispositivo de desarrollo para poder apagar el equipo dandole tres veces al titulo TARJETA ID\nflow.set(\"shut_flag\", false);\nflow.set(\"shut_count\",0)\nreturn;\n\n\n//*********************************************************** FUNCIONES **************************************************/\nfunction toObject(array) {\n const obj = {};\n array.forEach(el => {\n obj[el.parametro] = el.valor;\n });\n return obj;\n}\n", + "func": "const config = JSON.parse(msg.payload);\nmsg ={}; // clear msg object\nlet read;\nlet config_general = []\nlet config_tiempos = []\nlet config_mensajes = []\nlet config_sensor = []\nlet config_camara = []\nlet config_conex = []\nlet config_tipos = []\n\nconfig.general = toObject(config.general);\nconfig.camara = toObject(config.camara);\nconfig.conexion = toObject(config.conexion);\nconfig.sensores = toObject(config.sensores);\nconfig.mensajes = toObject(config.mensajes);\nconfig.tiempos = toObject(config.tiempos);\n\nconst tiposOriginales = config.tipos || {};\n\n\n// Aseguramos que cada tipo esté como array vacío si no tiene nada\nfor (let i = 1; i <= 6; i++) {\n if (!Array.isArray(tiposOriginales[i])) {\n tiposOriginales[i] = [];\n }\n}\nflow.set(\"tiposDatosGlobal\", tiposOriginales);\n\nconfig.tipos = tiposOriginales;\n\n\n\n//**************************** Parametros de Validacion OBLIGATORIOS para PESADA FINALIZADA ***************************************\n// Estos parametros pueden ser: QR / RFID / Boton Validacion / Detector Vehiculo \n//*********************************************************************************************************************************\n/*read = config.QR_obligatorio;\nconfig_general.push({ \"parametro\": \"QR_obligatorio\", \"valor\": read })\nflow.set(\"QR_obligatorio\", read);\nread = config.RFID_obligatorio;\nconfig_general.push({ \"parametro\": \"RFID_obligatorio\", \"valor\": read })\nflow.set(\"RFID_obligatorio\", read);\n\nread = config.general.funcion_totem;\nconfig_general.push({ \"parametro\": \"funcion_totem\", \"valor\": read })\nflow.set(\"funcion_totem\", read);\n\nread = config.general.identificacion_esp;\nconfig_general.push({ \"parametro\": \"identificacion_esp\", \"valor\": read })\nflow.set(\"identificacion_esp\", read);\n\nread = config.tiempos.timer1;\nconfig_tiempos.push({ \"parametro\": \"timer1\", \"valor\": read })\nflow.set(\"timer1\", read);\n\n*/\n\nread = config.general.Tipo_Configuracion;\nconfig_general.push({ \"parametro\": \"Tipo_Configuracion\", \"valor\": read })\nflow.set(\"Tipo_Configuracion\", read);\n\nread = config.general.ID_obligatorio;\nconfig_general.push({ \"parametro\": \"ID_obligatorio\", \"valor\": read })\nflow.set(\"ID_obligatorio\", read);\n\nread = config.general.salida_ID_obligatorio;\nconfig_general.push({ \"parametro\": \"salida_ID_obligatorio\", \"valor\": read })\nflow.set(\"salida_ID_obligatorio\", read);\n\nread = config.general.detector_obligatorio;\nconfig_general.push({ \"parametro\": \"detector_obligatorio\", \"valor\": read })\nflow.set(\"detector_obligatorio\", read);\n\nread = config.general.boton_obligatorio;\nconfig_general.push({ \"parametro\": \"boton_obligatorio\", \"valor\": read })\nflow.set(\"boton_obligatorio\", read);\nread = config.general.firma_obligatoria;\nconfig_general.push({ \"parametro\": \"firma_obligatoria\", \"valor\": read })\nflow.set(\"firma_obligatoria\", read);\nread = config.general.matricula_obligatoria;\nconfig_general.push({ \"parametro\": \"matricula_obligatoria\", \"valor\": read })\nflow.set(\"matricula_obligatoria\", read);\n\n\n\n//==================================================================================================================================\nread = config.general.edicion_matricula;\nconfig_general.push({ \"parametro\": \"edicion_matricula\", \"valor\": read })\nflow.set(\"edicion_matricula\", read); // Habilita la edicion de matricula\n\nread = config.general.botonsalida_activo;\nconfig_general.push({ \"parametro\": \"botonsalida_activo\", \"valor\": read })\nflow.set(\"botonsalida_activo\", read); // Permite poner boton de salida\n\nread = config.general.menu_activo;\nconfig_general.push({ \"parametro\": \"menu_activo\", \"valor\": read })\nflow.set(\"menu_activo\", read); // Permite poner los menus de contratacion\n\n/*\nread = config.general.firma_habilitada;\nconfig_general.push({ \"parametro\": \"firma_habilitada\", \"valor\": read })\nflow.set(\"firma_habilitada\", read); // Habilita la edicion de firma\n*/\n\nread = config.general.barrera_habilitada;\nconfig_general.push({ \"parametro\": \"barrera_habilitada\", \"valor\": read })\nflow.set(\"barrera_habilitada\", read); // Habilita el uso de barrera\n\nread = config.general.mostrar_ubicacion\nconfig_general.push({ \"parametro\": \"mostrar_ubicacion\", \"valor\": read })\nflow.set(\"mostrar_ubicacion\", read);\n\nread = config.general.switch_2_Totem_page;\nflow.set(\"switch_2_Totem_page\", read); // si igual a true cambia a pagina principal del totem siempre que se active la bascula\n// Con esto aseguramos que si queda abierta una ventana de configuracion se pueda quitar automaticamente\nconfig_general.push({ \"parametro\": \"switch_2_Totem_page\", \"valor\": read })\n\n//flow.set(\"peso\", read); // crea variable para recibir peso del supervisor\n\nread = parseInt(config.general.peso_minimo); // peso minimo para utilizado para indicar cuando hay vehiculo en la balanza o no\nconfig_general.push({ \"parametro\": \"peso_minimo\", \"valor\": read })\nflow.set(\"peso_minimo\", read);\n\nread = config.general.formatoPais; // peso minimo para utilizado para indicar cuando hay vehiculo en la balanza o no\nconfig_general.push({ \"parametro\": \"formatoPais\", \"valor\": read })\nflow.set(\"formatoPais\", read);\n\n//------------------- TOTEM MASTER tiempo de confirmacion -------------------------------------\nread = config.tiempos.confirm_master_time;\nconfig_tiempos.push({ \"parametro\": \"confirm_master_time\", \"valor\": read })\nflow.set(\"confirm_master_time\", read);\n//---------------------------------------------------------------------------------------------\n\n\nread = config.general.audio_level;\nconfig_general.push({ \"parametro\": \"audio_level\", \"valor\": read })\nflow.set(\"audio_level\", read);\n//-------------------------------------------------------\nread = config.general.mante_code_RFID; //codigo RFID para pasar a pantalla de mantenimiento\nconfig_general.push({ \"parametro\": \"mante_code_RFID\", \"valor\": read })\nflow.set(\"mante_code_RFID\", read)\nread = config.general.mante_code_QR; //codigo QR para pasar a pantalla de mantenimiento\nconfig_general.push({ \"parametro\": \"mante_code_QR\", \"valor\": read })\nflow.set(\"mante_code_QR\", read)\n\n//------------------------------- MENSAJES -------------------------\nread = config.tiempos.tiempo_mensaje;\nconfig_tiempos.push({ \"parametro\": \"tiempo_mensaje\", \"valor\": read })\nflow.set(\"tiempo_mensaje\", read);\n\nread = config.tiempos.relay2_pulse // duración del pulso generado en el relay 2 al terminar el pesaje para abrir barrera\nconfig_tiempos.push({ \"parametro\": \"relay2_pulse\", \"valor\": read })\nflow.set(\"relay2_pulse\", read);\n\nread = config.mensajes.mensaje2 // no se usa\nconfig_mensajes.push({ \"parametro\": \"mensaje2\", \"valor\": read })\nflow.set(\"mensaje2\", read);\nread = config.mensajes.mensaje1 // no se usa\nconfig_mensajes.push({ \"parametro\": \"mensaje1\", \"valor\": read })\nflow.set(\"mensaje1\", read);\nread = config.general.init_screen\nconfig_general.push({ \"parametro\": \"init_screen\", \"valor\": read })\nflow.set(\"init_screen\", read);\n\n//-------------------------------------------------------\nread = config.general.totem_serial // seriall asignado a este dispositivo\nconfig_general.push({ \"parametro\": \"totem_serial\", \"valor\": read })\nflow.set(\"totem_serial\", read);\n\n//---------------------------------------- Parametros por defecto del boton de salida para VALIDACION -----------------------------------------------------------\nread = config.mensajes.botonsalida_txt_def\nconfig_mensajes.push({ \"parametro\": \"botonsalida_txt_def\", \"valor\": read })\nflow.set(\"botonsalida_txt_def\", read)\nread = config.general.botonsalida_color_def\nconfig_general.push({ \"parametro\": \"botonsalida_color_def\", \"valor\": read })\nflow.set(\"botonsalida_color_def\", read)\nread = config.general.botonsalida_txt_color_def\nconfig_general.push({ \"parametro\": \"botonsalida_txt_color_def\", \"valor\": read })\nflow.set(\"botonsalida_txt_color_def\", read)\nread = config.mensajes.botonsalida_mensaje1\nconfig_mensajes.push({ \"parametro\": \"botonsalida_mensaje1\", \"valor\": read })\nflow.set(\"botonsalida_mensaje1\", read)\nread = config.mensajes.botonsalida_mensaje1_def\nconfig_mensajes.push({ \"parametro\": \"botonsalida_mensaje1_def\", \"valor\": read })\nflow.set(\"botonsalida_mensaje1_def\", read)\n\n//----------------------------------------- Textos Predefinidos -------------------------------------------------------------------------\nread = config.mensajes.men_pesaje_listo;\nconfig_mensajes.push({ \"parametro\": \"men_pesaje_listo\", \"valor\": read })\nflow.set(\"men_pesaje_listo\", read);\nread = config.mensajes.men_peso_estable;\nconfig_mensajes.push({ \"parametro\": \"men_peso_estable\", \"valor\": read })\nflow.set(\"men_peso_estable\", read);\nread = config.mensajes.men_identificacion;\nconfig_mensajes.push({ \"parametro\": \"men_identificacion\", \"valor\": read })\nflow.set(\"men_identificacion\", read);\nread = config.mensajes.men_salida;\nconfig_mensajes.push({ \"parametro\": \"men_salida\", \"valor\": read })\nflow.set(\"men_salida\", read);\nread = config.mensajes.id_titulo_matricula;\nconfig_mensajes.push({ \"parametro\": \"id_titulo_matricula\", \"valor\": read })\nflow.set(\"id_titulo_matricula\", read);\nread = config.mensajes.id_titulo_id;\nconfig_mensajes.push({ \"parametro\": \"id_titulo_id\", \"valor\": read })\nflow.set(\"id_titulo_id\", read);\nread = config.mensajes.id_leido;\nconfig_mensajes.push({ \"parametro\": \"id_leido\", \"valor\": read })\nflow.set(\"id_leido\", read);\nread = config.mensajes.id_no_leido;\nconfig_mensajes.push({ \"parametro\": \"id_no_leido\", \"valor\": read })\nflow.set(\"id_no_leido\", read);\n\n//===================================================CONEXION / CAMARA / SENSOR =====================================================\n//====================================================================================================================================\n\n\nread = config.conexion.modo_totem;\nconfig_conex.push({ \"parametro\": \"modo_totem\", \"valor\": read })\nflow.set(\"modo_totem\", read); // Modo de funcionamiento del totem\n\n\n//-----------------------------Gestion de IP fija / IP DHCP------------------------------------------------------\nvar ipdevice_flag = false // variables temporales para saber si hay cambios en las ip\nvar iprouter_flag = false\nread = config.conexion.ipdevice // direccion IP del dispositivo\nconfig_conex.push({ \"parametro\": \"ipdevice\", \"valor\": read })\nif (flow.get(\"ipdevice\") != read) {\n flow.set(\"ipdevice\", read);\n ipdevice_flag = true;\n}\nread = config.conexion.iprouter // direccion IP del router\nconfig_conex.push({ \"parametro\": \"iprouter\", \"valor\": read })\nif (flow.get(\"iprouter\") != read) {\n flow.set(\"iprouter\", read);\n iprouter_flag = true;\n}\nread = config.conexion.dhcp_flag // flag del DHCP\nconfig_conex.push({ \"parametro\": \"dhcp_flag\", \"valor\": read })\nif (flow.get(\"dhcp_flag\") != read) {\n flow.set(\"dhcp_flag\", read); // el dhcp_flag ha cambiado\n if (flow.get(\"dhcp_flag\")) {\n node.send([null, { payload: \". /home/trypton/scripts/enable_dhcp.sh\" }])\n }\n else {\n node.send([null, { payload: \". /home/trypton/scripts/enable_ipstatic.sh \" + flow.get(\"ipdevice\") + \"/24 \" + flow.get(\"iprouter\") }])\n }\n}\nelse { // el dhcp_flag no ha cambiado\n if (!flow.get(\"dhcp_flag\")) {\n if (ipdevice_flag || iprouter_flag) {\n node.send([null, { payload: \". /home/trypton/scripts/enable_ipstatic.sh \" + flow.get(\"ipdevice\") + \"/24 \" + flow.get(\"iprouter\") }])\n }\n }\n else {\n node.send([null, { payload: \". /home/trypton/scripts/enable_dhcp.sh\" }])\n }\n}\nflow.set(\"dhcpname_on\", \"IP Fija\") // texto que sale en el boton de la pantalla de mantenimiento\nflow.set(\"dhcpname_off\", \"DHCP\")\n//-----------------------------------Websocket IP--------------------------------------------------------------------------------------\nread = config.conexion.websocket_puerto // direccion IP del websocket del supervisor\nconfig_conex.push({ \"parametro\": \"websocket_puerto\", \"valor\": read })\nflow.set(\"websocket_puerto\", read);\nread = config.conexion.websocket_url\nconfig_conex.push({ \"parametro\": \"websocket_url\", \"valor\": read })\nif (flow.get(\"websocket_url\") != read) {\n flow.set(\"websocket_url\", read);\n node.send([{ payload: read + \" servidor.com\" }, null])\n}\n//----------------------------------- ENVIO DE MOVIMIENTOS --------------------------------------------------------------------------------------\nread = config.conexion.movimientosxmqtt;\nconfig_conex.push({ \"parametro\": \"movimientosxmqtt\", \"valor\": read })\nflow.set(\"movimientosxmqtt\", read); // Modo de envio de movimientos\n\nread = config.conexion.movimientosxapi;\nconfig_conex.push({ \"parametro\": \"movimientosxapi\", \"valor\": read })\nflow.set(\"movimientosxapi\", read); // Modo de envio de movimientos\n\n//----------------------------------- DEFINICION DEL BROKER DE MQTT --------------------------------------------------------------------------------------\n/*\nread = config.conexion.broker_url;\nconfig_conex.push({ \"parametro\": \"broker_url\", \"valor\": read })\nflow.set(\"broker_url\", read); // direccion del broker\n\nread = config.conexion.broker_port;\nconfig_conex.push({ \"parametro\": \"broker_port\", \"valor\": read })\nflow.set(\"broker_port\", read); // puerto del broker\n\nread = config.conexion.broker_username;\nconfig_conex.push({ \"parametro\": \"broker_username\", \"valor\": read })\nflow.set(\"broker_username\", read); // username del broker\n\nread = config.conexion.broker_pass;\nconfig_conex.push({ \"parametro\": \"broker_pass\", \"valor\": read })\nflow.set(\"broker_pass\", read); // password del broker\n*/\n/*\n//-------------------------------------------------- URL de la API de URBASER(Trypton) ------------------------------------------------------------------------------\nread = config.conexion.url_API;\nconfig_conex.push({ \"parametro\": \"url_API\", \"valor\": read })\nflow.set(\"url_API\", read); // Modo de funcionamiento del totem\n*/\nflow.set(\"semaforo\", \"green\")\n\n//------------------------------------Lector de QR / RF----------------------------------------------------------\nread = config.sensores.tipo_de_lector\nconfig_sensor.push({ \"parametro\": \"tipo_de_lector\", \"valor\": read })\nflow.set(\"tipo_de_lector\", read); //tipo de lector QR / RF\nread = config.sensores.rfid_invertido\nconfig_sensor.push({ \"parametro\": \"rfid_invertido\", \"valor\": read })\nflow.set(\"rfid_invertido\", read);\nflow.set(\"lector_activo\", true) // activamos el lector\nflow.set(\"ID_enviado\", false) // variable para controlar un solo envio de RFID / QR\n\n//-------------------------------------sensor de distancia-------------------------------------------------------\nread = config.sensores.sensor_distancia_local\nconfig_sensor.push({ \"parametro\": \"sensor_distancia_local\", \"valor\": read })\nflow.set(\"sensor_distancia_local\", read);\nread = config.sensores.sensor_distancia_remoto\nconfig_sensor.push({ \"parametro\": \"sensor_distancia_remoto\", \"valor\": read })\nflow.set(\"sensor_distancia_remoto\", read);\nflow.set(\"distance_flag_remote\", false)\nread = config.sensores.distance_min\nconfig_sensor.push({ \"parametro\": \"distance_min\", \"valor\": read })\nflow.set(\"distance_min\", read);\nread = config.sensores.min_cnt_in_ok\nconfig_sensor.push({ \"parametro\": \"min_cnt_in_ok\", \"valor\": read })\nflow.set(\"min_cnt_in_ok\", read);\nread = config.sensores.min_cnt_out_ok\nconfig_sensor.push({ \"parametro\": \"min_cnt_out_ok\", \"valor\": read })\nflow.set(\"min_cnt_out_ok\", read);\nflow.set(\"dist_in_counter\", 0);\nflow.set(\"dist_out_counter\", 0);\n\n//---------------------------------------Totems y Camara------------------------------------------------------------------------------\nread = config.conexion.totem_qty\nconfig_conex.push({ \"parametro\": \"totem_qty\", \"valor\": read })\nflow.set(\"totem_qty\", read);\nread = config.conexion.tipobascula\nconfig_conex.push({ \"parametro\": \"tipobascula\", \"valor\": read })\nflow.set(\"tipobascula\", read);\nread = config.conexion.totem_id_remoto\nconfig_conex.push({ \"parametro\": \"totem_id_remoto\", \"valor\": read })\nflow.set(\"totem_id_remoto\", read);\nread = config.conexion.totem_id\nconfig_conex.push({ \"parametro\": \"totem_id\", \"valor\": read })\nflow.set(\"totem_id\", read);\nread = config.conexion.numero_vial;\nconfig_conex.push({ \"parametro\": \"numero_vial\", \"valor\": read })\nflow.set(\"numero_vial\", read);\nread = config.conexion.ip_totem_pareja\nconfig_conex.push({ \"parametro\": \"ip_totem_pareja\", \"valor\": read })\nflow.set(\"ip_totem_pareja\", read);\nread = config.conexion.envia_mat_img\nconfig_conex.push({ \"parametro\": \"envia_mat_img\", \"valor\": read })\nflow.set(\"envia_mat_img\", read);\n\nread = config.camara.camara_matricula\nconfig_camara.push({ \"parametro\": \"camara_matricula\", \"valor\": read })\nflow.set(\"camara_matricula\", read);\nread = config.camara.camaraID\nconfig_camara.push({ \"parametro\": \"camaraID\", \"valor\": read })\nflow.set(\"camaraID\", read);\nread = config.camara.camara_IP\nconfig_camara.push({ \"parametro\": \"camara_IP\", \"valor\": read })\nflow.set(\"camara_IP\", read);\nread = config.camara.camara_contexto\nconfig_camara.push({ \"parametro\": \"camara_contexto\", \"valor\": read })\nflow.set(\"camara_contexto\", read);\nread = config.camara.camara_contexto_IP\nconfig_camara.push({ \"parametro\": \"camara_contexto_IP\", \"valor\": read })\nflow.set(\"camara_contexto_IP\", read);\nread = config.tiempos.camara_contexto_delay1\nconfig_tiempos.push({ \"parametro\": \"camara_contexto_delay1\", \"valor\": read })\nflow.set(\"camara_contexto_delay1\", read);\nread = config.tiempos.camara_contexto_delay2\nconfig_tiempos.push({ \"parametro\": \"camara_contexto_delay2\", \"valor\": read })\nflow.set(\"camara_contexto_delay2\", read);\n\n//---------------------------------------Lectura de Matricula-----------------------------------------------------------------------\nread = config.tiempos.time_max_2_bascula\nconfig_tiempos.push({ \"parametro\": \"time_max_2_bascula\", \"valor\": read })\nflow.set(\"time_max_2_bascula\", read);\nread = config.tiempos.wait_time_to_send_plate\nconfig_tiempos.push({ \"parametro\": \"wait_time_to_send_plate\", \"valor\": read })\nflow.set(\"wait_time_to_send_plate\", read);\nread = config.tiempos.max_wait_time_to_send_plate\nconfig_tiempos.push({ \"parametro\": \"max_wait_time_to_send_plate\", \"valor\": read })\nflow.set(\"max_wait_time_to_send_plate\", read);\nread = config.tiempos.max_time_bascula_sensor\nconfig_tiempos.push({ \"parametro\": \"max_time_bascula_sensor\", \"valor\": read })\nflow.set(\"max_time_bascula_sensor\", read);\nread = config.tiempos.time_to_snapshot\nconfig_tiempos.push({ \"parametro\": \"time_to_snapshot\", \"valor\": read })\nflow.set(\"time_to_snapshot\", read);\nflow.set(\"plate_sent\", false);\nflow.set(\"anpr0_flag\", false);\n\n//----------------------------------------Flags evento bascula---------------------------------------------------------------------------------------------\nflow.set(\"peso_trigger_OFF\", true);\nflow.set(\"peso_trigger_ON\", false);\n\n//-------------------------------------------------------------------------------------------------------------------------------------\n\nread = config.conexion.supervisor\nconfig_conex.push({ \"parametro\": \"supervisor\", \"valor\": read })\nflow.set(\"supervisor\", read);\n\n//---------------------------------------- Flags edicion de matricula ---------------------------------------------------------------------------------------------\nflow.set(\"badplate\", false) // inicializa flag de solicitu de modificación de matricula\nflow.set(\"prechar\", \"*\") // inicializa variable con caracter de prefijo para el $watch del nodo display de edicion de matricula\n\nflow.set(\"config_general\", config_general)\nflow.set(\"config_tiempos\", config_tiempos)\nflow.set(\"config_mensajes\", config_mensajes)\nflow.set(\"config_conex\", config_conex)\nflow.set(\"config_camara\", config_camara)\nflow.set(\"config_sensor\", config_sensor)\n\nflow.set(\"tiposDatosGlobal\", tiposOriginales) // Pasamos los datos de cada tipo a una variable de flow\n//notese que estos datos son leidos del archivo, sin hacerles transformacion a objeto.\n\n// Vamos a ver si tenemos que aplicar algun tipo de configuracion particular al tipo que se ha seleccionado en la configuracion (Tipo_Configuracion)\nconst tipo = parseInt(config.general.Tipo_Configuracion, 10); // asegura que el numero es integer\n\nif (tipo > 0 && tipo <= 9) {\n const tipoDatos = config.tipos[tipo] || [];\n\n tipoDatos.forEach(item => {\n let valor = item.valor;\n\n if (valor === \"true\") valor = true;\n if (valor === \"false\") valor = false;\n\n flow.set(item.parametro, valor); // sobre escribimos el valor configurado con la configuracion que este definida en el tipo.\n });\n}\n\n\n\n//Esto es exclusivo para el dispositivo de desarrollo para poder apagar el equipo dandole tres veces al titulo TARJETA ID\nflow.set(\"shut_flag\", false);\nflow.set(\"shut_count\",0)\nreturn;\n\n\n//*********************************************************** FUNCIONES **************************************************/\nfunction toObject(array) {\n const obj = {};\n array.forEach(el => {\n obj[el.parametro] = el.valor;\n });\n return obj;\n}\n", "outputs": 2, "timeout": 0, "noerr": 0, @@ -5825,7 +5829,7 @@ "type": "debug", "z": "953e8740401d175c", "name": "debug 29", - "active": false, + "active": true, "tosidebar": true, "console": false, "tostatus": false, @@ -5847,7 +5851,7 @@ "links": [ "8356acc61776cae7" ], - "x": 1265, + "x": 1185, "y": 1380, "wires": [] }, @@ -5857,7 +5861,7 @@ "z": "953e8740401d175c", "g": "0e2b148022a7af13", "name": "debug 30", - "active": false, + "active": true, "tosidebar": true, "console": false, "tostatus": false, @@ -5879,7 +5883,7 @@ "links": [ "6f9cc9fc71e8ca71" ], - "x": 815, + "x": 735, "y": 1380, "wires": [] }, @@ -5897,16 +5901,16 @@ "targetType": "full", "statusVal": "", "statusType": "auto", - "x": 2260, - "y": 1320, + "x": 2200, + "y": 1380, "wires": [] }, { "id": "53335688d117ec96", "type": "comment", "z": "953e8740401d175c", - "name": "Version 1.43 de bascula autonoma", - "info": "**Version 1.43** 11/05/2026\nHemos introducido un cambio significativo en la gestion del ID. Hasta ahora ele flag definitivo de ID_ok = !ID_obligatorio || ID_verificado Lo que quiere decir \nque el ID esta ok cuando el ID_obligatorio esta en false es decir no es obligatorio o cuando si es obligatorio y el ID_verificado esta en true, es decir \nesta autorizado para entrar. Pero si el ID_obligatorio esta en false y se presenta un ID cualquiera vamos a obtener ID_ok = true es decir esta autorizado.\nHemos introducido un nuevo estado que es ID_presentado y ahora la condicion es ID_ok = (!ID_obligatorio && !ID_presentado) || ID_verificado es decir que \nID_ok depende de ID_verificado si ID_obligatorio esta en true o si ID_presentado esta en true. En caso de que ID_obligatorio este en false y ID_presentado este\ntambien en false entonce ID_ok no interfiere en la autorizacion es decir esta en true, todo depende del menu de seleccion si existe.\nPara esto hemos cambiado en el nodo de \"Analisis del modo de Operacion\" linea 59 \"let ID_ok = !ID_obligatorio || ID_verificado\" por la nueva\nlet ID_ok = (!ID_obligatorio && !ID_presentado) || ID_verificado;\nAdicionalmernte en el nodo QR / RFID hemos activado el ID_presentado cada vez que leemos un ID. Este parametro se resetea en el nodo de peso cada vez que hay una nueva pesada\n\nEn el nodo de matricula (linea 27) hacemos el cambio del flag ID_obligatorio al flag salida_ID_obligatorio cuando el movimiento es final. De esta forma ID_obligatorio aplica a la entrada \nexclusivamente y salida_ID_obligatorio aplica a la salida solamente.\nEn el nodo QR/RFID hemos agregado la puesta a cero del flag de QR(linea 67) y del RFID(linea 126) cuando se trata de un ID incorrecto para que el usuario pueda volver a colocar otro ID.\n\n\n**Version 1.42** 29/04/2026\nEn el nodo \"Analisis\" en la linea 116 hemos puesto un timer que espera unos segundos antes de sacar la notificacion de ubicacion. Esto se hace para esperar a que la notificacion anterior en caso de existir\npermanezca un tiempo y en todo caso para que esta sea la ultima notificacion que quede en pantalla hasta que el vehiculo abandone la bascula. \nEliminamos de la pantalla el mensaje Acerque QR o Tarjeta en caso de recibir una lista de menu. Esto lo hacemos en el nodo \"check json\".\nEn el nodo \"Envio de Mensajes\" (linea 65) eliminamos las notificaciones que haya al final del movimiento (final de pesada) salvo si la notificacion es de Ubicacion.\nSe hicieron cambios en index.html y en index.js del uibuilder /totem/ para incluir en el menu de Tipo de Configuracion un boton (LISTA DE PARAMETROS) que nos permita visualizar todos los parametros que podemos incluir en cualquiera de los tipos de configuracion\nSe introdujo un chequeo en el nodo \"QR/RFID\" en la linea 37 para eliminar el boton de salida (simular que fue pulsado) cuando se recibe un QR valido durante el movimiento final. Lo mismo hacemos mas abajo en la recepcion de RFID.\nEn la parte RFID dado que luego de recibir la tarjeta validada en BD todavia necesitamos validar si la matricula asociada a la tarjeta tiene vehiculo en transito, esto hace que el estado movimiento = inicial puede permanecer por mas tiempo (mientras se chequea si hay transito) por lo tanto en el chequeo \ntenemos que meter la variable \"matricula_revisada\" como requisito para avanzar ya que es quien nos indica si el estado del movimiento ya es definitivo. Recordemos que este chequeo solo lo queremos hacer durante el movimiento final, es decir cuando el vehiculo esta cerrando toda la pesada.\n\n\n**Version 1.41** 1/04/2026\n- Dado que puede darse el caso que la camara de contexto no responda en un determinado momento a la solicitud de imagen, se han introducido unas condiciones para tratar de obtener\nla imagen de contexto en caso de fallo temporal. El metodo resumido es el siguiente: Cuando estamos seguros que el vehiculo esta correctamente posicionado\nen la bascula (peso estable) hacemos una solicitud y marcamos un estado (\"waiting1\"), esperamos por la imagen un tiempo definido en la variable (\"camara_contexto_delay1\") \nencaso de no llegar hacemos una segunda solicitud y marcamos el estado (\"waiting2\") y esperamos por la imagen un tiempo definido en la variable (\"camara_contexto_delay2\").\nMientras tanto el semaforo esta en rojo esperando la imagen de contexto, suponiendo que todo lo demas esta ok. Al cabo del ultimo tiempo, marcamos el nuevo estado\nen funcion de lo sucedido, \"ok\" si recibimos la imagen \"fail\" si no la hemos recibido. En cualquiera de los dos casos el semaforo pasa a verde y se continua con la pesada\nEn caso de no tener imagen de contexto, el nombre de la imagen ira vacio (\"\") al igual que la imagen.\n\n**Version 1.4** 25/03/2026\n- Debido a nuevo requerimiento se introduce la opcion (configurable) de tener una camara de contexto en la bascula. Esta camara en caso de estar\nhabilitada 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\n\"estable\" que es donde se hace la deteccion del camion completamente en la bascula. Posteriormente en el nodo \"Analisis del modo de Operacion\" es\ndonde verificamos que la imagen fue tomada y la colocamos en el registro que se esta haciendo para generar el movimiento correspondiente. El Grupo \n\"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\nde configuracion al igual que su habilitacion en el sistema.\n- 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\nse ha incluido una funcion en el menu de configuracion -> Pruebas del sistema -> Pedir Snapshot de contexto de forma que el usuario pueda visualizar \nla imagen actual que la camara esta enviando y pueda de esa forma confirmar el correcto funcionamiento de la conexión y de los equipos.\n\n**Version 1.3.2** \n- Eliminado la inicializacion en el nodo peso del flag distance_flag_SET del detector de vehiculos. Esto ocasionaba problemas en si \ncoincidia que estaba contando los pulsos de salida y antes de finalizar las bascula se ponia en peso < pmin. En esa situacion el detector\nse quedaba bloqueado in poder indicar detector OFF.\n\n- Hemos colocado un codigo de dos lineas para eliminar el boton del menu activo \"COMPLETAR INFO\" en caso que se presente una tarjeta o un codigo QR. \nEsto lo hicimos en el nodo \"Analisis del modo de Operacion\" en la linea 70 donde chequeamos si estamos en movimiento inicial.\n\n- Corregido un \"error\" que no permitia cerrar el movimiento inicial cuando se pasaba una tarjeta si teniamos el menu activo y la firma obligatoria. \nSe agrego una condicion (\"ID_verificado\") en la condicion global para llamar a la firma. Esto es en el nodo \"Analisis de Operacion\" en las primeras lineas\nif (flow.get(\"firma_obligatoria\") && !flow.get(\"firma_flag\") && !flow.get(\"firma_ok_flag\") && !flow.get(\"supervisor\") && (!flow.get(\"menu_activo\") || (flow.get(\"menu_activo\") && (flow.get(\"menu_seleccionado_flag\") || ID_verificado)))) \nCon esto permite continuar con la firma en caso que tengamos menu_activo y firma obligatoria y entremos con tarjeta o con codigo QR.\n\n**Version 1.3.1**\n- Hemos colocado una opcion mas en el menu de configuración en \"Pruebas del Sistema\" -> \"Probar Sensor\". Esto permite visualizar la salida del valor\nde la distancia que esta midiendo el sensor cada segundo. Para que el medidor no este continuamente inundando el configurador con el dato de distancia\nhemos colocado un switch el cual solo deja pasar ese dato cuando se activa el menu, lo inhabilita cuando se sale del menu.\n\n- Hemos puesto el flag flow.set(\"RFID_ok_flag\", false) cuando leemos un QR y flow.set(\"QR_ok_flag\", false) cuando leemos una tarjeta, esto permite\nque se utilice un QR aunque primero hayamos metido una tarjeta invalida y viceversa. Se hizo en el nodo \"envio QR/RFID\".\n\n**Version 1.3** \n- En esta version hemos cambiado los timers a modo contexto local (del nodo) Importante este punto ya que el timer es un objeto no serializable\ny por lo tanto no deberiamos guardar en contexto flow o global ya que esos contextos estan previstos para objetos serializables (estados, valores, arrays, json...)\nSi lo ponemos en flow el timer puede romperse, quedar corrupto, o incoherente al hacer deploy o cualquier operacion de inicio.\nlo limpio es hacerlo en el contexto del nodo y poner el cierre del timer al salir del nodo en node.on(\"close\"). En su lugar ponemos el codigo\nen la pestaña del editor del nodo \"Al final\" que es lo mismo. La pestaña Close se refiere a cuando el nodered se reinicia, se hace redeploy etc..\n\n- Hemos cambiado los timers del snapshot en Analisis detector y Analisis de peso, igualmente hemos cambiado la forma de apagarlo desde otro nodo.\nPara ello hemos implementado en el nodo donde reside el timer un codigo que se activa cuando topic = cancel_snapshot_timer.\n\n- Se arreglo la repeticion del mensaje (audio) de firme en la pantalla. Para ello hemos inluido el flag de solicitud de firma en el \"if\" de envio del mensaje\n de manera que si ya se hizo solicitud de la firma no se aplica el mensaje nuevamente.\n\n- Hemos puesto un nuevo nodo (esperar) en el grupo deteccion de matricula para evitar un posible aunque remoto caso cuando se activa el detector remoto pero no se activa la bascula\ndurante unos segundos. La descripcion esta en un nodo comentario \"CASO QUE PODRIA BLOQUEAR EL SISTEMA\" que se encuentra en el grupo \"ITSAPI - WEB SERVER Y ANALISIS\".\n\n**Version 1.2** Hemos agregado una nueva funcionalidad para indicarle al conductor acerca de la ubicacion interna donde debe dirigirse con el residuo que\ntransporta. Esta notificacion se muestra una vez que hayan cumplido todos los requisitos de entrada y antes de poner el semaforo en verde. \nPara esta funcionalidad hemos introducido un codigo en el nodo \"Analisis del modo de Operacion\" (lineas 76 a 112) y en el nodo \"check json content\"\nen las lineas 44 a 50 bajo el titulo \"RESPUESTA DE LA REVISION DE LA UBICACION INTERNA EN BD\". Si recibimos respuesta positiva de la BD es decir que si \ntiene ubicacion interna el residuo presentado entonces sacamos notificacion con lo que dice el campo de descripcion encontrado en la tabla, en caso contrario\nel mensaje que aparece en pantalla es diciendo que no hay ubicacion para ese residuo.\nSe ha hecho una modificacion en el nodo \"detector rx\" para inicializar todas las variables del Totem cuando ocurra una falsa deteccion de vehiculo, es decir \ncuando que se recibe la señal de detector remoto ON pero no se activa la bascula en el tiempo previsto. Dado que en ese momento hay variables que han cambiado de estado \ny ademas se ha recibido una imagen, es necesario forzar la inicializacion de todas las variables ya que el peso no se ha movido de cero. Si dejamos las variables\nsin inicializar, cuando un vehiculo entre es probable que haya estados confusos.\n\n**Version 1.12** Se corrigio el nodo peso estable para procesar correctamente el valor de Peso Maximo Admitido y sacar un mensaje que no se pueda borrar\nhasta que el vehiculo abandone la bascula. Se agrego un nodo para mantener el aviso de salir de la bascula en caso de PMA, si el usuario pulsa OK.\nCorregido en el nodo \"check JSON content\" la recepcion de la matricula asociada a una tarjeta. Hemos activado los flags correspondientes para que el nodo \n\"matricula 2\" pueda hacer el chequeo de la matricula respecto a si esta o no en transito. \nEn el nodo \"Analisis del modo de operacion\" hemos puesto una condicion para no mostrar el boton de salida cuando el usuario utiliza una tarjerta valida.\nEn el nodo \"Analisis del modo de operacion\" hemos colocado una condicion para sacar la pantalla de firma. Esta condicion es que se trate de que estamos en \nel proceso de movimiento inicial. Adicionalmente si estamos en el proceso de movimiento final y la firma es obligatoria, marcamos el flag de firma como si estuviera\nrealizada ya que para la salida no es preciso firmar.\nEn el archivo generado de la firma se elimino el encabezado \"data:image/png Base64,\" Esto lo hacemos en el nodo firma en la linea 252 del script.\ndataUrl = canvas.toDataURL().split(\",\")[1];\n\n**Version 1.11**Se puso una condicion (peso en bascula > pmin) en el nodo \"enviar matricula\" para enviar matricula al nodo de analisis. Esto evita que se este\nenviando matricula si se recibe sin que haya vehiculo en bascula. Esto no evita que se reciban matriculas automaticas antes de entrar el vehiculo en la bascula.\nEstas ultimas fotos se conservan en memoria hasta que el vehiculo entre en la bascula y pueden ser utilizadas como foto principal en caso de fallo en la solicitud.\nHemos eliminado en la linea 156 del nodo matricula2 la condicion de \"qr_revisado\" para eximirlo de disponer de matricula debido a que ahora el codigo qr puede estar asociado a varias matriculas.\ny por lo tanto sera necesario disponer de una matricula a pesar de tener el codigo de autorizacio (solo indica residuo).\nHemos inicializado la matricula [flow.set(\"matricula\", \"Sin Matricula\")] tambien en el momento en que el peso supera a pmin por primera vez (es decir entrando en la bascula).\nSe agrego en la salida de la edicion de matricula [flow.set(\"matriculacheck_solicitada\", false)] para permitir que se busque nuevamente la matricula en BD.\n\n**Version 1.1** Hemos agregado excepcion de obligacion de tener que disponer de matricula para el caso de presentar una tarjeta RFID. Anteriormente se habia puesto en el nodo de \"Analisis\ndel modo de operacion\" (*let matricula_obligatoria = flow.get(\"matricula_obligatoria\") || true // siempre es obligatoria*) una linea para obligar a tener siempre matricula,\nindependiente de lo que este configurado en el archivo de configuracion. Ahora esa linea sigue funcionando pero en el caso de la tarjeta se permite que no haya matricula ya que esta identificacion \ncontiene la matricula unica. Ver linea 53 en el nodo mencionado anteriormente. No es el caso de los QR los cuales puede haber el mismo GUUID asociados a diferentes matriculas.\n\nSe han agregado nos nuevos campos a la tabla movimierntos_cerrados en el supervisor estos campos son \"qr_salida\" y \"rfid_salida\". La presencia de estos \ndos campos es para disponer de la identificaciob utilizada en salida en caso de hacerlo. Para ello en el nodo \"Analisis del modo de Operacion\" se evalua \nsi estamos en el movimiento inicial o final para ver cual campo es el que tenemos que rellenar. Est se hace el la linea nº 100\nSe ha moviso la inicializacion flow.set(\"distance_flag_SET\", false) al momento donde la bascula pasa de peso < pmin por primera vez. \nAntes se inicializaba cada vez que el peso que llegaba era menor que pmin. Esto podia ocasionar un error ya que el nodo detector perdia el control\nde este flag al ser inicializado cada vez que el peso < pmin.\n\nEl nodo \"matricula 2\" sustituye al nodo matricula anterior. Este nuevo contiene todas las modificaciones necesarias para adaptarse a los \nnuevos requerimientos de la bascula automatica\n\nPara que el QR pueda entrar en la planta es necesario que la matricula este disponible.\nEn caso de que la matricula leida sea diferente a la matricula obtenida en la BD luego de enviar el QR se toma la matricula leida de la BD\n\n**version 1.0.** Este codigo de Bascula Autonoma sale a partir del Totem Autonomo. Todo el codigo relativo a la gestion de BAse de Datos se traslada al RPI Supervisor\nEn este RPi solo queda lo relativo a la gestion de sensores, camaras, lectores de Identioficacion, semaforo, barrera y la pantalla para que el usuario \npueda interactuar con el sistema como por ejemplo hacer seleccion del residuo a traves de un menu de seleccion.\n\nEn esta version se ha introducido el termino PMA(Peso Maximo Autorizado. Este es un valor que el Totem recibe cuando el Supervisor le envia la autorizacion\nde la matricula junto con la tara- El Totem revisa el peso estable del vehiculo con el PMA recibidoy en caso de ser superior se envia un aviso en la pantalla\nindicandole al conductor que debe retroceder ya que supera el peso admitido.\n\nOtra función que contiene esta version es el chequeo de la Tara. Si el valor recibido de la Tara es superior a cero entonces el movimiento inicial recibido por el supervisor \nes enviado como movimiento cerrado al Servidor.Realmente para el Totem es transparente. La gestion de la Tara en efecto la hace el Supervisor una vez que recibe\nel movimiento inicial que envia el Totem.", + "name": "Version 1.45 de bascula autonoma", + "info": "**Version 1.45** 17/05/2026\nSe ha agregado codigo en el nodo \"Envia Matricula\" y en el nodo \"Convert to B64\" para en caso en que el flag \"envia_mat_img\" este en true enviar la matricula,\nla imagen de la matricula y la imagen de contexto al supervisor en el momento que se obtengan con la finalidad de que el supervisor a su vez la envie en\nbroadcasting para que los interesados puedan obtener en tiempo real esta informaciones y plasmarlas sobre una pantalla. La idea es que se envia al servidor \npara que disponga de esa informacion en alguna pagina web\nLa trama que se envia al supervisor en el caso de la matricula es:\n{payload: { device: \"supervisor\", accion: \"lastplate\", data: {numero_vial: 2, totem_id: \"totem_2\", matricula: \"1234ABC\", imagen: imgBase64}}}\nen el caso de la imagen de contexto es:\n{payload: { device: \"supervisor\", accion: \"lastcontext\", data: {numero_vial: 2, totem_id: \"totem_2\", imagen_contexto: imgBase64}}}\nAmbas tramas se envian al momento de recibir las imagenes en el Totem.\nPor su lado el Supervisor envia las siguientes tramas para matricula y para contexto:\n{payload:{sender:\"supervisor\", device:\"servidor\", accion:\"lastplate\", data:{totem_id:\"totem_2\", numero_vial:2, matricula:\"1234ABC\", imagen:imgBase64}}}\n{payload:{sender:\"supervisor\", device:\"servidor\", accion:\"lastcontext\", data:{totem_id:\"totem_2\", numero_vial:2, imagen_contexto:imgBase64}}}\nEl flag \"envia_mat_img\" es configurable a traves del menu de configuracion en el apartado Comunicacion.\n\nSe agrego un nodo \"estado semaforo\" antes de accionar el semaforo fisicamente, la idea de este nodo es que envie el estado del semaforo via websocket cada\nvez que este cambia de estado, de esta forma podemos mantener un estado tiempo real en cualquier sitio remoto con solo observar esta trama. El envio se hace\nal supervisor que es quien sirve el websocket. La trama es la siguiente: \n{device:\"supervisor\", accion: \"semaforo_status\", status:{totem_id:flow.get(\"totem_id\"), numero_vial:flow.get(\"numero_vial\"), semaforo:semaforo}}\nCuando llegue al supervisor este enviara una trama al servidor asi (esta trama va en broadcasting osea que fisicamente llega a todos los clientes de websocket):\n{payload:{sender:\"supervisor\", device:\"servidor\", accion:\"update_semaforo\", semaforo:{totem_id:totem, numero_vial:vial, semaforo:semaforo}}}\n\n**Version 1.44** 14/05/2026\nPara asegurar que las basculas de un solo totem se usan para su proposito (entrada / salida) se ha introducido un nuevo parametro configurable para \ndefinir el proposito de la bascula. Esto evitara que una matricula que no tiene transito salga por la bascula, igualmente esta la opcion \nde si un vehiculo ya tiene transito no pueda utilizar la bascula de entrada. En caso que queramos anular esta funcionalidad solo hay que \nconfigurar el parametro con el valor \"ambos\". Todo esto es valido solamente cuando tenemos un solo totem en la bascula.\n\n**Version 1.43** 11/05/2026\nHemos introducido un cambio significativo en la gestion del ID. Hasta ahora ele flag definitivo de ID_ok = !ID_obligatorio || ID_verificado Lo que quiere decir \nque el ID esta ok cuando el ID_obligatorio esta en false es decir no es obligatorio o cuando si es obligatorio y el ID_verificado esta en true, es decir \nesta autorizado para entrar. Pero si el ID_obligatorio esta en false y se presenta un ID cualquiera vamos a obtener ID_ok = true es decir esta autorizado.\nHemos introducido un nuevo estado que es ID_presentado y ahora la condicion es ID_ok = (!ID_obligatorio && !ID_presentado) || ID_verificado es decir que \nID_ok depende de ID_verificado si ID_obligatorio esta en true o si ID_presentado esta en true. En caso de que ID_obligatorio este en false y ID_presentado este\ntambien en false entonce ID_ok no interfiere en la autorizacion es decir esta en true, todo depende del menu de seleccion si existe.\nPara esto hemos cambiado en el nodo de \"Analisis del modo de Operacion\" linea 59 \"let ID_ok = !ID_obligatorio || ID_verificado\" por la nueva\nlet ID_ok = (!ID_obligatorio && !ID_presentado) || ID_verificado;\nAdicionalmernte en el nodo QR / RFID hemos activado el ID_presentado cada vez que leemos un ID. Este parametro se resetea en el nodo de peso cada vez que hay una nueva pesada\n\nEn el nodo de matricula (linea 27) hacemos el cambio del flag ID_obligatorio al flag salida_ID_obligatorio cuando el movimiento es final. De esta forma ID_obligatorio aplica a la entrada \nexclusivamente y salida_ID_obligatorio aplica a la salida solamente.\nEn el nodo QR/RFID hemos agregado la puesta a cero del flag de QR(linea 67) y del RFID(linea 126) cuando se trata de un ID incorrecto para que el usuario pueda volver a colocar otro ID.\n\n\n**Version 1.42** 29/04/2026\nEn el nodo \"Analisis\" en la linea 116 hemos puesto un timer que espera unos segundos antes de sacar la notificacion de ubicacion. Esto se hace para esperar a que la notificacion anterior en caso de existir\npermanezca un tiempo y en todo caso para que esta sea la ultima notificacion que quede en pantalla hasta que el vehiculo abandone la bascula. \nEliminamos de la pantalla el mensaje Acerque QR o Tarjeta en caso de recibir una lista de menu. Esto lo hacemos en el nodo \"check json\".\nEn el nodo \"Envio de Mensajes\" (linea 65) eliminamos las notificaciones que haya al final del movimiento (final de pesada) salvo si la notificacion es de Ubicacion.\nSe hicieron cambios en index.html y en index.js del uibuilder /totem/ para incluir en el menu de Tipo de Configuracion un boton (LISTA DE PARAMETROS) que nos permita visualizar todos los parametros que podemos incluir en cualquiera de los tipos de configuracion\nSe introdujo un chequeo en el nodo \"QR/RFID\" en la linea 37 para eliminar el boton de salida (simular que fue pulsado) cuando se recibe un QR valido durante el movimiento final. Lo mismo hacemos mas abajo en la recepcion de RFID.\nEn la parte RFID dado que luego de recibir la tarjeta validada en BD todavia necesitamos validar si la matricula asociada a la tarjeta tiene vehiculo en transito, esto hace que el estado movimiento = inicial puede permanecer por mas tiempo (mientras se chequea si hay transito) por lo tanto en el chequeo \ntenemos que meter la variable \"matricula_revisada\" como requisito para avanzar ya que es quien nos indica si el estado del movimiento ya es definitivo. Recordemos que este chequeo solo lo queremos hacer durante el movimiento final, es decir cuando el vehiculo esta cerrando toda la pesada.\n\n\n**Version 1.41** 1/04/2026\n- Dado que puede darse el caso que la camara de contexto no responda en un determinado momento a la solicitud de imagen, se han introducido unas condiciones para tratar de obtener\nla imagen de contexto en caso de fallo temporal. El metodo resumido es el siguiente: Cuando estamos seguros que el vehiculo esta correctamente posicionado\nen la bascula (peso estable) hacemos una solicitud y marcamos un estado (\"waiting1\"), esperamos por la imagen un tiempo definido en la variable (\"camara_contexto_delay1\") \nencaso de no llegar hacemos una segunda solicitud y marcamos el estado (\"waiting2\") y esperamos por la imagen un tiempo definido en la variable (\"camara_contexto_delay2\").\nMientras tanto el semaforo esta en rojo esperando la imagen de contexto, suponiendo que todo lo demas esta ok. Al cabo del ultimo tiempo, marcamos el nuevo estado\nen funcion de lo sucedido, \"ok\" si recibimos la imagen \"fail\" si no la hemos recibido. En cualquiera de los dos casos el semaforo pasa a verde y se continua con la pesada\nEn caso de no tener imagen de contexto, el nombre de la imagen ira vacio (\"\") al igual que la imagen.\n\n**Version 1.4** 25/03/2026\n- Debido a nuevo requerimiento se introduce la opcion (configurable) de tener una camara de contexto en la bascula. Esta camara en caso de estar\nhabilitada 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\n\"estable\" que es donde se hace la deteccion del camion completamente en la bascula. Posteriormente en el nodo \"Analisis del modo de Operacion\" es\ndonde verificamos que la imagen fue tomada y la colocamos en el registro que se esta haciendo para generar el movimiento correspondiente. El Grupo \n\"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\nde configuracion al igual que su habilitacion en el sistema.\n- 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\nse ha incluido una funcion en el menu de configuracion -> Pruebas del sistema -> Pedir Snapshot de contexto de forma que el usuario pueda visualizar \nla imagen actual que la camara esta enviando y pueda de esa forma confirmar el correcto funcionamiento de la conexión y de los equipos.\n\n**Version 1.3.2** \n- Eliminado la inicializacion en el nodo peso del flag distance_flag_SET del detector de vehiculos. Esto ocasionaba problemas en si \ncoincidia que estaba contando los pulsos de salida y antes de finalizar las bascula se ponia en peso < pmin. En esa situacion el detector\nse quedaba bloqueado in poder indicar detector OFF.\n\n- Hemos colocado un codigo de dos lineas para eliminar el boton del menu activo \"COMPLETAR INFO\" en caso que se presente una tarjeta o un codigo QR. \nEsto lo hicimos en el nodo \"Analisis del modo de Operacion\" en la linea 70 donde chequeamos si estamos en movimiento inicial.\n\n- Corregido un \"error\" que no permitia cerrar el movimiento inicial cuando se pasaba una tarjeta si teniamos el menu activo y la firma obligatoria. \nSe agrego una condicion (\"ID_verificado\") en la condicion global para llamar a la firma. Esto es en el nodo \"Analisis de Operacion\" en las primeras lineas\nif (flow.get(\"firma_obligatoria\") && !flow.get(\"firma_flag\") && !flow.get(\"firma_ok_flag\") && !flow.get(\"supervisor\") && (!flow.get(\"menu_activo\") || (flow.get(\"menu_activo\") && (flow.get(\"menu_seleccionado_flag\") || ID_verificado)))) \nCon esto permite continuar con la firma en caso que tengamos menu_activo y firma obligatoria y entremos con tarjeta o con codigo QR.\n\n**Version 1.3.1**\n- Hemos colocado una opcion mas en el menu de configuración en \"Pruebas del Sistema\" -> \"Probar Sensor\". Esto permite visualizar la salida del valor\nde la distancia que esta midiendo el sensor cada segundo. Para que el medidor no este continuamente inundando el configurador con el dato de distancia\nhemos colocado un switch el cual solo deja pasar ese dato cuando se activa el menu, lo inhabilita cuando se sale del menu.\n\n- Hemos puesto el flag flow.set(\"RFID_ok_flag\", false) cuando leemos un QR y flow.set(\"QR_ok_flag\", false) cuando leemos una tarjeta, esto permite\nque se utilice un QR aunque primero hayamos metido una tarjeta invalida y viceversa. Se hizo en el nodo \"envio QR/RFID\".\n\n**Version 1.3** \n- En esta version hemos cambiado los timers a modo contexto local (del nodo) Importante este punto ya que el timer es un objeto no serializable\ny por lo tanto no deberiamos guardar en contexto flow o global ya que esos contextos estan previstos para objetos serializables (estados, valores, arrays, json...)\nSi lo ponemos en flow el timer puede romperse, quedar corrupto, o incoherente al hacer deploy o cualquier operacion de inicio.\nlo limpio es hacerlo en el contexto del nodo y poner el cierre del timer al salir del nodo en node.on(\"close\"). En su lugar ponemos el codigo\nen la pestaña del editor del nodo \"Al final\" que es lo mismo. La pestaña Close se refiere a cuando el nodered se reinicia, se hace redeploy etc..\n\n- Hemos cambiado los timers del snapshot en Analisis detector y Analisis de peso, igualmente hemos cambiado la forma de apagarlo desde otro nodo.\nPara ello hemos implementado en el nodo donde reside el timer un codigo que se activa cuando topic = cancel_snapshot_timer.\n\n- Se arreglo la repeticion del mensaje (audio) de firme en la pantalla. Para ello hemos inluido el flag de solicitud de firma en el \"if\" de envio del mensaje\n de manera que si ya se hizo solicitud de la firma no se aplica el mensaje nuevamente.\n\n- Hemos puesto un nuevo nodo (esperar) en el grupo deteccion de matricula para evitar un posible aunque remoto caso cuando se activa el detector remoto pero no se activa la bascula\ndurante unos segundos. La descripcion esta en un nodo comentario \"CASO QUE PODRIA BLOQUEAR EL SISTEMA\" que se encuentra en el grupo \"ITSAPI - WEB SERVER Y ANALISIS\".\n\n**Version 1.2** Hemos agregado una nueva funcionalidad para indicarle al conductor acerca de la ubicacion interna donde debe dirigirse con el residuo que\ntransporta. Esta notificacion se muestra una vez que hayan cumplido todos los requisitos de entrada y antes de poner el semaforo en verde. \nPara esta funcionalidad hemos introducido un codigo en el nodo \"Analisis del modo de Operacion\" (lineas 76 a 112) y en el nodo \"check json content\"\nen las lineas 44 a 50 bajo el titulo \"RESPUESTA DE LA REVISION DE LA UBICACION INTERNA EN BD\". Si recibimos respuesta positiva de la BD es decir que si \ntiene ubicacion interna el residuo presentado entonces sacamos notificacion con lo que dice el campo de descripcion encontrado en la tabla, en caso contrario\nel mensaje que aparece en pantalla es diciendo que no hay ubicacion para ese residuo.\nSe ha hecho una modificacion en el nodo \"detector rx\" para inicializar todas las variables del Totem cuando ocurra una falsa deteccion de vehiculo, es decir \ncuando que se recibe la señal de detector remoto ON pero no se activa la bascula en el tiempo previsto. Dado que en ese momento hay variables que han cambiado de estado \ny ademas se ha recibido una imagen, es necesario forzar la inicializacion de todas las variables ya que el peso no se ha movido de cero. Si dejamos las variables\nsin inicializar, cuando un vehiculo entre es probable que haya estados confusos.\n\n**Version 1.12** Se corrigio el nodo peso estable para procesar correctamente el valor de Peso Maximo Admitido y sacar un mensaje que no se pueda borrar\nhasta que el vehiculo abandone la bascula. Se agrego un nodo para mantener el aviso de salir de la bascula en caso de PMA, si el usuario pulsa OK.\nCorregido en el nodo \"check JSON content\" la recepcion de la matricula asociada a una tarjeta. Hemos activado los flags correspondientes para que el nodo \n\"matricula 2\" pueda hacer el chequeo de la matricula respecto a si esta o no en transito. \nEn el nodo \"Analisis del modo de operacion\" hemos puesto una condicion para no mostrar el boton de salida cuando el usuario utiliza una tarjerta valida.\nEn el nodo \"Analisis del modo de operacion\" hemos colocado una condicion para sacar la pantalla de firma. Esta condicion es que se trate de que estamos en \nel proceso de movimiento inicial. Adicionalmente si estamos en el proceso de movimiento final y la firma es obligatoria, marcamos el flag de firma como si estuviera\nrealizada ya que para la salida no es preciso firmar.\nEn el archivo generado de la firma se elimino el encabezado \"data:image/png Base64,\" Esto lo hacemos en el nodo firma en la linea 252 del script.\ndataUrl = canvas.toDataURL().split(\",\")[1];\n\n**Version 1.11**Se puso una condicion (peso en bascula > pmin) en el nodo \"enviar matricula\" para enviar matricula al nodo de analisis. Esto evita que se este\nenviando matricula si se recibe sin que haya vehiculo en bascula. Esto no evita que se reciban matriculas automaticas antes de entrar el vehiculo en la bascula.\nEstas ultimas fotos se conservan en memoria hasta que el vehiculo entre en la bascula y pueden ser utilizadas como foto principal en caso de fallo en la solicitud.\nHemos eliminado en la linea 156 del nodo matricula2 la condicion de \"qr_revisado\" para eximirlo de disponer de matricula debido a que ahora el codigo qr puede estar asociado a varias matriculas.\ny por lo tanto sera necesario disponer de una matricula a pesar de tener el codigo de autorizacio (solo indica residuo).\nHemos inicializado la matricula [flow.set(\"matricula\", \"Sin Matricula\")] tambien en el momento en que el peso supera a pmin por primera vez (es decir entrando en la bascula).\nSe agrego en la salida de la edicion de matricula [flow.set(\"matriculacheck_solicitada\", false)] para permitir que se busque nuevamente la matricula en BD.\n\n**Version 1.1** Hemos agregado excepcion de obligacion de tener que disponer de matricula para el caso de presentar una tarjeta RFID. Anteriormente se habia puesto en el nodo de \"Analisis\ndel modo de operacion\" (*let matricula_obligatoria = flow.get(\"matricula_obligatoria\") || true // siempre es obligatoria*) una linea para obligar a tener siempre matricula,\nindependiente de lo que este configurado en el archivo de configuracion. Ahora esa linea sigue funcionando pero en el caso de la tarjeta se permite que no haya matricula ya que esta identificacion \ncontiene la matricula unica. Ver linea 53 en el nodo mencionado anteriormente. No es el caso de los QR los cuales puede haber el mismo GUUID asociados a diferentes matriculas.\n\nSe han agregado nos nuevos campos a la tabla movimierntos_cerrados en el supervisor estos campos son \"qr_salida\" y \"rfid_salida\". La presencia de estos \ndos campos es para disponer de la identificaciob utilizada en salida en caso de hacerlo. Para ello en el nodo \"Analisis del modo de Operacion\" se evalua \nsi estamos en el movimiento inicial o final para ver cual campo es el que tenemos que rellenar. Est se hace el la linea nº 100\nSe ha moviso la inicializacion flow.set(\"distance_flag_SET\", false) al momento donde la bascula pasa de peso < pmin por primera vez. \nAntes se inicializaba cada vez que el peso que llegaba era menor que pmin. Esto podia ocasionar un error ya que el nodo detector perdia el control\nde este flag al ser inicializado cada vez que el peso < pmin.\n\nEl nodo \"matricula 2\" sustituye al nodo matricula anterior. Este nuevo contiene todas las modificaciones necesarias para adaptarse a los \nnuevos requerimientos de la bascula automatica\n\nPara que el QR pueda entrar en la planta es necesario que la matricula este disponible.\nEn caso de que la matricula leida sea diferente a la matricula obtenida en la BD luego de enviar el QR se toma la matricula leida de la BD\n\n**version 1.0.** Este codigo de Bascula Autonoma sale a partir del Totem Autonomo. Todo el codigo relativo a la gestion de BAse de Datos se traslada al RPI Supervisor\nEn este RPi solo queda lo relativo a la gestion de sensores, camaras, lectores de Identioficacion, semaforo, barrera y la pantalla para que el usuario \npueda interactuar con el sistema como por ejemplo hacer seleccion del residuo a traves de un menu de seleccion.\n\nEn esta version se ha introducido el termino PMA(Peso Maximo Autorizado. Este es un valor que el Totem recibe cuando el Supervisor le envia la autorizacion\nde la matricula junto con la tara- El Totem revisa el peso estable del vehiculo con el PMA recibidoy en caso de ser superior se envia un aviso en la pantalla\nindicandole al conductor que debe retroceder ya que supera el peso admitido.\n\nOtra función que contiene esta version es el chequeo de la Tara. Si el valor recibido de la Tara es superior a cero entonces el movimiento inicial recibido por el supervisor \nes enviado como movimiento cerrado al Servidor.Realmente para el Totem es transparente. La gestion de la Tara en efecto la hace el Supervisor una vez que recibe\nel movimiento inicial que envia el Totem.", "x": 1300, "y": 800, "wires": [] @@ -5927,7 +5931,7 @@ "className": "", "topic": "", "name": "aviso PMA", - "x": 1350, + "x": 1270, "y": 1160, "wires": [ [ @@ -5949,7 +5953,7 @@ "targetType": "full", "statusVal": "", "statusType": "auto", - "x": 1240, + "x": 1160, "y": 1080, "wires": [] }, @@ -5963,7 +5967,7 @@ "links": [ "0df0c819dae1658d" ], - "x": 1925, + "x": 1845, "y": 1100, "wires": [] }, @@ -6330,14 +6334,14 @@ "z": "953e8740401d175c", "g": "9328043d4e9512a9", "name": "matricula-2", - "func": "// DEFINICION DE ALGUNOS FLAGS UTILIZADOS EN EL NODO\n//matricula_leida_flag: Si es igual a true, indica que hemos recibido matrícula de la camara.La matrícula puede ser valida o no.Sólo indica recepción de objeto de camara\n//peso_bajo_flag: Si es igual a true, indica que el ESTADO del peso esta por debajo del peso minimo.\n//Sirve para tomar unas acciones una sola vez cuándo se detecta peso > pmin(el flag esta en true en ese momento) y luego el flag pasa a false es decir el ESTADO ahora es peso por encima del minimo.\n//matricula_revisada: Si true, indica que la matrícula ha sido revisada en la Base de Datos\n//matricula_autorizada: Si true, indica que la matrícula ha sido encontrada en la base de datos y esta autorizada es decir dispone de un contrato por lo tanto el usuario va a recibir un menu en pantalla.\n//matricula_ok_flag : Si igual a true indica que el proceso de lectura de matrícula (camara o edicion en pantalla) ha sido completado satisfactoriamente y el sistema puede seguir adelante.\n//matricula_formato_flag: Si es igual a true indica que la matrícula recibida de la camara tiene el formato adecuado o que el conductor ha introducido en la pantalla una matrícula correcta en formato.\n//edicion_matricula: Si true, indica que el usuario puede utilizar el editor de matrícula en caso de error de lectura, para colocar la matrícula correcta.\n//editor_matricula_ok: Si true, indica que el usuario ha aceptado abrir el editor de matrículas.\n\n\n//====================================== Chequea validez de Matricula ========================================\nlet repetir_matricula = false // variable para gestionar repeticion de la edicion de matricula en caso de error en la introduccion\nif (flow.get(\"matricula_leida_flag\") && !flow.get(\"supervisor\") && flow.get(\"totem_master\") && !flow.get(\"peso_bajo_flag\")) { // Primero verifica si se ha leido y si esta aun en proceso\n if (flow.get(\"matricula_formato_flag\")) {// Verifica si la matricula recibida o editada esta en formato\n flow.set(\"matricula_ok_flag\", true) // matricula conforme en formato para seguir adelante con el proceso\n if (flow.get(\"matricula_revisada\")) {//chequea si ya se ha revisado la existencia de la matricula en BD\n if (flow.get(\"matricula_autorizada\")) {// Revisa si la matricula esta autorizada en BD\n flow.set(\"matricula_leida_flag\", false) // proceso de matricula ya finalizado, evita entrar aqui otra vez\n /*\n if (flow.get(\"tipo_movimiento\") == \"final\" && flow.get(\"botonsalida\") == false) {\n //node.warn(\"activado boton de salida\")\n flow.set(\"botonsalida\", true)\n }\n */\n if (flow.get(\"tipo_movimiento\") == \"final\" && flow.get(\"salida_ID_obligatorio\") == true) {\n //node.warn(\"activado boton de salida\")\n flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"));\n }\n else if (flow.get(\"tipo_movimiento\") == \"final\" && flow.get(\"botonsalida\") == false) {\n flow.set(\"botonsalida\", true)\n }\n //else if (flow.get(\"tipo_movimiento\") == \"inicial\" && !flow.get(\"menu_activo\")) {\n else if (flow.get(\"tipo_movimiento\") == \"inicial\" ) { // sacamos siempre el mensaje aunque sea opcional el ID\n flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"));\n }\n \n }\n else {\n\n // SIN edicion de matricula\n if (!flow.get(\"edicion_matricula\") && !flow.get(\"nota_avise_basculista_done\")) {\n node.send([null, { payload: \"

MATRICULA NO REGISTRADA

AVISE AL BASCULISTA

\" }, null])\n flow.set(\"nota_avise_basculista_done\", true)\n }\n\n // Matricula editada anteriormente\n else if (flow.get(\"editor_matricula_ok\") && !flow.get(\"nota_vuelva_intentar_done\")) {\n node.send([null, { payload: \"

MATRICULA NO REGISTRADA

VUELVA A INTENTARLO

\" }, null])\n flow.set(\"nota_vuelva_intentar_done\", true)\n flow.set(\"badplate\", true) // para poder volver a sacar anuncio de matricula mala y editor de matricula\n flow.set(\"badplate_llamado\", false)\n repetir_matricula = true // para poder hacer un delay antes de sacar anuncio de editor\n }\n\n // Edicion de Matricula\n else if (flow.get(\"edicion_matricula\") && !flow.get(\"nota_modifique_matricula_done\")) {\n node.send([null, { payload: \"

MATRICULA NO REGISTRADA

MODIFIQUE MATRICULA

\" }, null])\n flow.set(\"nota_modifique_matricula_done\", true)\n flow.set(\"nota_vuelva_intentar_done\", true)\n flow.set(\"badplate\", true) // para poder volver a sacar anuncio de matricula mala y editor de matricula\n repetir_matricula = true // para poder hacer un delay antes de sacar anuncio de editor\n }\n /*\n //CON identificacion especial\n else if (flow.get(\"identificacion_esp\")) {\n flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n flow.set(\"identificacion_esp_aplicada\", true)\n flow.set(\"matricula_ok_flag\", true) // marcamos como matricula conforme para seguir adelante con el proceso\n flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n if (flow.get(\"botonsalida_activo\")) {\n flow.set(\"botonsalida\", true)\n }\n node.send([null, { payload: \"

MATRICULA ERRONEA

DEBE PRESENTAR UN QR O TARJETA VALIDA

\" }, null])\n }\n \n if (((flow.get(\"QR_revisado\") && flow.get(\"QR_verificado\")) || (flow.get(\"QR_revisado\") && flow.get(\"QR_verificado\"))) && flow.get(\"matricula_autorizada\")){\n\n }\n */\n }\n }\n else {// como aun no se ha revisado en BD entonces enviamos solicitud al Supervisor\n if(!flow.get(\"matriculacheck_solicitada\")){\n let matricula = flow.get(\"matricula\")\n //msg.topic = `SELECT v.Matricula, v.Autorizado FROM Vehiculos v WHERE v.Matricula = ('${matricula}')`;\n msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), device: \"supervisor\", accion: \"check\", matricula: matricula }\n node.send([null, null, msg])\n flow.set(\"matriculacheck_solicitada\", true) // marca que se solicito revision de matricula\n setTimeout(function () { // timer para revisar si recibimos respuesta de BD\n if(!flow.get(\"matricula_revisada\")){ // Si matricula no ha sido revisada se vuelve a activar el chequeo de la matricula\n flow.set(\"matriculacheck_solicitada\", false)\n node.send([{payload:1}, null, null])\n }\n }, 5000)\n \n }\n }\n \n /*\n \n if(flow.get(\"menu_activo\")){// chequea si el totem debe estar pendiente de las matriculas y los contratos\n //================================================== MENU ACTIVO ================================================================================\n \n }\n //============================================================= MENU NO ACTIVO ======================================================================================\n else {// MATRICULA en formato, SIN MENU ACTIVO\n //node.warn(\"sin menu, matricula ok\")\n \n flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"))\n //node.send([null, { payload: \"

MATRICULA OK

DEBE PRESENTAR UN QR O TARJETA VALIDA

\" }, null])\n let matricula = flow.get(\"matricula\")\n msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), device: \"supervisor\", accion: \"check\", matricula: matricula }\n node.send([null, null, msg]) \n //Guardamos la imagen del vehiculo en un archivo. Lo hacemos aqui y no al recibir la imagen por si hubo una edicion de la matricula\n //en ese caso el nombre de la imagen fue regenerado con la nueva matricula\n //flow.set(\"matricula_leida_flag\", false) // proceso de matricula ya finalizado, evita entrar aqui otra vez\n let msg_plate = { payload: flow.get(\"matricula_image\"), filename: flow.get(\"path_images\") + flow.get(\"nombre_imagen\") }\n node.send([msg_plate, null])\n\n }\n */\n }\n //=============================== MATRICULA FUERA DE FORMATO =====================================\n else {\n // CON Edicion de Matricula\n if (flow.get(\"edicion_matricula\")) { \n if (!flow.get(\"editor_matricula_ok\")) {// Si la edicion ha sido habilitada y aun no hemos sacado el editor de matricula entonces sacamos la ventana de edicion\n flow.set(\"badplate\", true)\n //flow.set(\"badplate_llamado\", false) // permite sacar nuevamente la notificacion para editar matricula\n }\n\n //Caso donde el usuario no hace edicion sino que presenta identificacion\n //if(flow.get(\"RFID_revisado\") || flow.get(\"qr_revisado\")){\n if(flow.get(\"RFID_revisado\")){ // consideramos solo tarjeta ya que el QR puede estar asociado a varias matriculas\n flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n flow.set(\"identificacion_esp_aplicada\", true)\n flow.set(\"matricula_ok_flag\", true) // marcamos como matricula conforme para seguir adelante con el proceso\n //flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n }\n\n }\n\n // SIN Edicion de Matricula\n else if (!flow.get(\"edicion_matricula\")) { // si edicion de matricula no esta habilitada solicitamos que el vehiculo entre de nuevo en la bascula\n //completamos datos del registro para registrar el movimiento al momento que el peso < pmin\n \n //node.send([null, { payload: \"

MATRICULA ERRONEA

VUELVA A ENTRAR EN LA BASCULA O PRESENTE UNA IDENTIFICACION

\" }, null])\n let matricula = flow.get(\"matricula\")\n node.send([\n null,\n {\n payload:\n \"
\" +\n \"

MATRICULA ERRONEA

\" +\n \"

Matrícula: \" + matricula + \"

\" +\n \"

VUELVA A ENTRAR EN LA BASCULA O PRESENTE UNA IDENTIFICACION

\" +\n \"
\"\n },\n null\n ]);\n\n\n\n\n\n\n//Tengo que analizar el caso cuando no hay editor de matricula . Hay que salir de la bascula o presentar un ID para verificar la matricula asociada\n// **************************************************************************************\n\n\n/* PARTE ANTERIOR *******************************************************************\n\n // SIN Identificacion Especial\n if (!flow.get(\"identificacion_esp\")) {\n let new_register = flow.get(\"register\")\n new_register.matricula = flow.get(\"matricula\")\n new_register.matriculaeditada = flow.get(\"matricula_editada_flag\")\n new_register.nombre_imagen = flow.get(\"nombre_imagen\")\n new_register.qr = flow.get(\"last_QR\")\n new_register.rfid = flow.get(\"last_RFID\")\n new_register.sentido = flow.get(\"sentido\")\n new_register.botonvalidacion = flow.get(\"salida_valida_flag\")\n\n flow.set(\"register\", new_register)\n flow.set(\"movimiento_registrado_flag\", true)\n //sacamos notificacion en pantalla para volver a entrar en bascula\n node.send([null, { payload: \"

MATRICULA ERRONEA

VUELVA A ENTRAR EN LA BASCULA

\" }, null])\n }\n\n // CON Identificacion Especial y QR o RFID revisado (autorizado)\n //else if (flow.get(\"RFID_revisado\") || flow.get(\"QR_revisado\")) {//caso donde identificacion_esp es verdadero\n else{\n flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n flow.set(\"identificacion_esp_aplicada\", true)\n flow.set(\"matricula_ok_flag\", true) // marcamos como matricula conforme para seguir adelante con el proceso\n flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n if (flow.get(\"botonsalida_activo\")) {\n flow.set(\"botonsalida\", true)\n }\n node.send([null, { payload: \"

MATRICULA ERRONEA

DEBE PRESENTAR UN QR O TARJETA VALIDA

\" }, null])\n }\n\n************************************************************************************/\n\n }\n }\n}\n\n//------------------------------------------ Edicion de Matricula ------------------------------------------------------\nif (flow.get(\"badplate\") && !flow.get(\"badplate_llamado\")) { // se ejecuta si no se ha llamado antes\n flow.set(\"badplate_llamado\", true) // Marcamos que hemos sacado la notificacion \n let matricula = flow.get(\"matricula\")\n if (matricula == \"NOT FOUND\" || matricula == \"\"){ // evitamos escribir NOT FOUND en la pantalla de edicion de matricula\n matricula = \"\"\n }\n const matricula_array = matricula.split('')\n const matricula_final = new Array(8) // Tamaño maximo de la matricula\n for (let i = 0; i < matricula_array.length; i++) {\n matricula_final[i] = matricula_array[i];\n }\n flow.set(\"matricula_array\", matricula_final)\n //node.warn(\"matricula_revisada = \" + flow.get(\"matricula_revisada\") + \" matricula = \" + matricula)\n if(repetir_matricula != true){\n node.send([null, {payload:\"

MATRICULA ERRONEA

MODIFIQUE EN LA SIGUIENTE PANTALLA

\", matricula:matricula_final, size:matricula_final.length}])\n }\n else\n setTimeout(() => {\n node.send([null, { payload: \"

MODIFIQUE MATRICULA

EN LA SIGUIENTE PANTALLA

\", matricula: matricula_final, size: matricula_final.length }])\n }, 3000); \n}\n//-------------------------------------------------------------------------------------------------------------------------\n //=============================================================================================================\nreturn;", + "func": "// DEFINICION DE ALGUNOS FLAGS UTILIZADOS EN EL NODO\n//matricula_leida_flag: Si es igual a true, indica que hemos recibido matrícula de la camara.La matrícula puede ser valida o no.Sólo indica recepción de objeto de camara\n//peso_bajo_flag: Si es igual a true, indica que el ESTADO del peso esta por debajo del peso minimo.\n//Sirve para tomar unas acciones una sola vez cuándo se detecta peso > pmin(el flag esta en true en ese momento) y luego el flag pasa a false es decir el ESTADO ahora es peso por encima del minimo.\n//matricula_revisada: Si true, indica que la matrícula ha sido revisada en la Base de Datos\n//matricula_autorizada: Si true, indica que la matrícula ha sido encontrada en la base de datos y esta autorizada es decir dispone de un contrato por lo tanto el usuario va a recibir un menu en pantalla.\n//matricula_ok_flag : Si igual a true indica que el proceso de lectura de matrícula (camara o edicion en pantalla) ha sido completado satisfactoriamente y el sistema puede seguir adelante.\n//matricula_formato_flag: Si es igual a true indica que la matrícula recibida de la camara tiene el formato adecuado o que el conductor ha introducido en la pantalla una matrícula correcta en formato.\n//edicion_matricula: Si true, indica que el usuario puede utilizar el editor de matrícula en caso de error de lectura, para colocar la matrícula correcta.\n//editor_matricula_ok: Si true, indica que el usuario ha aceptado abrir el editor de matrículas.\n\n\n//====================================== Chequea validez de Matricula ========================================\nlet repetir_matricula = false // variable para gestionar repeticion de la edicion de matricula en caso de error en la introduccion\nif (flow.get(\"matricula_leida_flag\") && !flow.get(\"supervisor\") && flow.get(\"totem_master\") && !flow.get(\"peso_bajo_flag\")) { // Primero verifica si se ha leido y si esta aun en proceso\n if (flow.get(\"matricula_formato_flag\")) {// Verifica si la matricula recibida o editada esta en formato\n flow.set(\"matricula_ok_flag\", true) // matricula conforme en formato para seguir adelante con el proceso\n if (flow.get(\"matricula_revisada\")) {//chequea si ya se ha revisado la existencia de la matricula en BD\n if (flow.get(\"matricula_autorizada\")) {// Revisa si la matricula esta autorizada en BD\n flow.set(\"matricula_leida_flag\", false) // proceso de matricula ya finalizado, evita entrar aqui otra vez\n \n if (flow.get(\"totem_qty\") === 1 && flow.get(\"tipobascula\") === \"entrada\" && flow.get(\"entransito\")) {\n flow.set(\"botonsalida\", false) //elimina posibilidad de presentar boton de OK\n flow.set(\"tipo_movimiento\", undefined) // no permite tomar accione a posteriori\n //node.send([null, { payload: `

MATRICULA ${flow.get(\"matricula\")} TIENE UN TRANSITO PENDIENTE

AVISE AL BASCULISTA

` }, null])\n node.send([null, {\n payload: `

MATRICULA ${flow.get(\"matricula\")} TIENE UN TRANSITO PENDIENTE

AVISE AL BASCULISTA

`}, null])\n } \n \n \n\n if (flow.get(\"tipo_movimiento\") == \"final\" && flow.get(\"salida_ID_obligatorio\") == true) {\n //node.warn(\"activado boton de salida\")\n flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"));\n }\n else if (flow.get(\"tipo_movimiento\") == \"final\" && flow.get(\"botonsalida\") == false) {\n flow.set(\"botonsalida\", true)\n }\n //else if (flow.get(\"tipo_movimiento\") == \"inicial\" && !flow.get(\"menu_activo\")) {\n else if (flow.get(\"tipo_movimiento\") == \"inicial\" ) { // sacamos siempre el mensaje aunque sea opcional el ID\n\n if (flow.get(\"totem_qty\") === 1 && flow.get(\"tipobascula\") === \"salida\" && !flow.get(\"entransito\")) {\n node.send([null, { payload: `

MATRICULA ${flow.get(\"matricula\")} NO TIENE TRANSITO PENDIENTE

AVISE AL BASCULISTA

` }, null])\n }\n else{\n flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"));\n }\n }\n \n }\n else {\n\n // SIN edicion de matricula\n if (!flow.get(\"edicion_matricula\") && !flow.get(\"nota_avise_basculista_done\")) {\n node.send([null, { payload: \"

MATRICULA NO REGISTRADA

AVISE AL BASCULISTA

\" }, null])\n flow.set(\"nota_avise_basculista_done\", true)\n }\n\n // Matricula editada anteriormente\n else if (flow.get(\"editor_matricula_ok\") && !flow.get(\"nota_vuelva_intentar_done\")) {\n node.send([null, { payload: \"

MATRICULA NO REGISTRADA

VUELVA A INTENTARLO

\" }, null])\n flow.set(\"nota_vuelva_intentar_done\", true)\n flow.set(\"badplate\", true) // para poder volver a sacar anuncio de matricula mala y editor de matricula\n flow.set(\"badplate_llamado\", false)\n repetir_matricula = true // para poder hacer un delay antes de sacar anuncio de editor\n }\n\n // Edicion de Matricula\n else if (flow.get(\"edicion_matricula\") && !flow.get(\"nota_modifique_matricula_done\")) {\n node.send([null, { payload: \"

MATRICULA NO REGISTRADA

MODIFIQUE MATRICULA

\" }, null])\n flow.set(\"nota_modifique_matricula_done\", true)\n flow.set(\"nota_vuelva_intentar_done\", true)\n flow.set(\"badplate\", true) // para poder volver a sacar anuncio de matricula mala y editor de matricula\n repetir_matricula = true // para poder hacer un delay antes de sacar anuncio de editor\n }\n /*\n //CON identificacion especial\n else if (flow.get(\"identificacion_esp\")) {\n flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n flow.set(\"identificacion_esp_aplicada\", true)\n flow.set(\"matricula_ok_flag\", true) // marcamos como matricula conforme para seguir adelante con el proceso\n flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n if (flow.get(\"botonsalida_activo\")) {\n flow.set(\"botonsalida\", true)\n }\n node.send([null, { payload: \"

MATRICULA ERRONEA

DEBE PRESENTAR UN QR O TARJETA VALIDA

\" }, null])\n }\n \n if (((flow.get(\"QR_revisado\") && flow.get(\"QR_verificado\")) || (flow.get(\"QR_revisado\") && flow.get(\"QR_verificado\"))) && flow.get(\"matricula_autorizada\")){\n\n }\n */\n }\n }\n else {// como aun no se ha revisado en BD entonces enviamos solicitud al Supervisor\n if(!flow.get(\"matriculacheck_solicitada\")){\n let matricula = flow.get(\"matricula\")\n //msg.topic = `SELECT v.Matricula, v.Autorizado FROM Vehiculos v WHERE v.Matricula = ('${matricula}')`;\n msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), device: \"supervisor\", accion: \"check\", matricula: matricula }\n node.send([null, null, msg])\n flow.set(\"matriculacheck_solicitada\", true) // marca que se solicito revision de matricula\n setTimeout(function () { // timer para revisar si recibimos respuesta de BD\n if(!flow.get(\"matricula_revisada\")){ // Si matricula no ha sido revisada se vuelve a activar el chequeo de la matricula\n flow.set(\"matriculacheck_solicitada\", false)\n node.send([{payload:1}, null, null])\n }\n }, 5000)\n \n }\n }\n \n /*\n \n if(flow.get(\"menu_activo\")){// chequea si el totem debe estar pendiente de las matriculas y los contratos\n //================================================== MENU ACTIVO ================================================================================\n \n }\n //============================================================= MENU NO ACTIVO ======================================================================================\n else {// MATRICULA en formato, SIN MENU ACTIVO\n //node.warn(\"sin menu, matricula ok\")\n \n flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"))\n //node.send([null, { payload: \"

MATRICULA OK

DEBE PRESENTAR UN QR O TARJETA VALIDA

\" }, null])\n let matricula = flow.get(\"matricula\")\n msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), device: \"supervisor\", accion: \"check\", matricula: matricula }\n node.send([null, null, msg]) \n //Guardamos la imagen del vehiculo en un archivo. Lo hacemos aqui y no al recibir la imagen por si hubo una edicion de la matricula\n //en ese caso el nombre de la imagen fue regenerado con la nueva matricula\n //flow.set(\"matricula_leida_flag\", false) // proceso de matricula ya finalizado, evita entrar aqui otra vez\n let msg_plate = { payload: flow.get(\"matricula_image\"), filename: flow.get(\"path_images\") + flow.get(\"nombre_imagen\") }\n node.send([msg_plate, null])\n\n }\n */\n }\n //=============================== MATRICULA FUERA DE FORMATO =====================================\n else {\n // CON Edicion de Matricula\n if (flow.get(\"edicion_matricula\")) { \n if (!flow.get(\"editor_matricula_ok\")) {// Si la edicion ha sido habilitada y aun no hemos sacado el editor de matricula entonces sacamos la ventana de edicion\n flow.set(\"badplate\", true)\n //flow.set(\"badplate_llamado\", false) // permite sacar nuevamente la notificacion para editar matricula\n }\n\n //Caso donde el usuario no hace edicion sino que presenta identificacion\n //if(flow.get(\"RFID_revisado\") || flow.get(\"qr_revisado\")){\n if(flow.get(\"RFID_revisado\")){ // consideramos solo tarjeta ya que el QR puede estar asociado a varias matriculas\n flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n flow.set(\"identificacion_esp_aplicada\", true)\n flow.set(\"matricula_ok_flag\", true) // marcamos como matricula conforme para seguir adelante con el proceso\n //flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n }\n\n }\n\n // SIN Edicion de Matricula\n else if (!flow.get(\"edicion_matricula\")) { // si edicion de matricula no esta habilitada solicitamos que el vehiculo entre de nuevo en la bascula\n //completamos datos del registro para registrar el movimiento al momento que el peso < pmin\n \n //node.send([null, { payload: \"

MATRICULA ERRONEA

VUELVA A ENTRAR EN LA BASCULA O PRESENTE UNA IDENTIFICACION

\" }, null])\n let matricula = flow.get(\"matricula\")\n node.send([\n null,\n {\n payload:\n \"
\" +\n \"

MATRICULA ERRONEA

\" +\n \"

Matrícula: \" + matricula + \"

\" +\n \"

VUELVA A ENTRAR EN LA BASCULA O PRESENTE UNA IDENTIFICACION

\" +\n \"
\"\n },\n null\n ]);\n\n\n\n\n\n\n//Tengo que analizar el caso cuando no hay editor de matricula . Hay que salir de la bascula o presentar un ID para verificar la matricula asociada\n// **************************************************************************************\n\n\n/* PARTE ANTERIOR *******************************************************************\n\n // SIN Identificacion Especial\n if (!flow.get(\"identificacion_esp\")) {\n let new_register = flow.get(\"register\")\n new_register.matricula = flow.get(\"matricula\")\n new_register.matriculaeditada = flow.get(\"matricula_editada_flag\")\n new_register.nombre_imagen = flow.get(\"nombre_imagen\")\n new_register.qr = flow.get(\"last_QR\")\n new_register.rfid = flow.get(\"last_RFID\")\n new_register.sentido = flow.get(\"sentido\")\n new_register.botonvalidacion = flow.get(\"salida_valida_flag\")\n\n flow.set(\"register\", new_register)\n flow.set(\"movimiento_registrado_flag\", true)\n //sacamos notificacion en pantalla para volver a entrar en bascula\n node.send([null, { payload: \"

MATRICULA ERRONEA

VUELVA A ENTRAR EN LA BASCULA

\" }, null])\n }\n\n // CON Identificacion Especial y QR o RFID revisado (autorizado)\n //else if (flow.get(\"RFID_revisado\") || flow.get(\"QR_revisado\")) {//caso donde identificacion_esp es verdadero\n else{\n flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n flow.set(\"identificacion_esp_aplicada\", true)\n flow.set(\"matricula_ok_flag\", true) // marcamos como matricula conforme para seguir adelante con el proceso\n flow.set(\"identificacion_pendiente\", true) // pendiente de presentar identificacion\n if (flow.get(\"botonsalida_activo\")) {\n flow.set(\"botonsalida\", true)\n }\n node.send([null, { payload: \"

MATRICULA ERRONEA

DEBE PRESENTAR UN QR O TARJETA VALIDA

\" }, null])\n }\n\n************************************************************************************/\n\n }\n }\n}\n\n//------------------------------------------ Edicion de Matricula ------------------------------------------------------\nif (flow.get(\"badplate\") && !flow.get(\"badplate_llamado\")) { // se ejecuta si no se ha llamado antes\n flow.set(\"badplate_llamado\", true) // Marcamos que hemos sacado la notificacion \n let matricula = flow.get(\"matricula\")\n if (matricula == \"NOT FOUND\" || matricula == \"\"){ // evitamos escribir NOT FOUND en la pantalla de edicion de matricula\n matricula = \"\"\n }\n const matricula_array = matricula.split('')\n const matricula_final = new Array(8) // Tamaño maximo de la matricula\n for (let i = 0; i < matricula_array.length; i++) {\n matricula_final[i] = matricula_array[i];\n }\n flow.set(\"matricula_array\", matricula_final)\n //node.warn(\"matricula_revisada = \" + flow.get(\"matricula_revisada\") + \" matricula = \" + matricula)\n if(repetir_matricula != true){\n node.send([null, {payload:\"

MATRICULA ERRONEA

MODIFIQUE EN LA SIGUIENTE PANTALLA

\", matricula:matricula_final, size:matricula_final.length}])\n }\n else\n setTimeout(() => {\n node.send([null, { payload: \"

MODIFIQUE MATRICULA

EN LA SIGUIENTE PANTALLA

\", matricula: matricula_final, size: matricula_final.length }])\n }, 3000); \n}\n//-------------------------------------------------------------------------------------------------------------------------\n //=============================================================================================================\nreturn;", "outputs": 3, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 1090, + "x": 1010, "y": 1240, "wires": [ [ @@ -6434,7 +6438,7 @@ "targetType": "full", "statusVal": "", "statusType": "auto", - "x": 1360, + "x": 1280, "y": 1300, "wires": [] }, @@ -6448,7 +6452,7 @@ "links": [ "e0b4dd6107f95f28" ], - "x": 1265, + "x": 1185, "y": 1220, "wires": [] }, @@ -6475,7 +6479,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1490, + "x": 1410, "y": 1160, "wires": [ [ @@ -6499,7 +6503,7 @@ "className": "", "topic": "", "name": "Ubicacion", - "x": 1940, + "x": 1860, "y": 1240, "wires": [ [] @@ -6708,7 +6712,7 @@ "sendError": false, "encoding": "none", "allProps": false, - "x": 2010, + "x": 1930, "y": 1500, "wires": [ [ @@ -6725,7 +6729,7 @@ "group": "ac875e596615614c", "voice": "", "always": true, - "x": 2180, + "x": 2100, "y": 1560, "wires": [] }, @@ -6742,7 +6746,7 @@ "sendError": false, "encoding": "none", "allProps": false, - "x": 2010, + "x": 1930, "y": 1540, "wires": [ [ @@ -6763,7 +6767,7 @@ "sendError": false, "encoding": "none", "allProps": false, - "x": 1990, + "x": 1910, "y": 1580, "wires": [ [ @@ -6783,7 +6787,7 @@ "sendError": false, "encoding": "none", "allProps": false, - "x": 1990, + "x": 1910, "y": 1620, "wires": [ [ @@ -6824,7 +6828,7 @@ "checkall": "true", "repair": false, "outputs": 4, - "x": 1820, + "x": 1740, "y": 1540, "wires": [ [ @@ -6852,7 +6856,7 @@ "6530f7fdb887dbf1", "fab29e0f6601e531" ], - "x": 1365, + "x": 1285, "y": 1540, "wires": [ [ @@ -6873,7 +6877,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1490, + "x": 1410, "y": 1540, "wires": [ [ @@ -6894,7 +6898,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1650, + "x": 1570, "y": 1540, "wires": [ [ @@ -6916,7 +6920,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1600, + "x": 1520, "y": 1640, "wires": [] }, @@ -6929,7 +6933,7 @@ "scope": [ "23706ca448e439ea" ], - "x": 1430, + "x": 1350, "y": 1640, "wires": [ [ @@ -7062,7 +7066,7 @@ "links": [ "9b0761cc102bc028" ], - "x": 1195, + "x": 1115, "y": 1180, "wires": [] }, @@ -7083,7 +7087,7 @@ "authType": "digest", "senderr": false, "headers": [], - "x": 1760, + "x": 1680, "y": 1360, "wires": [ [ @@ -7098,14 +7102,14 @@ "z": "953e8740401d175c", "g": "1b1ee13b380e32b6", "name": "convert to base64", - "func": "const reqIdActual = flow.get(\"camara_contexto_reqid\");\nconst estado = flow.get(\"camara_contexto_estado\") || \"idle\";\nconst reqIdMsg = msg.contexto_reqid;\n// Ignorar respuestas viejas o fuera de contexto\nif (reqIdMsg && reqIdMsg !== reqIdActual) {\n node.warn(\"Respuesta de contexto ignorada por reqId antiguo\");\n return null;\n}\n// Si ya estaba resuelto, ignoramos duplicados\nif (estado === \"ok\" || estado === \"fail\") {\n return null;\n}\n\n// Validar contenido recibido\nlet imagenB64 = \"\";\n\nif (Buffer.isBuffer(msg.payload)) {\n imagenB64 = msg.payload.toString(\"base64\");\n} else if (typeof msg.payload === \"string\") {\n imagenB64 = msg.payload;\n} else {\n node.warn(\"La respuesta de la cámara no contiene imagen válida\");\n return null;\n}\n\n// Guardar resultado exitoso\nflow.set(\"last_camara_contexto\", imagenB64);\nflow.set(\"camara_contexto_rx\", true);\nflow.set(\"camara_contexto_estado\", \"ok\");\n\n//Generamos nombre del archivo de la imagen recibida\nlet now = new Date()\nlet nowdate = now.toLocaleString(\"sv-SE\").replace(/[- :]/g, \"\")\nif (flow.get(\"tipo_movimiento\") === \"inicial\") {\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_i-\" + nowdate + \".jpg\")\n}\nelse {\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_f-\" + nowdate + \".jpg\")\n}\n\n// Cancelar timers pendientes\nconst t1 = flow.get(\"camara_contexto_timer1\");\nif (t1) clearTimeout(t1);\nconst t2 = flow.get(\"camara_contexto_timer2\");\nif (t2) clearTimeout(t2);\nflow.set(\"camara_contexto_timer1\", null);\nflow.set(\"camara_contexto_timer2\", null);\n\nmsg.camara_contexto_estado = \"ok\";\n\nreturn { payload: 1 };\n\n\n/*\n\n// msg.payload llega como Buffer\nif (Buffer.isBuffer(msg.payload)) {\n msg.payload = msg.payload.toString('base64');\n flow.set(\"last_camara_contexto\", msg.payload)\n flow.set(\"camara_contexto_rx\", true)\n let now = new Date()\n //const nowdate = now.toLocaleString('es-ES', { hour12: false });\n let nowdate = now.toLocaleString(\"sv-SE\").replace(/[- :]/g, \"\")\n if(flow.get(\"tipo_movimiento\") === \"inicial\"){\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_i-\" + nowdate + \".jpg\")\n }\n else{\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_f-\" + nowdate + \".jpg\")\n }\n}\nreturn {payload:1};\n*/", + "func": "const reqIdActual = flow.get(\"camara_contexto_reqid\");\nconst estado = flow.get(\"camara_contexto_estado\") || \"idle\";\nconst reqIdMsg = msg.contexto_reqid;\n// Ignorar respuestas viejas o fuera de contexto\nif (reqIdMsg && reqIdMsg !== reqIdActual) {\n node.warn(\"Respuesta de contexto ignorada por reqId antiguo\");\n return null;\n}\n// Si ya estaba resuelto, ignoramos duplicados\nif (estado === \"ok\" || estado === \"fail\") {\n return null;\n}\n\n// Validar contenido recibido\nlet imagenB64 = \"\";\n\nif (Buffer.isBuffer(msg.payload)) {\n imagenB64 = msg.payload.toString(\"base64\");\n} else if (typeof msg.payload === \"string\") {\n imagenB64 = msg.payload;\n} else {\n node.warn(\"La respuesta de la cámara no contiene imagen válida\");\n return null;\n}\n\n// Guardar resultado exitoso\nflow.set(\"last_camara_contexto\", imagenB64);\nflow.set(\"camara_contexto_rx\", true);\nflow.set(\"camara_contexto_estado\", \"ok\");\n\n//Generamos nombre del archivo de la imagen recibida\nlet now = new Date()\nlet nowdate = now.toLocaleString(\"sv-SE\").replace(/[- :]/g, \"\")\nif (flow.get(\"tipo_movimiento\") === \"inicial\") {\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_i-\" + nowdate + \".jpg\")\n}\nelse {\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_f-\" + nowdate + \".jpg\")\n}\n\n// Cancelar timers pendientes\nconst t1 = flow.get(\"camara_contexto_timer1\");\nif (t1) clearTimeout(t1);\nconst t2 = flow.get(\"camara_contexto_timer2\");\nif (t2) clearTimeout(t2);\nflow.set(\"camara_contexto_timer1\", null);\nflow.set(\"camara_contexto_timer2\", null);\n\nmsg.camara_contexto_estado = \"ok\";\n\n//======================================= ENVIO DE IMAGEN PARA MONITOREO ===========================================================\n// verificamos si tenemos que enviar la imagen de contexto al supervisor\nlet newmsg = flow.get(\"envia_mat_img\")\n ? {\n payload: { device: \"supervisor\", accion: \"lastcontext\", data: { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), imagen_contexto: imagenB64 } }\n }\n : null;\n//====================================================================================================\nreturn [{ payload: 1 }, newmsg];\n\n\n/*\n\n// msg.payload llega como Buffer\nif (Buffer.isBuffer(msg.payload)) {\n msg.payload = msg.payload.toString('base64');\n flow.set(\"last_camara_contexto\", msg.payload)\n flow.set(\"camara_contexto_rx\", true)\n let now = new Date()\n //const nowdate = now.toLocaleString('es-ES', { hour12: false });\n let nowdate = now.toLocaleString(\"sv-SE\").replace(/[- :]/g, \"\")\n if(flow.get(\"tipo_movimiento\") === \"inicial\"){\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_i-\" + nowdate + \".jpg\")\n }\n else{\n flow.set(\"nombre_contexto\", flow.get(\"matricula\") + \"-contexto_f-\" + nowdate + \".jpg\")\n }\n}\nreturn {payload:1};\n*/", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], - "x": 1970, + "x": 1890, "y": 1360, "wires": [ [ @@ -7122,7 +7126,7 @@ "links": [ "c79815b5a79ef60a" ], - "x": 1625, + "x": 1545, "y": 1360, "wires": [ [ @@ -7141,7 +7145,7 @@ "links": [ "31e0432a8e9a2f00" ], - "x": 2105, + "x": 2025, "y": 1360, "wires": [] }, @@ -7154,7 +7158,7 @@ "links": [ "8385582377c91fb6" ], - "x": 1605, + "x": 1525, "y": 1060, "wires": [ [ @@ -7175,7 +7179,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 1680, + "x": 1600, "y": 1280, "wires": [ [ @@ -7197,7 +7201,7 @@ "targetType": "full", "statusVal": "", "statusType": "auto", - "x": 1940, + "x": 1860, "y": 1400, "wires": [] }, @@ -7223,7 +7227,7 @@ "topic": "", "payload": "1", "payloadType": "num", - "x": 1650, + "x": 1570, "y": 1220, "wires": [ [ @@ -7245,7 +7249,7 @@ "targetType": "full", "statusVal": "", "statusType": "auto", - "x": 1740, + "x": 1660, "y": 1400, "wires": [] }, @@ -7407,8 +7411,126 @@ "links": [ "e0b4dd6107f95f28" ], - "x": 1195, + "x": 1115, "y": 1380, "wires": [] + }, + { + "id": "f77d63ef3e4ba959", + "type": "inject", + "z": "953e8740401d175c", + "name": "entrada", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "entrada", + "payloadType": "str", + "x": 1190, + "y": 1780, + "wires": [ + [ + "6d62286e9440afda" + ] + ] + }, + { + "id": "6d62286e9440afda", + "type": "change", + "z": "953e8740401d175c", + "name": "tipobascula", + "rules": [ + { + "t": "set", + "p": "tipobascula", + "pt": "flow", + "to": "payload", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 1350, + "y": 1820, + "wires": [ + [] + ] + }, + { + "id": "20564b423ac8d39d", + "type": "inject", + "z": "953e8740401d175c", + "name": "salida", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "salida", + "payloadType": "str", + "x": 1190, + "y": 1840, + "wires": [ + [ + "6d62286e9440afda" + ] + ] + }, + { + "id": "6af84585bb71f167", + "type": "function", + "z": "953e8740401d175c", + "g": "646bdfb9988a1708", + "name": "estado semaforo", + "func": "let semaforo = msg.payload ? \"green\": \"red\"\nmsg = {}\nmsg.payload = {device:\"supervisor\", accion: \"semaforo_status\", status:{totem_id:flow.get(\"totem_id\"), numero_vial:flow.get(\"numero_vial\"), semaforo:semaforo}}\nreturn msg;", + "outputs": 1, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 2230, + "y": 1300, + "wires": [ + [ + "a0f7956de8f33a0b" + ] + ] + }, + { + "id": "a0f7956de8f33a0b", + "type": "link out", + "z": "953e8740401d175c", + "g": "646bdfb9988a1708", + "name": "link out 10", + "mode": "link", + "links": [ + "0df0c819dae1658d" + ], + "x": 2355, + "y": 1300, + "wires": [] } ] \ No newline at end of file