[
    {
        "id": "953e8740401d175c",
        "type": "tab",
        "label": "Totem",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "2af1fbc4.72dce4",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "QR & RFID",
        "style": {
            "stroke": "#92d04f",
            "stroke-opacity": "0.9",
            "label": true,
            "fill": "#e3f3d3",
            "fill-opacity": "0.4",
            "color": "#001f60"
        },
        "nodes": [
            "608a45d.a49bebc",
            "82a82563.9db368",
            "a3edbb144fbdc523",
            "97d5ddae428797d0",
            "38dc1312c5833a43",
            "d570cae191434cbe",
            "a66f75ea959be3c4",
            "03abdad8b6e3e504"
        ],
        "x": 434,
        "y": 159,
        "w": 812,
        "h": 202
    },
    {
        "id": "5682e468.0f80dc",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "Firma del conductor",
        "style": {
            "fill": "#bfdbef",
            "fill-opacity": "0.21",
            "label": true,
            "stroke": "#3f93cf",
            "color": "#001f60"
        },
        "nodes": [
            "16e2d449.1ae45c",
            "3a1a2185.62ac6e",
            "35590e14.a3db22",
            "c79666f4.caad38",
            "713ffffb.3fdbd",
            "bacd5c0e.3c6d8",
            "95c5aff8.f07e9",
            "8356acc61776cae7",
            "91a3eb404d0e121c",
            "6b37f85b1ad9472f"
        ],
        "x": 1294,
        "y": 59,
        "w": 772,
        "h": 302
    },
    {
        "id": "3d6884b5563ad96b",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "EDICION DE MATRICULA",
        "style": {
            "stroke": "#0070c0",
            "fill": "#bfdbef",
            "fill-opacity": "0.25",
            "label": true,
            "color": "#001f60"
        },
        "nodes": [
            "88733868.cf1938",
            "349d0dd447b79ed9",
            "881bd652d27e3f70",
            "0b7fe85c390e446a",
            "0df085dfc5184645",
            "d384dd920c270a70",
            "e8d66667ae2598db",
            "0168d111e7191cbf",
            "fe332bc89bc4f606",
            "da0422341e874eb1"
        ],
        "x": 2194,
        "y": 359,
        "w": 1072,
        "h": 282
    },
    {
        "id": "595fb0f89af85c42",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "CONFIGURACION",
        "style": {
            "stroke": "#9363b7",
            "fill": "#dbcbe7",
            "fill-opacity": "0.22",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "3d273f36272c6de9",
            "fc94a121dfd9ee37",
            "8e5c7bf9dc501a93",
            "7c5a1da447b15ffd",
            "45fb86ba06a5fcce",
            "ca75ac762b4f0884",
            "b13c06048c797a97",
            "da672c95637f91bd",
            "dcaffa245475dd85",
            "2b018bba18c09a54",
            "d930458472ce86cf",
            "8360f8ecbe5bfff4",
            "cbcfabf68c8a649c"
        ],
        "x": 1074,
        "y": 379,
        "w": 992,
        "h": 229.5
    },
    {
        "id": "4126e77db69df2f9",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "URL - Websocket",
        "style": {
            "stroke": "#92d04f",
            "fill": "#92d04f",
            "fill-opacity": "0.08",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "37d1d77b10692029",
            "86e20fa9c20ade1f",
            "7433bbe3775ba5b7",
            "a992f39766e3c694",
            "8e4d425eebc745f0"
        ],
        "x": 2194,
        "y": 699,
        "w": 1012,
        "h": 142
    },
    {
        "id": "710eba632fa2e38d",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "GESTION DE CAMARAS",
        "style": {
            "stroke": "#000000",
            "fill": "#bfdbef",
            "fill-opacity": "0.24",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "6443f177eec330e4",
            "61f2c799bf9b937c"
        ],
        "x": 3362,
        "y": 933,
        "w": 1456,
        "h": 900
    },
    {
        "id": "22864a680b3389c9",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "MENU DE SELECCION DE MATERIALES",
        "style": {
            "stroke": "#0070c0",
            "fill": "#bfdbef",
            "fill-opacity": "0.35",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "e771c9458e352071",
            "7e46c4508c5c2f47",
            "7881be0e538c924f",
            "31dd60651ad6ac63",
            "95c29990fdb60d16",
            "813259300549debc",
            "19709ef01b428164",
            "18baf4cac7c5059f",
            "eba5e6108a6457db",
            "dd95d08b4e8afe26",
            "c08676e7c1b4e6b4",
            "d6ce579c7544f360",
            "fe5e2d99339ebc08",
            "8ee5165edf4c80fe",
            "f2352fbfa81f1fbd",
            "22fabd2601b01d02"
        ],
        "x": 2184,
        "y": -1,
        "w": 1772,
        "h": 302
    },
    {
        "id": "bad04a9f657a59e6",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "DISPLAY PRINCIPAL DEL TOTEM",
        "style": {
            "stroke": "#0070c0",
            "fill": "#bfdbef",
            "fill-opacity": "0.28",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "641f679340b3e774",
            "338d3caabc377186",
            "d4f51c3e79849570",
            "2f88b8abeda0f377",
            "6fcda8c89afa5620",
            "b010f913d811a8e4",
            "b06ca493e3ecfb9b",
            "774eadbf0b93e9e5",
            "feceb805a5f051dd",
            "55be72af5b5206e5",
            "8ee7b0e07937fde7",
            "be06f3844edc5a69",
            "9d3fe180d1427902",
            "5d188f7a3a6eaa62",
            "b5e1afed9e1c8376",
            "750c950486ec6cb1",
            "43ae11415631042a",
            "184d7d929902a0d4",
            "f33bb7424ebd286a",
            "f825c6ab70a8cffc"
        ],
        "x": 2474,
        "y": 979,
        "w": 832,
        "h": 342
    },
    {
        "id": "646bdfb9988a1708",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "NUCLEO DEL TOTEM",
        "style": {
            "stroke": "#ff0000",
            "fill": "#ffbfbf",
            "fill-opacity": "0.27",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "60ccc5ce.32a35c",
            "d27e3fef.df3b1",
            "3bd15195.2069fe",
            "197dfc68.458e54",
            "48eda297.9cdedc",
            "e0b4dd6107f95f28",
            "422897281c1c5ef6",
            "610afee2981890f5",
            "bf6a26e8fa632175",
            "6c4fcf9dad031bf8",
            "b7b5cf32f7eea205",
            "f4b1c07e498c11be",
            "b890981ca603e6e1",
            "6530f7fdb887dbf1",
            "07212ec8d039ced0",
            "3f7b861dd5cd6b93",
            "bb6e7b4ac0e9a9f0",
            "db3e9975c8f562b6",
            "5263b792c0037321",
            "fab29e0f6601e531",
            "a862be7fbfd8827a",
            "9328043d4e9512a9",
            "06832f8daa7c54f9",
            "423023a153680e64",
            "6f9cc9fc71e8ca71",
            "cf7b0c00631682b2",
            "b2149025f64c76c7",
            "b6a2b0d90858876e",
            "7cb744ca5e4180bf",
            "0fc258061e59d527",
            "1b1ee13b380e32b6",
            "31e0432a8e9a2f00",
            "f3676f905e4ade55",
            "ff3913749bd9e2f3",
            "6af84585bb71f167",
            "a0f7956de8f33a0b"
        ],
        "x": 54,
        "y": 893,
        "w": 2342,
        "h": 814
    },
    {
        "id": "05cb4985c3c68ae8",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "SIMULACION DE CAMARA Y SENSORES",
        "style": {
            "stroke": "#ff0000",
            "fill-opacity": "0.27",
            "label": true,
            "color": "#000000",
            "fill": "#ffefbf"
        },
        "nodes": [
            "2f1a8bd6dd9ea0d5",
            "0e4b3c888740bdc2",
            "f12149b9937d1f9c",
            "864ac0651127081b",
            "6e7d311b8384ddbb",
            "654f0bef9ed49b91",
            "25d47d91059e756a",
            "9bd5c49ea39343ab",
            "6b8e8d62fb919f6c",
            "1ee7d9733d5bdccd",
            "9db92332012fa977",
            "120a33aec5cdad35",
            "bc10f90fd35fca2a",
            "2b68b52db6cc1683",
            "0c96e404f0aeec9c",
            "715237a531d3d520",
            "46476ff7f3b16d50",
            "088db8eaaaf5641a",
            "bcb4dca5a8f9ea87",
            "d28debfbf333767f",
            "e9ad5c9298c1658d",
            "86102659d7bd05ca",
            "fefe878035fbe1d8",
            "afba789e9a047452",
            "de4c24109d3a3cec",
            "e82fc95572128625",
            "5eada7a4d497bdf2",
            "1032df4341902ad3",
            "8bfc0e03b763b6b1",
            "eb6a49472973163d",
            "41b4309de63a6bba"
        ],
        "x": 2534,
        "y": 1439,
        "w": 552,
        "h": 1202
    },
    {
        "id": "2245c4484f098470",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "SALIDA WEBSOCKET ",
        "style": {
            "stroke": "#6f2fa0",
            "fill": "#dbcbe7",
            "fill-opacity": "0.23",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "2f66b472df7dae12",
            "fac0534420b62514",
            "0df0c819dae1658d"
        ],
        "x": 1684,
        "y": 739,
        "w": 382,
        "h": 142
    },
    {
        "id": "e3bc37824e09b774",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "CONFIGURACION CON UIBUILDER",
        "style": {
            "stroke": "#0070c0",
            "fill-opacity": "0.75",
            "label": true,
            "fill": "#bfdbef",
            "color": "#000000"
        },
        "nodes": [
            "2ec53aa1784087c1",
            "b8fdc502b1bf35b7",
            "be843dd4381d45d5",
            "ecaff4d18050a7c5",
            "cd806a312b8e7e6e",
            "88b9b4d1c5aa7359",
            "7058b9e632aa584b",
            "207488e79f57a0eb",
            "0dee226932d66a29",
            "7ae4043cc5314f0b",
            "882ee24d1489b06c",
            "9667a055f9c967a7",
            "0c8c862285db249e",
            "72222dcbbfea7534",
            "772b17b6e7954d5f",
            "7a4c757e6f6ddfcd",
            "6fca6204952f0b67",
            "f4c61087c128e20f",
            "6d39924d1d2081dd",
            "8ccf22739ca72997",
            "7fd2f6e6fe3f37ea",
            "ee22e8c1327a7b65",
            "b90652f4011dad39"
        ],
        "x": 3364,
        "y": 1999,
        "w": 1162,
        "h": 482
    },
    {
        "id": "427191a7a5984434",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "DESARROLLO / PRUEBAS DE NUEVA PANTALLA TOTEM",
        "style": {
            "fill": "#bfdbef",
            "fill-opacity": "0.75",
            "label": true,
            "color": "#000000",
            "stroke": "#0070c0"
        },
        "nodes": [
            "1826a83d58a7539b",
            "51e2882d2082cd6c",
            "153ff925612f0a72",
            "4be318029a87b632",
            "42a05ba042772cf0",
            "3a6c2ab4113cbfc9",
            "656a6ccd2e2d8874",
            "9334d6e746bacf1d",
            "a1f46ee9a5bf3bd0"
        ],
        "x": 3374,
        "y": 2539,
        "w": 762,
        "h": 262
    },
    {
        "id": "c0824ad89a3b525c",
        "type": "group",
        "z": "953e8740401d175c",
        "name": "PRUEBA DE ENVIO Y RECEPCION MULTIPLES MENSAJES SIMULTANEOS AL SERVIDOR",
        "style": {
            "stroke": "#0070c0",
            "fill": "#addb7b",
            "fill-opacity": "0.37",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "42499939644932e1",
            "e7cb71b1e09afda6",
            "d117899cc8faa6d0",
            "27ff2fd5838987df",
            "5bf3c0851a93230d",
            "cb449de1338a8b6b",
            "545d688854bdc270",
            "6bdb05cfc53c7c6f",
            "c405dbca2e94854c",
            "c5c336e381de6e5d",
            "35c3f5c0d362ff4d",
            "10b95a9046bf57f7",
            "1b2a16010cfceb38",
            "84f8ce2853a75080"
        ],
        "x": 154,
        "y": 1819,
        "w": 772,
        "h": 442
    },
    {
        "id": "6443f177eec330e4",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "710eba632fa2e38d",
        "name": "SENSOR DE DISTANCIA / DETECTOR DE VEHICULO",
        "style": {
            "stroke": "#92d04f",
            "fill": "#e3f3d3",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "8b5516b1119c997f",
            "608b3302d291d648",
            "0bc82cd2485c7679",
            "9b3f8f7040a794d8",
            "d65005ac817d14e0",
            "20fe9f7863d4d4ec",
            "f730c0726e257f7f",
            "76877357b0cb15d2",
            "fb6b2f1f9986070e",
            "ea8197b45f511dd7",
            "55d3cd43f7070262",
            "5235b52ecb18fecd",
            "67cd75822b6c9afd",
            "93ee99e8761e6fbc",
            "06adfd5485a893aa"
        ],
        "x": 3394,
        "y": 959,
        "w": 1152,
        "h": 262
    },
    {
        "id": "61f2c799bf9b937c",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "710eba632fa2e38d",
        "name": "ITSAPI - WEB SERVER Y ANALISIS",
        "style": {
            "stroke": "#92d04f",
            "fill": "#c8e7a7",
            "fill-opacity": "0.24",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "f1e1c7ed9ae455b4",
            "a8776f6f1f238fcb",
            "18cab601c9919c4d",
            "14d1a0196daddc64",
            "729b5c7d2d5da9a5",
            "0e2b148022a7af13",
            "ade2db3a9cf2dab1",
            "38a39c85f67ce6f1"
        ],
        "x": 3388,
        "y": 1259,
        "w": 1404,
        "h": 548
    },
    {
        "id": "f4b1c07e498c11be",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "PANTALLA ON / OFF",
        "style": {
            "stroke": "#ff0000",
            "fill-opacity": "0.16",
            "label": true,
            "color": "#000000",
            "fill": "#a4a4a4"
        },
        "nodes": [
            "780c6c61.a0cb04",
            "49500a91.3971e4",
            "97ec61bc.7e2aa",
            "afb26e9306481251",
            "2c73d02c99e64b37",
            "cc25836d7a7314ad"
        ],
        "x": 714,
        "y": 1459,
        "w": 432,
        "h": 222
    },
    {
        "id": "9328043d4e9512a9",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "CONDICIONES  && BASE DE DATOS",
        "style": {
            "stroke": "#0070c0",
            "fill": "#bfdbef",
            "fill-opacity": "0.29",
            "label": true,
            "label-position": "s",
            "color": "#000000"
        },
        "nodes": [
            "3e708dc4202384a1",
            "95f3d3aa2f768a9e",
            "2f57175a6c859157",
            "5420d37f8224c191",
            "944c9f4b068e0946",
            "83a963e6bc339f0e",
            "4a10854d5d2302af",
            "76471be6630c1c3c",
            "0ebfa0099db1ed19",
            "658bbf0ae0c6d8de",
            "16994333f8deedd9",
            "5a4bb45aef7a1433",
            "f75c248d56207759",
            "efa47b4b532c3c95",
            "f5343bf87806ece4",
            "4ab4a211f9ce33d9",
            "41b9eef871e59dd2",
            "b31db528b0be605a",
            "185e340354acf892",
            "9d8e071a6d3642c0",
            "77d23eabe6e7d642",
            "c79815b5a79ef60a",
            "077ea8c7ce8cec99"
        ],
        "x": 914,
        "y": 919,
        "w": 572,
        "h": 510
    },
    {
        "id": "0fc258061e59d527",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "Audio",
        "style": {
            "stroke": "#9363b7",
            "fill": "#dbcbe7",
            "fill-opacity": "0.37",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "c0c7eac1a54f2ea0",
            "23706ca448e439ea",
            "0c7a53155c4927ab",
            "ce9b745d5013070e",
            "7a5b8831173f85b2",
            "3683a851f79ee374",
            "f47c019e5baa0206",
            "20ba13f6d67ea6ac",
            "ad5beee5d0151013",
            "a395ed21d7049d5d",
            "6ebdceb73c4cfbf0"
        ],
        "x": 1244,
        "y": 1459,
        "w": 942,
        "h": 222
    },
    {
        "id": "1b1ee13b380e32b6",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "CAMARA CONTEXTO",
        "style": {
            "stroke": "#92d04f",
            "fill": "#c8e7a7",
            "fill-opacity": "0.29",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "72071eee550615d7",
            "f5b38e6e811de790",
            "9b0761cc102bc028",
            "8385582377c91fb6",
            "a0aa0f96fe081912",
            "730bbe3df0ab62db"
        ],
        "x": 1504,
        "y": 1319,
        "w": 562,
        "h": 122
    },
    {
        "id": "0e2b148022a7af13",
        "type": "group",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "RECEPCION DE MATRICULA",
        "style": {
            "stroke": "#ff0000",
            "fill-opacity": "0.15",
            "label": true,
            "color": "#000000",
            "label-position": "n"
        },
        "nodes": [
            "9b16b5291a12f8d7",
            "d3223fe2481ad802",
            "f04db2f5954d97a7",
            "76b2fa93b3b5d48a",
            "5b23fa622cf4d01b",
            "5c93493bec44bb46",
            "4a51d53512daec64",
            "e49672ad6c84b341",
            "ead36b6ad9ef17b8",
            "ee2d8058d3e40c85",
            "e978bcd8c43a62b1",
            "92309a9f0b0f2c29",
            "dba345c891c1fb51",
            "fe75553cfb61d760",
            "108fa8de0b9851c3",
            "34b97be83a87f482",
            "fc2c3f9d0fd1f0f7",
            "41cb4ca9c6c75818",
            "85fe3844a73d42e6",
            "4e1d349cc2c30e66",
            "ae0cbe34a4030d33",
            "1c98099a9069bb98",
            "0dbd268561a07c9e",
            "b9cf7510f73e95c3",
            "67d5b1beafb80c7d",
            "4120796963ac5cf7",
            "cbea4a6fab6a2384",
            "0e5b5bc676912875",
            "46dbff31c3270ba1"
        ],
        "x": 3414,
        "y": 1451.5,
        "w": 1352,
        "h": 329.5
    },
    {
        "id": "3966bb218232f6b3",
        "type": "mqtt-broker",
        "name": "",
        "broker": "trypton02.vservers.es",
        "port": 1883,
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": 4,
        "keepalive": 60,
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    },
    {
        "id": "4662144382ad3833",
        "type": "serial-port",
        "name": "",
        "serialport": "/dev/ttyAMA3",
        "serialbaud": "9600",
        "databits": "8",
        "parity": "none",
        "stopbits": "1",
        "waitfor": "",
        "dtr": "none",
        "rts": "none",
        "cts": "none",
        "dsr": "none",
        "newline": "100",
        "bin": "false",
        "out": "interbyte",
        "addchar": "",
        "responsetimeout": "10000"
    },
    {
        "id": "dc90e121aa15a212",
        "type": "modbus-client",
        "name": "",
        "clienttype": "serial",
        "bufferCommands": true,
        "stateLogEnabled": false,
        "queueLogEnabled": false,
        "failureLogEnabled": true,
        "tcpHost": "127.0.0.1",
        "tcpPort": "502",
        "tcpType": "DEFAULT",
        "serialPort": "/dev/ttyAMA5",
        "serialType": "RTU-BUFFERD",
        "serialBaudrate": "9600",
        "serialDatabits": "8",
        "serialStopbits": "1",
        "serialParity": "none",
        "serialConnectionDelay": "100",
        "serialAsciiResponseStartDelimiter": "0x3A",
        "unit_id": 1,
        "commandDelay": 1,
        "clientTimeout": 1000,
        "reconnectOnTimeout": true,
        "reconnectTimeout": 2000,
        "parallelUnitIdsAllowed": true,
        "showErrors": false,
        "showWarnings": true,
        "showLogs": true
    },
    {
        "id": "be0e1690e4e13e0d",
        "type": "websocket-client",
        "path": "ws://servidor.com:1880",
        "tls": "",
        "wholemsg": "false",
        "hb": "20",
        "subprotocol": "",
        "headers": []
    },
    {
        "id": "eb652223.5293e",
        "type": "websocket-listener",
        "path": "/ws/test",
        "wholemsg": "false"
    },
    {
        "id": "f2df94e43357a7bb",
        "type": "sqlitedb",
        "db": "/home/trypton/sqlite/totemdb",
        "mode": "RWC"
    },
    {
        "id": "91f675979e1f226e",
        "type": "ui_tab",
        "name": "Firma",
        "icon": "Firma",
        "order": 2,
        "disabled": false,
        "hidden": true
    },
    {
        "id": "e01b6f.a9d07c9",
        "type": "ui_tab",
        "name": "Matricula",
        "icon": "dashboard",
        "order": 3,
        "disabled": false,
        "hidden": true
    },
    {
        "id": "f1a28aea5e222dd9",
        "type": "ui_tab",
        "name": "Lista",
        "icon": "dashboard",
        "order": 4,
        "disabled": false,
        "hidden": true
    },
    {
        "id": "0a4e8b499f60b5f3",
        "type": "ui_tab",
        "name": "Totem",
        "icon": "dashboard",
        "order": 1,
        "disabled": false,
        "hidden": true
    },
    {
        "id": "80938936064ade5a",
        "type": "ui_group",
        "name": "Default",
        "tab": "91f675979e1f226e",
        "order": 1,
        "disp": false,
        "width": "14",
        "collapse": false,
        "className": ""
    },
    {
        "id": "f0166794.33066",
        "type": "ui_group",
        "name": "Device management",
        "tab": "e01b6f.a9d07c9",
        "order": 2,
        "disp": false,
        "width": "14",
        "collapse": false,
        "className": ""
    },
    {
        "id": "5b5c29c99e5eb902",
        "type": "ui_group",
        "name": "Lista",
        "tab": "f1a28aea5e222dd9",
        "order": 2,
        "disp": false,
        "width": "14",
        "collapse": false,
        "className": ""
    },
    {
        "id": "ac875e596615614c",
        "type": "ui_group",
        "name": "Peso",
        "tab": "0a4e8b499f60b5f3",
        "order": 1,
        "disp": false,
        "width": "10",
        "collapse": false,
        "className": ""
    },
    {
        "id": "9ef57be94bfc3d3f",
        "type": "ui_group",
        "name": "semaforo",
        "tab": "0a4e8b499f60b5f3",
        "order": 2,
        "disp": false,
        "width": "4",
        "collapse": false,
        "className": ""
    },
    {
        "id": "15b0f8318476dc16",
        "type": "ui_group",
        "name": "matricula",
        "tab": "0a4e8b499f60b5f3",
        "order": 5,
        "disp": false,
        "width": "4",
        "collapse": false,
        "className": ""
    },
    {
        "id": "eabd81899686a562",
        "type": "ui_group",
        "name": "mensaje",
        "tab": "0a4e8b499f60b5f3",
        "order": 3,
        "disp": false,
        "width": "10",
        "collapse": false,
        "className": ""
    },
    {
        "id": "10b245a5a10f6814",
        "type": "ui_base",
        "theme": {
            "name": "theme-light",
            "lightTheme": {
                "default": "#0094CE",
                "baseColor": "#0094CE",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": false
            },
            "darkTheme": {
                "default": "#097479",
                "baseColor": "#097479",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": false
            },
            "customTheme": {
                "name": "Untitled Theme 1",
                "default": "#4B7930",
                "baseColor": "#4B7930",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
            },
            "themeState": {
                "base-color": {
                    "default": "#0094CE",
                    "value": "#0094CE",
                    "edited": false
                },
                "page-titlebar-backgroundColor": {
                    "value": "#0094CE",
                    "edited": false
                },
                "page-backgroundColor": {
                    "value": "#fafafa",
                    "edited": false
                },
                "page-sidebar-backgroundColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "group-textColor": {
                    "value": "#1bbfff",
                    "edited": false
                },
                "group-borderColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "group-backgroundColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "widget-textColor": {
                    "value": "#111111",
                    "edited": false
                },
                "widget-backgroundColor": {
                    "value": "#0094ce",
                    "edited": false
                },
                "widget-borderColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "base-font": {
                    "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
                }
            },
            "angularTheme": {
                "primary": "indigo",
                "accents": "blue",
                "warn": "red",
                "background": "grey",
                "palette": "light"
            }
        },
        "site": {
            "name": "Node-RED Dashboard",
            "hideToolbar": "false",
            "allowSwipe": "false",
            "lockMenu": "false",
            "allowTempTheme": "true",
            "dateFormat": "DD/MM/YYYY",
            "sizes": {
                "sx": 48,
                "sy": 48,
                "gx": 6,
                "gy": 6,
                "cx": 6,
                "cy": 6,
                "px": 0,
                "py": 0
            }
        }
    },
    {
        "id": "9e9aa86cd45d70fa",
        "type": "serial-port",
        "name": "",
        "serialport": "dev/ttyAMA3",
        "serialbaud": "9600",
        "databits": 8,
        "parity": "none",
        "stopbits": 1,
        "waitfor": "",
        "dtr": "none",
        "rts": "none",
        "cts": "none",
        "dsr": "none",
        "newline": "\\n",
        "bin": "false",
        "out": "char",
        "addchar": "",
        "responsetimeout": 10000
    },
    {
        "id": "76020de5d49169e5",
        "type": "comment",
        "z": "953e8740401d175c",
        "name": "Version A1.85 de totem autonono",
        "info": "Version A1.85\nSe modificaron las condiciones para ser totem master. Realmente el resultado se obtiene igual que anteriormente, es decir al recibir el evento de detector remoto,\nsi estamos en configuracion de dos totems y en el detector local no presenta deteccion de objeto, entonces marcamos que este totem es el totem master, \npero si al cabo de \"n\" segundos (esta es la modificacion) detectamos que no hay peso > pmin en la bascula entonces eliminamos la indicacion de totem master \nya que esto ha sido una falsa deteccion. Los \"n\" segundos estan en el parametro \"confirm_master_time\" y puede ser configurado en la seccion de tiempos, en el archivo de configuracion\naccediendo a \"http://tote_IP:1880/totem\".\nSe elimino la inicializacion de la variable peso en el nodo de configuracion \"config edit\", ya que al salir del menu de configuracion y aplicar dicha configuracion\nocasionaba modificacion del peso y este no se restablecia hasta que hubiera una variacion en el peso, ocasionando asi falsos estados\nSe agrego la recepcion de un comando por websocket para gestionar el semaforo directamente. Para ello se gestiona un flag en la recepcion de los \nmensajes websockets para cuando el peso < pmin no tocar el estado del semaforo si el flag ha sido activado. Este flag se reinicia al terminar la pesada.\nSe agrego la recepcion de un comando por websocket para abrir la barrera directamente, indepemdiente si esta o no habilitada en configuracion\nSe introdujo el tiempo de espera para solicitar snaphot a la camara como un parametro configurable \"time_to_snapshot\".\nLa version A1.84 tiene un flag para evitar que cuando sacamos el mensaje de \"matricula erronea\" o de \"vuelva a intentar...\", relacionado con la edicion de matricula,\nsi llega un evento (ej. peso nuevo ) se vuelva a producir la nota y se vuelva a mostrar en pantalla impidiendo utilizar el editor de matricula. \nEste flag nuevo evita que se repitan las acciones de sacar nuevas notas si estas ya se han sacado en pantalla.\nHemos detectado que la inicializacion de ciertos parametros no debe ser realizada cada vez que el peso < pmin ya que puede ser que en un momento un parametro se active al ser detectado el vehiculo\ny luego se reinicia (equivocadamente) en cuanto la bascula empieza a subir de peso pero sin superar el peso minimo (en ese espacio de tiempo ocurren varios eventos de peso nuevo). \nEso nos puede dar un resultado falso en el proceso. Tal es el caso del flag \"totem_master\" el cual se activa cuando un detector es activado por un vehiculo pero luego ese flag se desactiva \ncuando la bascula empieza a subir de peso (antes de sobrepasar el pmin). Para evitar estos posibles errores hay ciertas variables (mayormente flags) que se deben inicializar solamente cuando el vehiculo sale \nde la bascula, es decir al momento en que por primera vez el peso < pmin una vez finalizada la pesada. De esta manera aseguramos que no habra falsas inicializaciones. \nAlgunas variables cuya inicializacion ha sido movida a este sitio son:  \"totem_master\", \"matricula\", \"nombre_imagen\", \"badplate\". Este sitio de inicializacion se encuentra en el nodo \"peso\"\njustamente despues de escribir el movimiento de pesada en base de datos.\n\nLa version A1.83 tiene un nuevo flag indicador de si este Totem esta trabajando como Master o no. Que sea Master quiere decir que el Totem \nes el que envia el movimiento registrado de la pesada a la base de datos y al servidor y es quien señala cuando el peso es estable \n(mediante sensor local, boton de salida, menu, o tarjeta), ver nodo \"peso\" en la linea 14 y el nodo \"estable\" en la linea 9 (el if que es largo).\nLa decision si es Master o no, se hace primero en el nodo \"peso\" al iniciar las variables donde \"totem_master se inicializa con false pero si \nel numero de totem = 1 entonces la variable se fija en true (siempre sera master). En el caso de dos totems, partimos de que no hay Master \nhasta que algun detector se active, en ese momento el Totem opuesto a ese sensor sera marcado como Master. Esto se realiza en el nodo \"detector rx\"\nlineas 5 y 6 donde en caso de recibir, en este nodo, mensaje de activacion del sensor remoto evaluamos el estado del sensor local y si este se encuentra desactivado\nentonces este Totem sera el Master para esta pesada. Quiere decir que el vehiculo entro por el otro Totem.\nSe agrego un flag (\"vehiculo_en_posicion\") cuando se pulsa el boton de acceso al menu para asi tomar en ese momento el peso estable en lugar de tommarlo al final de menu como haciamos anteriormente.\nPara evitar el borrado del boton de acceso al menu (completar informacion) en la pantalla, debido a la llegada de un nuevo peso o cualquier otro evento, se cambio de sitio la inicializacion\ndel flag \"startmenu_flag\" (el cual evitaba volver a escribir el boton. Este flag estaba en el nodo \"envio de mensajes\") ahora se permite volver a escribir el boton, de esta manera no se borra y \nel flag se desactiva en tres sitios: Cuando se recibe un QR valido o cuando se recibe una tarjeta RF valida o cuando se pulsa el mismo boton de acceso al menu.\n\nLa version A1.82 incorpora la posibilidad de enviar los datos de movimiento a traves de MQTT. Para ello el usuario debe configurar\nmediante el menu de configuracion el parametro \"movimientosxmqtt\" en true y el parametro \"movimientosxapi\" en false. En caso de querer enviar \nmediante api, la configuracion debe ser al contrario.\n\nEsta version A1.81 incorpora una nueva funcionalidad en la configuracion. Esta funcionalidad permite definir tipos de configuracion\n(TIPO 1, TIPO 2, ...) donde en cada tipo el usuario puede definir un conjunto de parametros con sus valores y luego definiendo el parametro\n\"Tipo_Configuracion\" (en el menu de parametros GENERAL) con el numero del TIPO (ej.\"Tipo_Configuracion\": 2 ) el sistema aplicara \nlos parametros definidos en ese TIPO independientemente de los valores que se definan en el menu de configuracion (GENERAL, CAMARA, COEXION;...). \nSi por el contrario el parametro \"Tipo_Configuracion\" : 0, entonces el sistema no tomara en cuenta ningun TIPO predefinido y colocara los valores \ndefinidos en el menu de configuracion.\nEn el analisis de matricula / tarjeta / QR hemos definido de momento tres tipos de operacion\nque llamamos Tipo 1, 2 y 3 donde cada tipo contiene diferentes casos de uso numerados y una configuracion de parametros particular.\nPor ejemplo el TIPO 1 tiene la siguiente configuracion de parametros asociados\nidentificacion_esp = true; menu_activo = true; editor_matricula = true; matricula_obligatoria = false; ID_obligatorio = false;\nLos CASOS estan definidos en una hoja excel.\nTipo 1 = CASO 1, CASO 2, CASO 3, CASO 4, CASO 5, CASO 6.\nTipo 2 = CASO 1, CASO 2, CASO 3.\nTipo 3 = CASO 1, CASO 3.\nLos tipos se seleccionan por configuracion y los casos son las diferentes situaciones que esa configuracion es capaz de gestionar.\n\nEsta version A1.7 incorpora la gestion de BD para revisar si las matriculas que entran en bascula estan asociadas a algun contrato de manera \nque en caso positivo se extrae una lista con los niveles de menu (en este momento 2) para presentar en pantalla.\nEn esta version tambien esta permitido que el usuario se identifique mediante una tarjeta o un QR siempre y cuando estos elementos\nesten identificados o asociados en BD a algun contrato.\nAdicionalmente esta version recibe por mqtt el contenido de las tablas de la base de datos las cuales permiten extraer los datos del contrato \ncorrespondientes a cada matricula.\nEn esta version se ha deshabilitado la configuracion hecha via angular mediante los nodos template del dashboard\n\nEsta version A1.6 del Totem Autonomo incorpora el uibuilder como servidor web, el cual de momento es utilizado solamente para atender\na la gestion de los parametros de configuracion, pruebas de semaforo, camara y dispone de botones para reinicio y apagado. Adicionalmente esta \npágina en su pantalla principal dispone del monitoreo de los principales parametros del Totem en tiempo real.\n\nEsta version A1.5 del Totem Autonomo ademas de funcionar en autonomo tambien tiene la posibilidad de funcionar en modo supervisor\nes decir en la misma forma que los Totem anteriores. Esta modalidad se puede seleccionar en el archivo de configuracion cambiando\nel parametro \"supervisor\" a true. Si lo dejamos en false, el sistema funciona en modo autonomo. \nLa barra azul del titulo que en ocasiones aparecia en la parte superior de la pantalla, sin posibilidad de eliminarla por los medios\nconvencionales (cambiando en la pestaña SITE del Dashboard la opcion \"Show the Title bar\" a \"Hide the title bar\"), ha podido esconderse \ndespues de poner una clase especifica en el template de \"clases\" la cual actua directamente sobre la accion de display de esta barra. \n(md-toolbar.md-default-theme, md-toolbar)\n\nLa version A1.4 incluye el registro en una API externa de todos los movimientos que se originen en la bascula. Este registro ocurre al \nabandonar el camion la bascula. Al enviar el registro hacia el API remoto, se revisa si la operacion estuvo correcta en cuyo caso \nse cambia el campo \"sincro\" del record a \"1\" en BD, indicando qu este registro ha sido sincronizado. \nAdicionalmente cada dia a una hora determinada se eliminan de la BD todos los registros que hayan sido sincronizados y que sean mas viejos\nque una determinada cantidad de dias (configurable en el archivo de configuracion)\nEsta version tambien provee una api para que de forma remota se puedan consultar los records que quedan sin sincronizar y/0 los sincronizados\nhttp://url:1880/api/movimientos?tipo=no_sincro  o  http://url:1880/api/movimientos?tipo=sincro. Esto genera un json con todos los records involucrados.\nEn caso de no poder sincronizar un movimiento a la salida del vehiculo en la bascula. Este queda registrado como no sincronizado. Cada vez que se registra un movimiento\nal salir el vehiculo de la bascula, se revisa en BD si existen records que no han sido sincronizados. En caso de existir esos records, se construye\nun array con dichos records ordenado por los records mas viejos y se comienza a enviar cada record al servidor. Cada vez que se detecte que un record \nha sido enviado y recibido satisfactoriamente, este record se marca como sincronizado en la BD local, en caso contrario se mantiene como record no sincronizado.\n\nLa Version A1.3 incluye la edicion de la matricula la cual se activa automaticamente en caso que haya sido habilitada por configuracion\nSi no esta habilitada entonces es necesario una identificacion especial (QR / RFID) para poder cerrar el movimiento (esto se habilita tambien\npor configuracion) 0 si no el conductor tendra un mensaje de poner retroceso y volver a entrar en la bascula.\nAdicionalmente tambien tiene la solicitud automatica de Firma por pantalla en caso que haya sido habilitada por configuracion.\n\nLa Version A1.2 posee la gestion de configuracion mediante el uso de un smartphone a traves de una conexion WiFi local. Para ello se ha \nhabilitado un Punto de Acceso (AP) en el Totem que permite la conexion de clientes y la manipulacion de un menu especializado en la configuracion\nde los parametros que influyen en la operacion del Totem.\n\nLa version A1.1 incluye la gestion de menus para que el usuario pueda seleccionar el origen y la naturaleza de los residuos\nque transporta en el vehiculo. Esta lista con los menu tiene que se introducida en una tabla de datos mediante una api, la cual aun no ha sidodesarrollada. \nLo que esta operativo en el Totem es la gestion del menu.\n\n\n",
        "x": 970,
        "y": 800,
        "wires": []
    },
    {
        "id": "4612b9d8c2ef56df",
        "type": "inject",
        "z": "953e8740401d175c",
        "name": "",
        "props": [
            {
                "p": "peso",
                "v": "17450",
                "vt": "num"
            },
            {
                "p": "semaforo_color",
                "v": "green",
                "vt": "str"
            },
            {
                "p": "matricula",
                "v": "6421FPG",
                "vt": "str"
            },
            {
                "p": "peso_estable_flag",
                "v": "true",
                "vt": "bool"
            },
            {
                "p": "vial",
                "v": "1",
                "vt": "num"
            },
            {
                "p": "totem_id",
                "v": "Totem-2",
                "vt": "str"
            },
            {
                "p": "iplan",
                "v": "192.168.0.29",
                "vt": "str"
            },
            {
                "p": "ipsupervisor",
                "v": "192.168.0.16",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 3550,
        "y": 1960,
        "wires": [
            [
                "2ec53aa1784087c1"
            ]
        ]
    },
    {
        "id": "5aea51ec98be1814",
        "type": "inject",
        "z": "953e8740401d175c",
        "name": "",
        "props": [
            {
                "p": "semaforo_color",
                "v": "red",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 3550,
        "y": 1920,
        "wires": [
            [
                "2ec53aa1784087c1"
            ]
        ]
    },
    {
        "id": "f2736d54833e6a3c",
        "type": "debug",
        "z": "953e8740401d175c",
        "name": "debug 12",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 2380,
        "y": 940,
        "wires": []
    },
    {
        "id": "8db77186d4b23dbc",
        "type": "mqtt out",
        "z": "953e8740401d175c",
        "d": true,
        "name": "totem",
        "topic": "totemautonomo/",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "3966bb218232f6b3",
        "x": 1930,
        "y": 700,
        "wires": []
    },
    {
        "id": "b4529cd7850cf166",
        "type": "debug",
        "z": "953e8740401d175c",
        "name": "debug 22",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4060,
        "y": 260,
        "wires": []
    },
    {
        "id": "608a45d.a49bebc",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "envio QR/RFID",
        "func": "\nlet mensaje1 = \"\";let mensaje2 = \"\";let reverseid = \"\";//cada mensaje es enviado y usado como una linea en html\nnode.status({fill:\"green\",shape:\"dot\",text:msg.payload});\nif (msg.id_topic == \"QR\") { //check si el mensaje es del lector de QR\n    //if (flow.get(\"peso_estable_flag\") && flow.get(\"modo_automatico\")) {\n    if (!flow.get(\"peso_bajo_flag\") ) {\n        mensaje1 = \"Lectura del QR:\";\n        mensaje2 = msg.payload;\n        flow.set(\"esperando_id\", false) // No permitimos envio de otro ID en esta pesada\n        flow.set(\"last_QR\", msg.payload)\n        msg.payload = {};\n        msg.id_topic = \"QR\"\n        msg.payload.QR = mensaje2;\n        msg.payload.QR_ok_flag = true;\n        flow.set(\"QR_ok_flag\", true); // marcamos que hemos colocado un codigo QR en last_QR\n        flow.set(\"RFID_ok_flag\", false);\n        flow.set(\"ID_enviado\", false) // lo pongo temporalmente para hacer pruebas. Normalmente solo se permite enviar un QR\n    }\n    else {\n        mensaje1 = \"Lectura de QR para BD\";\n        mensaje2 = msg.payload;\n        //msg.hex = Number(mensaje2).toString(16);\n        flow.set(\"last_QR\", msg.payload)\n        msg.payload = {};\n        msg.id_topic = \"QR\"\n        msg.payload.QR = mensaje2;\n        flow.set(\"QR_ok_flag\", true);\n        msg.payload.totem_id = flow.get(\"totem_id\");\n        msg.payload.numero_vial = flow.get(\"numero_vial\");\n        msg.payload.tipo = \"QR_BD\";\n        flow.set(\"ID_enviado\", false)\n        \n    }\n    \n}\nelse{ //mensaje viene del lector de RFID\n    reverseid = msg.payload.match(/[a-fA-F0-9]{2}/g).reverse().join('')\n    msg.reverseidH = reverseid\n    msg.reverseidD = parseInt(reverseid, 16)\n    if (flow.get(\"rfid_invertido\")) {\n        mensaje2 = msg.reverseidD\n    }\n    else{\n        mensaje2 = parseInt(msg.payload, 16);\n    }\n    \n    if (!flow.get(\"peso_bajo_flag\")) {\n        mensaje1 = \"Lectura del RFID:\";\n        flow.set(\"last_RFID\", mensaje2);\n        msg.payload = {}\n        msg.id_topic = \"rfid\"\n        msg.payload.RFID = mensaje2;\n        flow.set(\"RFID_ok_flag\", true);\n        flow.set(\"QR_ok_flag\", false);\n        flow.set(\"esperando_id\", false) // No permitimos lectura de otro ID\n        msg.payload.RFID_ok_flag = flow.get(\"RFID_ok_flag\");\n        if(flow.get(\"semaforo\") == \"red\"){\n            msg.payload.matricula = flow.get(\"matricula\");\n        }\n        \n        msg.payload.tipo = \"user_id\";\n        msg.payload.totem_id = flow.get(\"totem_id\");\n        msg.payload.numero_vial = flow.get(\"numero_vial\");\n        \n    }\n    else{\n        mensaje1 = \"Lectura de RFID para BD\";\n        msg.payload = {};\n        flow.set(\"last_RFID\", mensaje2);\n        msg.id_topic == \"rfid\"\n        msg.payload.RFID = mensaje2;\n        flow.set(\"RFID_ok_flag\", true);\n        msg.payload.RFID_ok_flag = flow.get(\"RFID_ok_flag\");\n        msg.payload.totem_id = flow.get(\"totem_id\");\n        msg.payload.numero_vial = flow.get(\"numero_vial\");\n        msg.payload.tipo = \"RFID_BD\";\n        flow.set(\"ID_enviado\", false)\n        \n        \n    }\n    \n}\n\nmsg.level = flow.get(\"audio_level\")\nif(!flow.get(\"ID_enviado\")){\n    node.send(msg)\n    flow.set(\"ID_enviado\", true)\n}\nreturn ;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1000,
        "y": 320,
        "wires": [
            [
                "82a82563.9db368",
                "38dc1312c5833a43"
            ]
        ]
    },
    {
        "id": "82a82563.9db368",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "QR/RFID",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1140,
        "y": 220,
        "wires": []
    },
    {
        "id": "a3edbb144fbdc523",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "QR",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 970,
        "y": 220,
        "wires": []
    },
    {
        "id": "97d5ddae428797d0",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "QR-raw",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 740,
        "y": 200,
        "wires": []
    },
    {
        "id": "38dc1312c5833a43",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "tarjeta leida tx",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 1155,
        "y": 300,
        "wires": []
    },
    {
        "id": "d570cae191434cbe",
        "type": "serial in",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "",
        "serial": "4662144382ad3833",
        "x": 530,
        "y": 320,
        "wires": [
            [
                "97d5ddae428797d0",
                "a66f75ea959be3c4"
            ]
        ]
    },
    {
        "id": "a66f75ea959be3c4",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "Tipo de Lector",
        "func": "if (flow.get(\"lector_activo\")){\n    flow.set(\"lector_activo\", false)\n    if (flow.get(\"tipo_de_lector\") == \"Kimaldi\"){\n        var rx = msg.payload\n        const regex = /\\[([^\\]]+)\\]/;\n        const match = rx.match(regex);\n        const contentInsideBrackets = match ? match[1] : null;\n        var type = rx.substring(2,4)\n        msg.payload = contentInsideBrackets\n        msg.id_topic = type\n    }\n    else{\n        var mensaje2 = msg.payload\n        var index = mensaje2.indexOf(\"-\");  // Gets the first index where a - occours\n        msg.id_topic = mensaje2.substr(0, index); // Gets the first part\n        msg.payload = mensaje2.substr(index + 1);  // Gets the text part\n\n    }\n    \n    setTimeout(function () {\n       flow.set(\"lector_activo\", true)\n    }, 2000);\n    if (flow.get(\"esperando_id\")){\n        node.send(msg)\n    }\n}\nreturn msg ;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 740,
        "y": 320,
        "wires": [
            [
                "608a45d.a49bebc",
                "a3edbb144fbdc523"
            ]
        ]
    },
    {
        "id": "03abdad8b6e3e504",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "2af1fbc4.72dce4",
        "name": "prueba QR",
        "links": [
            "120a33aec5cdad35"
        ],
        "x": 835,
        "y": 260,
        "wires": [
            [
                "608a45d.a49bebc"
            ]
        ]
    },
    {
        "id": "16e2d449.1ae45c",
        "type": "ui_ui_control",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "",
        "events": "all",
        "x": 1600,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "3a1a2185.62ac6e",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "start",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "0.5",
        "topic": "topic",
        "payload": "{\"tab\":\"Totem\"}",
        "payloadType": "json",
        "x": 1390,
        "y": 140,
        "wires": [
            [
                "16e2d449.1ae45c"
            ]
        ]
    },
    {
        "id": "35590e14.a3db22",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "firma2",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1970,
        "y": 100,
        "wires": []
    },
    {
        "id": "c79666f4.caad38",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "firma2",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "0.5",
        "topic": "topic",
        "payload": "{\"tab\":\"Firma\"}",
        "payloadType": "json",
        "x": 1390,
        "y": 320,
        "wires": [
            [
                "16e2d449.1ae45c"
            ]
        ]
    },
    {
        "id": "713ffffb.3fdbd",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "envia firma",
        "func": "if(flow.get(\"firma_flag\")){\n    flow.set(\"last_firma\", msg.payload)\n    if (!flow.get(\"supervisor\")) {\n        //var firma = msg.payload;\n        flow.set(\"firma_ok_flag\", true) // marcamos que hemos colocado una firma en memoria\n        msg.payload = 1\n\n        //msg.payload = {\"numero_vial\":flow.get(\"numero_vial\"),\"totem_id\":flow.get(\"totem_id\"),\"firma\":firma, \"tipo\":\"base64\", \"matricula\":flow.get(\"matricula\")};\n        node.send([msg, null, { \"payload\": { \"tab\": \"Totem\" } }]);\n        flow.set(\"firma_flag\", false);\n        //flow.set(\"firma_cmd_flag\", false);\n        //flow.set(\"firma_activa\", false)\n    }\n    else{\n        msg.payload = { \"numero_vial\": flow.get(\"numero_vial\"), \"totem_id\": flow.get(\"totem_id\"), \"firma\": flow.get(\"last_firma\"), \"tipo\": \"base64\", \"matricula\": flow.get(\"matricula\") };\n        node.send([{payload:1}, msg, { \"payload\": { \"tab\": \"Totem\" } }]);\n    }\n}\nelse\n{\n    node.send([null, null, {\"payload\":{\"tab\":\"Totem\"}}]);\n    flow.set(\"firma_flag\", false);\n    //flow.set(\"firma_cmd_flag\", false);\n    //flow.set(\"firma_activa\", false)\n}\nreturn ;",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1830,
        "y": 140,
        "wires": [
            [
                "35590e14.a3db22",
                "91a3eb404d0e121c"
            ],
            [
                "6b37f85b1ad9472f"
            ],
            [
                "16e2d449.1ae45c"
            ]
        ]
    },
    {
        "id": "bacd5c0e.3c6d8",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "firma1",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1790,
        "y": 100,
        "wires": []
    },
    {
        "id": "95c5aff8.f07e9",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "group": "80938936064ade5a",
        "name": "Firma",
        "order": 1,
        "width": "14",
        "height": "8",
        "format": "<!DOCTYPE html>\n<html>\n <style>\nbody {overscroll-behavior: none;}\nhtml { cursor: none; }     \n  </style> \n<body>\n \n\n<style>\n\n#sig-canvas {\n  border: 2px dotted #000000;\n  border-radius: 15px;\n  cursor: crosshair;\n  background-color:#DBD9D9 ;\n\n}\n\n<!-- Content  -->\n    \n.container {\n  width: 80%;\n  height: 350px;\n  background: aqua;\n  margin: auto;\n  padding: 5px;\n}\n\n.one {\n  width: 70%;\n  height: 340px;\n  margin-left: 2%;\n  /*margin-top:8%;*/\n  /*background: blue;*/\n  float: left;\n}\n\n.two {\n  margin-left: 80%;\n  height: 100px;\n  margin-top:4%;\n \n}\n.send_button{\n  background-color: #07D810; \n  border: none;\n  color: black;\n  padding: 5px 8px;\n  text-align: center;\n  font-weight: bold;\n  display: inline-block;\n  font-size: 30px;\n  width:50%;\n  height:80%;\n  margin-left:10%;\n  margin-top:5%;\n  border-radius: 12px;\n}\n.clear_button{\n  background-color: #cc0000; \n  border: none;\n  color: white;\n  padding: 2px 2px;\n  text-align: center;\n  font-weight: bold;\n  display: inline-block;\n  font-size: 44px;\n  width:50%;\n  height:80%;\n  margin-left:10%;\n  margin-top:5%;\n  border-radius: 12px;\n}\n</style>\n\n\t<h1 style=\"margin-left:25%; color:#0000ff\">Su Firma:</h1>\n\n<section class=\"container\">\n\n  <div class=\"one\">\n        <canvas  id=\"sig-canvas\" width=\"550\" height=\"340\">\n\t\t</canvas>\n  </div>\n  <div class=\"two\">\n      <button class=\"send_button\" id=\"sig-submitBtn\">OK</button>\n  </div>\n  <div class=\"two\" style=\"margin-top:10%\">\n      <button class=\"clear_button\"  id=\"sig-clearBtn\">X</button>\n  </div>\n</section>\n\n  \n\n<script>\nvar dataUrl;\n//window.addEventListener(\"touchstart\", touchHandler, false);\n(function($scope) {\n  window.requestAnimFrame = (function(callback) {\n    return window.requestAnimationFrame ||\n      window.webkitRequestAnimationFrame ||\n      window.mozRequestAnimationFrame ||\n      window.oRequestAnimationFrame ||\n      window.msRequestAnimaitonFrame ||\n      function(callback) {\n        window.setTimeout(callback, 1000 / 60);\n      };\n  })();\n\n  var canvas = document.getElementById(\"sig-canvas\");\n  var ctx = canvas.getContext(\"2d\");\n  ctx.strokeStyle = \"#222222\";\n  ctx.lineWidth = 4;\n  \n  var drawing = false;\n  var mousePos = {\n    x: 0,\n    y: 0\n  };\n  var lastPos = mousePos;\n\n  canvas.addEventListener(\"mousedown\", function(e) {\n    drawing = true;\n    lastPos = getMousePos(canvas, e);\n  }, false);\n\n  canvas.addEventListener(\"mouseup\", function(e) {\n    drawing = false;\n  }, false);\n\n  canvas.addEventListener(\"mousemove\", function(e) {\n    mousePos = getMousePos(canvas, e);\n  }, false);\n\n  // Add touch event support for mobile\n//  canvas.addEventListener(\"touchstart\", function(e) {\n\n//  }, false);\n\n  canvas.addEventListener(\"touchmove\", function(e) {\n    \n     // e.stopPropagation();\n      \n    e.preventDefault();\n    var touch = e.touches[0];\n    var me = new MouseEvent(\"mousemove\", {\n      clientX: touch.clientX,\n      clientY: touch.clientY\n    });\n    canvas.dispatchEvent(me);\n    \n  }, false);\n\n  canvas.addEventListener(\"touchstart\", function(e) {\n    //e.preventDefault(); // esta linea evita que se haga propagación del evento y permite ser mas rapido en la firma\n    mousePos = getTouchPos(canvas, e);\n    var touch = e.touches[0];\n    var me = new MouseEvent(\"mousedown\", {\n      clientX: touch.clientX,\n      clientY: touch.clientY\n    });\n    canvas.dispatchEvent(me);\n  }, false);\n\n  canvas.addEventListener(\"touchend\", function(e) {\n    //e.preventDefault();// esta linea evita que se haga propagación del evento y permite ser mas rapido en la firma\n    var me = new MouseEvent(\"mouseup\", {});\n    canvas.dispatchEvent(me);\n  }, false);\n\n  function getMousePos(canvasDom, mouseEvent) {\n    var rect = canvasDom.getBoundingClientRect();\n    return {\n      x: mouseEvent.clientX - rect.left,\n      y: mouseEvent.clientY - rect.top\n    }\n  }\n\n  function getTouchPos(canvasDom, touchEvent) {\n    var rect = canvasDom.getBoundingClientRect();\n    return {\n      x: touchEvent.touches[0].clientX - rect.left,\n      y: touchEvent.touches[0].clientY - rect.top\n    }\n  }\n\n  function renderCanvas() {\n    if (drawing) {\n      ctx.moveTo(lastPos.x, lastPos.y);\n      ctx.lineTo(mousePos.x, mousePos.y);\n      ctx.stroke();\n      lastPos = mousePos;\n    }\n  }\n/*  \n  function touchHandler(event){\n    if(event.touches.length > 1){\n        //the event is multi-touch\n        //you can then prevent the behavior\n        event.preventDefault()\n    }\n}\n */\n  // Prevent scrolling when touching the canvas\n  document.body.addEventListener(\"touchstart\", function(e) {\n    if (e.target == canvas) {\n      e.preventDefault();\n      \n    }\n    else{\n        \n    }\n  }, false);\n  document.body.addEventListener(\"touchend\", function(e) {\n    if (e.target == canvas) {\n      e.preventDefault();\n      \n    }\n  }, false);\n\n  document.body.addEventListener(\"touchmove\", function(e) {\n    \n    if (!e.target == canvas) {\n      e.preventDefault();\n     \n    }\n    \n  }, false);\n\n  (function drawLoop() {\n    requestAnimFrame(drawLoop);\n    renderCanvas();\n  })();\n\n  function clearCanvas() {\n    canvas.width = canvas.width;\n    ctx.lineWidth = 4;\n  }\n\n  // Set up the UI\n  var sigText = document.getElementById(\"sig-dataUrl\");\n  var sigImage = document.getElementById(\"sig-image\");\n  var clearBtn = document.getElementById(\"sig-clearBtn\");\n  var submitBtn = document.getElementById(\"sig-submitBtn\");\n  \n  clearBtn.addEventListener(\"click\", function(e) {\n    clearCanvas();\n    //sigText.innerHTML = \"Data URL for your signature will go here!\";\n    //sigImage.setAttribute(\"src\", \"\");\n  }, false);\n  submitBtn.addEventListener(\"click\", function(e) {\n    dataUrl = canvas.toDataURL().split(\",\")[1];\n    //dataUrl = canvas.toDataURL('image/jpeg', 0.1); //01 low res; 05 med: 1 high\n    //sigText.innerHTML = dataUrl;\n    //sigImage.setAttribute(\"src\", dataUrl);\n    $scope.send({\"payload\": dataUrl});\n  }, false);\n\n})(scope);\n/*\n(function($scope) { \n    $scope.watch('dataUrl',function(){\n        scope.send({ \"payload\": dataUrl });\n        \n    });\n    \n})($scope);\n*/\n</script>\n</body>\n</html>",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 1590,
        "y": 140,
        "wires": [
            [
                "bacd5c0e.3c6d8",
                "713ffffb.3fdbd"
            ]
        ]
    },
    {
        "id": "8356acc61776cae7",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "uicontrol",
        "links": [
            "0168d111e7191cbf",
            "55be72af5b5206e5",
            "8ee7b0e07937fde7",
            "83a963e6bc339f0e",
            "5263b792c0037321",
            "efa47b4b532c3c95"
        ],
        "x": 1395,
        "y": 200,
        "wires": [
            [
                "16e2d449.1ae45c"
            ]
        ]
    },
    {
        "id": "91a3eb404d0e121c",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "firma_out",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 1995,
        "y": 140,
        "wires": []
    },
    {
        "id": "6b37f85b1ad9472f",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "5682e468.0f80dc",
        "name": "firma_out_supervisor",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 1995,
        "y": 180,
        "wires": []
    },
    {
        "id": "88733868.cf1938",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "group": "f0166794.33066",
        "name": "teclado",
        "order": 2,
        "width": "14",
        "height": "6",
        "format": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  \n\n<style>\nbody {overscroll-behavior: none;}\nhtml { cursor: none !important; }\n\n.main\n{\nheight:330px;\nwidth:655px;\nbackground-color:lightgray;\nmargin-left:5%;\n\n\n}\n.table{\n  width=100%;\n  height=200px;\n}\n\n.button-1{ /* Teclas de la fila de numeros */\n  width: 60px; \n  height:58px; \n  background-color: #b5b5b5; \n  border: 2px solid #000;\n  border-radius: 4px;\n  color: blue;\n  text-align: center;\n  font-weight: bold;\n  font-size: 40px\n}\n.button-2{ /* Teclas de las letras */\n  width: 60px; \n  height:58px; \n  background-color: white; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: black;\n  text-align: center;\n  font-weight: bold;\n  font-size: 40px\n  \n}\n.button-3{ /* Boton de BORRAR */\n  width: 190px; \n  height:58px; \n  background-color: #ca0e02; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: white;\n  text-align: center;\n  font-weight: bold;\n  font-size: 30px\n}\n.button-4{ /* Boton de LIMPIAR */\n  width: 260px; \n  height:58px; \n  background-color: #20c0d0; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: black;\n  text-align: center;\n  font-weight: bold;\n  font-size: 30px\n}\n.button-5{ /* Boton de ENTER */\n  width: 190px; \n  height:58px; \n  background-color: #04f93c; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: black;\n  text-align: center;\n  font-weight: bold;\n  font-size: 30px\n}\n.button-ins{ /* boton de  INSERT */\n  width: 190px; \n  height:58px; \n  background-color: #777878; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: black;\n  text-align: center;\n  font-weight: bold;\n  font-size: 30px\n}\n.clicked {\n  background-color: #72dcfe; !important /* fondo de la tecla cuando es pulsada */\n}\n.insert {\n  background-color: orange; !important /* fondo de la tecla de INSERT cuando es pulsada */\n}\n</style>\n\n<script>\n (function($scope) {\n     $scope.insert = function(tdElemento){ /*funcion para cambiar el fondo de la tecla INSERT */\n        //$scope.send({ payload: \"hola\" });\n        console.log(\"insert OK\")\n        const buttonInsert = tdElemento.querySelector('button');\n        if(!$scope.insertflag){\n            buttonInsert.classList.add('insert');\n        }\n        else{\n            buttonInsert.classList.remove('insert');\n        }\n        $scope.insertflag = !$scope.insertflag\n     }\n      $scope.changeBackground = function(tdElement) { /* funcion para cambiar el background temporal de las teclas pulsadas */\n        //$scope.send({ payload: \"hello\" });\n        const buttonElement = tdElement.querySelector('button');\n        buttonElement.classList.add('clicked');\n\n        setTimeout(() => {\n          $scope.$apply(() => {\n            buttonElement.classList.remove('clicked');\n          });\n        }, 200);\n      };\n    })(scope);\n</script>\n\n</head>\n\n<body>\n\n\n<div class=main >\n<table  class= \"table\">\n\n<tr>\n<td ng-click=\"send({payload: '1'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">1</button></td>\n<td ng-click=\"send({payload: '2'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">2</button></td>\n<td ng-click=\"send({payload: '3'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">3</button></td>\n<td ng-click=\"send({payload: '4'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">4</button></td>\n<td ng-click=\"send({payload: '5'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">5</button></td>\n<td ng-click=\"send({payload: '6'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">6</button></td>\n<td ng-click=\"send({payload: '7'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">7</button></td>\n<td ng-click=\"send({payload: '8'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">8</button></td>\n<td ng-click=\"send({payload: '9'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">9</button></td>\n<td ng-click=\"send({payload: '0'}); changeBackground($event.currentTarget);\" ><button class=\"button-1\">0</button></td>\n</tr>\n<tr>\n<td ng-click=\"send({payload: 'Q'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">Q</button></td>\n<td ng-click=\"send({payload: 'W'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">W</button></td>\n<td ng-click=\"send({payload: 'E'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">E</button></td>\n<td ng-click=\"send({payload: 'R'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">R</button></td>\n<td ng-click=\"send({payload: 'T'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">T</button></td>\n<td ng-click=\"send({payload: 'Y'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">Y</button></td>\n<td ng-click=\"send({payload: 'U'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">U</button></td>\n<td ng-click=\"send({payload: 'I'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">I</button></td>\n<td ng-click=\"send({payload: 'O'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">O</button></td>\n<td ng-click=\"send({payload: 'P'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">P</button></td>\n</tr>\n<tr>\n<td ng-click=\"send({payload: 'A'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">A</button></td>\n<td ng-click=\"send({payload: 'S'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">S</button></td>\n<td ng-click=\"send({payload: 'D'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">D</button></td>\n<td ng-click=\"send({payload: 'F'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">F</button></td>\n<td ng-click=\"send({payload: 'G'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">G</button></td>\n<td ng-click=\"send({payload: 'H'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">H</button></td>\n<td ng-click=\"send({payload: 'J'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">J</button></td>\n<td ng-click=\"send({payload: 'K'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">K</button></td>\n<td ng-click=\"send({payload: 'L'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">L</button></td>\n<td ng-click=\"send({payload: 'Ñ'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">Ñ</button></td>\n</tr>\n<tr>\n\n<td ng-click=\"send({payload: 'ins'}); insert($event.currentTarget); \" colspan=\"3\" ><button class=\"button-ins\">INSERTAR</button></td>\n\n<td ng-click=\"send({payload: 'Z'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">Z</button></td>\n<td ng-click=\"send({payload: 'X'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">X</button></td>\n<td ng-click=\"send({payload: 'C'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">C</button></td>\n<td ng-click=\"send({payload: 'V'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">V</button></td>\n<td ng-click=\"send({payload: 'B'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">B</button></td>\n<td ng-click=\"send({payload: 'N'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">N</button></td>\n<td ng-click=\"send({payload: 'M'}); changeBackground($event.currentTarget);\" ><button class=\"button-2\">M</button></td>\n\n</tr>\n<tr>\n<td ng-click=\"send({payload: 'del'}); changeBackground($event.currentTarget);\" colspan=\"3\"><button class=\"button-3\">< BORRAR</button></td>\n<td ng-click=\"send({payload: 'sp'}); changeBackground($event.currentTarget);\" colspan=\"4\"><button class=\"button-4\">LIMPIAR</button></td>\n<td ng-click=\"send({payload: 'ret'}); changeBackground($event.currentTarget);\" colspan=\"3\"><button class=\"button-5\">ENTER</button></td>\n</tr>\n</table>\n</div>\n\n</body>\n</html>",
        "storeOutMessages": true,
        "fwdInMessages": false,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 2280,
        "y": 440,
        "wires": [
            [
                "881bd652d27e3f70"
            ]
        ]
    },
    {
        "id": "349d0dd447b79ed9",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "matricula",
        "props": [
            {
                "p": "matricula",
                "v": "[\"1\",\"2\",\"3\",\"4\",\"F\",\"P\",\"S\"]",
                "vt": "json"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 2480,
        "y": 400,
        "wires": [
            [
                "0b7fe85c390e446a"
            ]
        ]
    },
    {
        "id": "881bd652d27e3f70",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "add prefix",
        "func": "/*Dado que $watch (utilizado en el template pantalla) no se activa cuando el caracter recibido\nes igual al caracter recibido anteriormente. Es decir su funcion es reportar cambios y no eventos.\nPor lo tanto para que el watch de nuestra funcion se active siempre aunque vayn caracteres iguales uno despues del otro\nlo que hacemos es agregar un caracter al inicio para hacerlo diferente al anterior y luego dentro \ndel $watch le quitamos ese caracter. Asi podemos recibir caracteres iguales seguidos.\n\n*/\n\n\nlet caracter = msg.payload\ncaracter = flow.get(\"prechar\") + caracter //forzamos a que payload cambie siempre para que active $watch en el template\nif(flow.get(\"prechar\") == \"*\"){\n    flow.set(\"prechar\", \"+\" )\n}\nelse{\nflow.set(\"prechar\", \"*\" )\n}\nmsg.payload = caracter;\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2460,
        "y": 440,
        "wires": [
            [
                "0b7fe85c390e446a"
            ]
        ]
    },
    {
        "id": "0b7fe85c390e446a",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "group": "f0166794.33066",
        "name": "display",
        "order": 1,
        "width": "14",
        "height": "2",
        "format": "<style>\nbody {overscroll-behavior: none;}\nhtml { cursor: none !important; }\n\n.matricula-container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  \n}\n\n.matricula-editable {\n  margin:0 auto; /*centra la fila de la tabla */\n  color: black;\n  text-align: center;\n /* border-collapse: collapse; /* Elimina el espacio entre celdas */\n  \n}\n\n.selected{\n  margin: 0; /* Elimina los márgenes predeterminados */\n  padding: 0; /* Elimina el padding predeterminado */\n  width: 60px; \n  height:60px; \n  background-color: #979898; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: black;\n  text-align: center;\n  font-weight: 900;\n  font-size: 65px\n  \n}\n.selected_ins{\n  margin: 0; /* Elimina los márgenes predeterminados */\n  padding: 0; /* Elimina el padding predeterminado */\n  width: 60px; \n  height:60px; \n  background-color: orange; \n  border-width: 3px; \n  border: 2px solid #00f;\n  border-radius: 4px; \n  color: black;\n  text-align: center;\n  font-weight: 900;\n  font-size: 65px\n  \n}\n.unselected{\n  margin: 0; /* Elimina los márgenes predeterminados */\n  padding: 0; /* Elimina el padding predeterminado */\n  width: 60px; \n  height:60px; \n  background-color: white; \n  text-align: center;\n  font-weight: 900;\n  font-size: 65px\n  \n}\n</style>\n<script>\n(function($scope) {\n     $scope.insert = function(tdElemento){\n        console.log(\"insert OK\")\n        const buttonInsert = tdElemento.querySelector('button');\n        if(!$scope.insertflag){\n            buttonInsert.classList.add('insert');\n        }\n        else{\n            buttonInsert.classList.remove('insert');\n        }\n        $scope.insertflag = !$scope.insertflag\n     }\n      $scope.changeBackground = function(tdElement) {\n        //$scope.send({ payload: \"hello\" });\n        const buttonElement = tdElement.querySelector('button');\n        buttonElement.classList.add('clicked');\n\n        setTimeout(() => {\n          $scope.$apply(() => {\n            buttonElement.classList.remove('clicked');\n          });\n        }, 200);\n      };\n    })(scope);\n</script>\n\n<div class=\"matricula-container\" ng-init=\"matricula = msg.matricula; indiceCursor = 0; insert = false;\">\n  <table id=\"table1\"  class=\"matricula-editable\" width=\"70%\">\n  <tr>\n    <td ng-repeat=\"digit in matricula track by $index\" ng-click=\"selectDigit($index)\" ng-class=\"{selected: indiceCursor === $index, unselected: indiceCursor !== $index, selected_ins: indiceCursor == $index && insert == true}\">\n      {{ digit }}\n    </td>\n  </tr>\n</table>\n</div>\n\n<script>\n(function(scope) {\n    \n  // Función para insertar un dígito en la posición seleccionada\n    scope.insertDigit = function(digit) {\n        //console.log(\"matricula \", scope.matricula);\n        //console.log(\"insertDigit \", digit);\n        if (scope.insert){\n            scope.matricula.splice(scope.indiceCursor, 0, digit);\n            //console.log(\"matricula \", scope.matricula);\n        }\n        else{\n            scope.matricula[scope.indiceCursor] = digit;\n        }\n    };\n            \n        \n  // Función para mover el cursor a la derecha\n    scope.selectDigit = function(index) {\n        console.log(\"SelectedDigit = \"+ index);\n        scope.indiceCursor = index;\n    };\n    scope.moverCursorDerecha = function() {\n        console.log(\"Cursor derecha: \", scope.indiceCursor);\n    scope.indiceCursor = (scope.indiceCursor + 1) % scope.matricula.length;\n  };\n  // Función para mover el cursor a la izquierda\n    scope.moverCursorIzquierda = function() {\n        console.log(\"Cursor derecha: \", scope.indiceCursor);\n    scope.indiceCursor = (scope.indiceCursor - 1 + scope.matricula.length) % scope.matricula.length;\n  };\n //********************************************************* $watch **********************************************************\n  // Escucha los cambios en msg.payload\n  scope.$watch('msg', function(msg) {\n      let newCaracter = msg.payload\n      if (msg.hasOwnProperty(\"matricula\")){ // detecta si hemos recibido una matricula para mostrar en pantalla de edicion\n          scope.matricula = msg.matricula;\n          newCaracter = \"\";\n      }\n      //console.log(\"Caracter recibido:\", newCaracter);\n      newCaracter = newCaracter.substring(1);\n     \n    if (newCaracter) {\n      console.log(\"Caracter recibido:\", newCaracter);\n      //dejamos pasar hacia la pantalla solamente numeros y letras\n      \n      //------------------------------- INSERTAR o CAMBIAR CARACTERES -----------------------------------------------------\n      if(newCaracter !== \"<\" && newCaracter !== \">\" && newCaracter !== \"ret\" && newCaracter !== \"del\" && newCaracter !== \"sp\" && newCaracter !== \"ins\" && newCaracter.length == 1){\n        console.log(\"insert = \" + scope.insert)\n        \n        if(!scope.insert){\n            \n            scope.insertDigit (newCaracter) // escribe caracter recibido\n            scope.moverCursorDerecha(); // movemos cursor\n        }\n        else{\n            //console.log(\"matricula size = \" + scope.matricula.length)\n            //console.log(\"indice = \" + scope.indiceCursor)\n            for (let i = scope.matricula.length -1; i >= scope.indiceCursor; i-- ){\n                scope.matricula[i] = scope.matricula[i-1]\n            }\n            scope.matricula[scope.indiceCursor] = newCaracter; // hacemos display del nuevo caracter en la posicion actual\n            scope.moverCursorDerecha(); // movemos cursor\n        }\n        //--------------------------------------------------------------------------------------------------------------------\n      }\n      else if(newCaracter == \"<\"){\n        scope.moverCursorIzquierda();  \n      }\n      else if(newCaracter == \">\"){\n        scope.moverCursorDerecha();  \n      }\n      else if(newCaracter == \"ret\"){ // enviamos matricula editada\n        scope.send({\"payload\":scope.matricula, topic:\"corregida\", caracter:newCaracter});  \n      }\n      else if(newCaracter == \"sp\"){ // enviamos matricula editada\n        scope.matricula = [' ', ' ',' ', ' ',' ', ' ',' ', ' '] ;\n        scope.indiceCursor = 0;\n      }\n      \n      //-------------------------- BORRAR CARACTERES -----------------------------------------------------\n       else if(newCaracter == \"del\"){ // borramos un digito y desplazamos a la izquierda\n        console.log(\"matricula = \" + scope.matricula)\n        console.log(\"matricula_size = \" + scope.matricula.length)\n        console.log(\"indice = \" + scope.indiceCursor)\n        let maxindex = scope.matricula.length\n        /* activar estas lineas para hacer que el cursor sea circular al borrar a la izquierda. Hay que poner <= maxindex aqui abajo\n        if(scope.indiceCursor == 0){\n            scope.indiceCursor = maxindex \n        }\n        */\n        if (scope.indiceCursor > 0 && scope.indiceCursor < maxindex){\n            for (let i = scope.indiceCursor; i <= scope.matricula.length - 1; i++ ){\n                scope.matricula[i-1] = scope.matricula[i]\n            }\n        scope.matricula[scope.matricula.length - 1] = \" \";\n        scope.indiceCursor = scope.indiceCursor - 1\n       }\n      }\n      else if(newCaracter == \"ins\"){ // enviamos matricula editada\n        scope.insert = !scope.insert;  //activamos / desactivamos insercción\n      }\n      //-------------------------------------------------------------------------------------------------------\n    }\n    newCaracter = \"\"\n  });\n})(scope);\nfunction esNumero(caracter) {\n    return /\\d/.test(caracter);\n}\n</script>\n",
        "storeOutMessages": false,
        "fwdInMessages": false,
        "resendOnRefresh": false,
        "templateScope": "local",
        "className": "",
        "x": 2720,
        "y": 440,
        "wires": [
            [
                "d384dd920c270a70"
            ]
        ]
    },
    {
        "id": "0df085dfc5184645",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "in_badplate",
        "links": [
            "77d23eabe6e7d642"
        ],
        "x": 2445,
        "y": 520,
        "wires": [
            [
                "0b7fe85c390e446a",
                "d384dd920c270a70"
            ]
        ]
    },
    {
        "id": "d384dd920c270a70",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "matricula edit",
        "func": "if (msg.topic == \"corregida\"){\n    const corregida = msg.payload\n    // Verificar si el último carácter es un espacio y eliminarlo si lo es\n    if (corregida.length > 0 && corregida[corregida.length - 1] === ' ') {\n        corregida.pop(); // Elimina el último elemento\n    }\n    const matricula = corregida.join('')\n    flow.set(\"matricula_corregida\", matricula)\n    const regex = /(\\d+)|([A-Z]+)/g; // separamos los grupos para que sea mas facil leerlos en el popup\n    const matricula_new = matricula.replace(regex, '$1 $2 ');\n    //node.send([{payload:{tab:\"Totem\"}}, {payload: {numero_vial:flow.get(\"numero_vial\"), totem_id:flow.get(\"totem_id\"), matricula_corregida: matricula}}]);\n    \n    node.send([null, null, {payload:\"<p>MATRICULA MODIFICADA :</p> <p style='font-size:55px;border:5px solid #f00;'>\" + matricula_new +\"</p>\"}, null]);\n    \n}\n// En el siguiente \"if\" incluimos RFID porque si cuando se saca la notificacion de editar matricula el usuario \n// presenta identificacion de tarjeta valida entonces no debemos mostrar la pantalla de edicion al salir de la notificacion\nelse if(flow.get(\"badplate\") && msg.payload ==\"OK\" && !flow.get(\"RFID_revisado\")){\n    flow.set(\"badplate\", false)\n    flow.set(\"editor_matricula_ok\", true) // indica que hemos aceptado ir al editor de matricula\n    node.send([{payload:{tab:\"Matricula\"}}, null, null, null]); \n}\nelse if (msg.payload == \"SI\"){\n    if (!flow.get(\"supervisor\")) {\n        flow.set(\"matricula\", flow.get(\"matricula_corregida\")) //actualiza la matricula en memoria y en pantalla\n        flow.set(\"badplate\", false)\n        flow.set(\"matricula_formato_flag\", true)\n        flow.set(\"matricula_editada_flag\", true) // marcamos que la matricula fue editada por el conductor\n        flow.set(\"nota_vuelva_intentar_done\", false) // para poder sacar la notificacion en caso que haya que volver a sacar editor nuevamente\n        flow.set(\"matricula_revisada\", false) // para poder volver a revisar en base de datos\n        flow.set(\"matriculacheck_solicitada\", false) // marca que hay que solicitar revision de matricula\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        //node.send([{ payload: { tab: \"Totem\" } }, { payload: { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), matricula_corregida: flow.get(\"matricula_corregida\") } }, null]);\n        node.send([{ payload: { tab: \"Totem\" } }, { payload: 1 }, null, null]);\n    }\n    else{\n        node.send([{ payload: { tab: \"Totem\" } }, {payload: 1}, null, { payload: { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), matricula_corregida: flow.get(\"matricula_corregida\") } }]);\n    }\n}\nreturn ;",
        "outputs": 4,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2950,
        "y": 440,
        "wires": [
            [
                "0168d111e7191cbf"
            ],
            [
                "e8d66667ae2598db"
            ],
            [
                "fe332bc89bc4f606"
            ],
            [
                "da0422341e874eb1"
            ]
        ]
    },
    {
        "id": "e8d66667ae2598db",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "matricula_value",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 3125,
        "y": 480,
        "wires": []
    },
    {
        "id": "0168d111e7191cbf",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "matricula_out",
        "mode": "link",
        "links": [
            "8356acc61776cae7"
        ],
        "x": 3125,
        "y": 400,
        "wires": []
    },
    {
        "id": "fe332bc89bc4f606",
        "type": "ui_toast",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 1,
        "ok": "SI",
        "cancel": "NO",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "popup_check",
        "x": 3170,
        "y": 600,
        "wires": [
            [
                "d384dd920c270a70"
            ]
        ]
    },
    {
        "id": "da0422341e874eb1",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "3d6884b5563ad96b",
        "name": "matricula_value_supervisor",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 3125,
        "y": 520,
        "wires": []
    },
    {
        "id": "3d273f36272c6de9",
        "type": "exec",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "command": " ./remove-add-host.sh ",
        "addpay": "payload",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "change hosts",
        "x": 1790,
        "y": 500,
        "wires": [
            [
                "fc94a121dfd9ee37",
                "a992f39766e3c694"
            ],
            [],
            []
        ]
    },
    {
        "id": "fc94a121dfd9ee37",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1950,
        "y": 440,
        "wires": []
    },
    {
        "id": "8e5c7bf9dc501a93",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "1.5",
        "topic": "",
        "payload": "192.168.11.67 raspeso.com",
        "payloadType": "str",
        "x": 1550,
        "y": 420,
        "wires": [
            [
                "3d273f36272c6de9"
            ]
        ]
    },
    {
        "id": "7c5a1da447b15ffd",
        "type": "exec",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "command": "",
        "addpay": "payload",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "change IP",
        "x": 1780,
        "y": 560,
        "wires": [
            [
                "45fb86ba06a5fcce"
            ],
            [],
            []
        ]
    },
    {
        "id": "45fb86ba06a5fcce",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "change IP",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1960,
        "y": 540,
        "wires": []
    },
    {
        "id": "ca75ac762b4f0884",
        "type": "file in",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "read-file",
        "filename": "/home/trypton/.node-red/projects/config_comun.txt",
        "filenameType": "str",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1320,
        "y": 560,
        "wires": [
            [
                "2b018bba18c09a54",
                "d930458472ce86cf"
            ]
        ]
    },
    {
        "id": "b13c06048c797a97",
        "type": "file in",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "read-file",
        "filename": "/home/trypton/.node-red/projects/config_bascula_autonoma.txt",
        "filenameType": "str",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1320,
        "y": 500,
        "wires": [
            [
                "8360f8ecbe5bfff4"
            ]
        ]
    },
    {
        "id": "da672c95637f91bd",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "read_config_in",
        "links": [
            "6c4fcf9dad031bf8",
            "0dee226932d66a29"
        ],
        "x": 1145,
        "y": 520,
        "wires": [
            [
                "b13c06048c797a97",
                "ca75ac762b4f0884"
            ]
        ]
    },
    {
        "id": "dcaffa245475dd85",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "2",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 1170,
        "y": 460,
        "wires": [
            [
                "b13c06048c797a97",
                "ca75ac762b4f0884"
            ]
        ]
    },
    {
        "id": "2b018bba18c09a54",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "config comun",
        "func": "var config = JSON.parse(msg.payload);\nmsg ={}; // clear msg object\nvar read;\n\nflow.set(\"vehiculo_activo_flag\", false);\nflow.set(\"last_QR\", \"-\");\n\n//read = config.peso;\n//flow.set(\"peso\", read);  // crea variable para recibir peso del supervisor\nread = config.peso_estable_flag;\nflow.set(\"peso_estable_flag\", read);\nread = config.peso_estable; // valor del peso estabilizado\nflow.set(\"peso_estable\", read);\nread = config.fin_pesada_flag;\nflow.set(\"fin_pesada_flag\", read);\nread = config.salida_vehiculo_flag;\nflow.set(\"salida_vehiculo_flag\", read);\nread = config.vehiculo_presente_flag;\nflow.set(\"vehiculo_presente_flag\", read);\nread = config.remover_firma;\nflow.set(\"remover_firma\", read);\n\nread = config.matricula_ok_flag;\nflow.set(\"matricula_ok_flag\", read);\nread = config.QR_ok_flag;\nflow.set(\"QR_ok_flag\", read);\nread = config.RFID\nflow.set(\"RFID\", read);\nread = config.RFID_ok_flag;\nflow.set(\"RFID_ok_flag\", read);\nread = config.QR_flag;\nflow.set(\"QR_flag\", read);\n\n\nread = config.auth_pin;\nflow.set(\"auth_pin\", read)\nread = config.pin_timeout; // timeout de espera del pin\nflow.set(\"pin_timeout\", read)\nread = config.mante_timeout; // timeout de la pantalla de mantenimiento\nflow.set(\"mante_timeout\", read)\n\nread = config.sw_bkgnd_on // color del background ON de los botones de seleccion de parametro \nflow.set(\"sw_bkgnd_on\", read);\nread = config.sw_bkgnd_off // color del background OFF de los botones de seleccion de parametro \nflow.set(\"sw_bkgnd_off\", read);\nread = config.titulo_param_color // color del nombre de los parametros\nflow.set(\"titulo_param_color\", read);\nflow.set(\"value_param_color\", read);\nread = config.namefixedparams_color // color del nombre de los parametros fijos (ip, serial, puertos remotos)\nflow.set(\"namefixedparams_color\", read);\nread = config.semaforo_green_color // color del semaforo en verde\nflow.set(\"semaforo_green_color\", read);\nread = config.semaforo_red_color // color del semaforo en rojo\nflow.set(\"semaforo_red_color\", read);\nread = config.titulo_matricula_color // color del titulo de la matricula y del ID\nflow.set(\"titulo_matricula_color\", read)\nread = config.valor_matricula_color // color del titulo de la matricula y del ID\nflow.set(\"valor_matricula_color\", read)\n\nread = config.totemid_max  // maximo numero de identificacion asignado al totem\nflow.set(\"totemid_max\", read);\nread = config.vial_max   // maximo numero de viales admitidos para asignar al dispositico\nflow.set(\"vial_max\", read);\n\n\n\n\nreturn",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1570,
        "y": 560,
        "wires": [
            []
        ]
    },
    {
        "id": "d930458472ce86cf",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "debug 11",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1500,
        "y": 460,
        "wires": []
    },
    {
        "id": "37d1d77b10692029",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "4126e77db69df2f9",
        "name": "Prepare reload flows header-2",
        "func": "\nvar secret_token = msg.payload.access_token\n\nmsg.payload = {}\nmsg.headers = {}\nmsg.method = \"POST\"\nmsg.url = \"http://localhost:1880/flows\" ;\n\nmsg.headers[\"Authorization\"] = \"Bearer \" + secret_token\nmsg.headers[\"content-type\"] = \"application/json\"\nmsg.headers[\"Node-RED-Deployment-Type\"] = \"reload\"\n\nreturn msg;\n\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2810,
        "y": 740,
        "wires": [
            [
                "86e20fa9c20ade1f"
            ]
        ]
    },
    {
        "id": "86e20fa9c20ade1f",
        "type": "http request",
        "z": "953e8740401d175c",
        "g": "4126e77db69df2f9",
        "name": "Request reload flows-2",
        "method": "use",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "senderr": false,
        "x": 3080,
        "y": 740,
        "wires": [
            []
        ]
    },
    {
        "id": "7433bbe3775ba5b7",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "4126e77db69df2f9",
        "name": "test",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "20",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 2290,
        "y": 800,
        "wires": [
            [
                "a992f39766e3c694"
            ]
        ]
    },
    {
        "id": "a992f39766e3c694",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "4126e77db69df2f9",
        "name": "Pepare auth token header",
        "func": "msg.headers = {}\nmsg.method = \"POST\"\nmsg.url = \"http://localhost:1880/auth/token\" ;\nmsg.headers[\"content-type\"] = \"application/json\"\n\n\nmsg.payload = {\n\"client_id\":\"node-red-admin\",\n\"grant_type\":\"password\",\n\"scope\":\"*\",\n\"username\":\"trypton\",\n\"password\":\"Trypton#2022\"\n}\n\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2330,
        "y": 740,
        "wires": [
            [
                "8e4d425eebc745f0"
            ]
        ]
    },
    {
        "id": "8e4d425eebc745f0",
        "type": "http request",
        "z": "953e8740401d175c",
        "g": "4126e77db69df2f9",
        "name": "Request auth token",
        "method": "use",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "senderr": false,
        "x": 2570,
        "y": 740,
        "wires": [
            [
                "37d1d77b10692029"
            ]
        ]
    },
    {
        "id": "8b5516b1119c997f",
        "type": "modbus-flex-getter",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "distance",
        "showStatusActivities": true,
        "showErrors": true,
        "showWarnings": true,
        "logIOActivities": false,
        "server": "dc90e121aa15a212",
        "useIOFile": false,
        "ioFile": "",
        "useIOForPayload": false,
        "emptyMsgOnFail": false,
        "keepMsgProperties": false,
        "delayOnStart": false,
        "startDelayTime": "",
        "x": 3940,
        "y": 1080,
        "wires": [
            [
                "ea8197b45f511dd7"
            ],
            [
                "608b3302d291d648"
            ]
        ]
    },
    {
        "id": "608b3302d291d648",
        "type": "modbus-response",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "",
        "registerShowMax": 20,
        "x": 4130,
        "y": 1100,
        "wires": []
    },
    {
        "id": "0bc82cd2485c7679",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "distance ID1",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "1",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"value\":1,\"fc\":3,\"unitid\":1,\"address\":\"0x100\",\"quantity\":4}",
        "payloadType": "json",
        "x": 3520,
        "y": 1080,
        "wires": [
            [
                "5235b52ecb18fecd"
            ]
        ]
    },
    {
        "id": "9b3f8f7040a794d8",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "temp ID1",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"value\":1,\"fc\":3,\"unitid\":1,\"address\":\"0x102\",\"quantity\":1}",
        "payloadType": "json",
        "x": 3640,
        "y": 1160,
        "wires": [
            [
                "8b5516b1119c997f"
            ]
        ]
    },
    {
        "id": "d65005ac817d14e0",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "distance2 ID1",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"value\":1,\"fc\":3,\"unitid\":1,\"address\":\"0x100\",\"quantity\":4}",
        "payloadType": "json",
        "x": 3630,
        "y": 1040,
        "wires": [
            []
        ]
    },
    {
        "id": "20fe9f7863d4d4ec",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "distance-out",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 4315,
        "y": 1040,
        "wires": []
    },
    {
        "id": "f730c0726e257f7f",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "dist2ble",
        "mode": "link",
        "links": [
            "7fd2f6e6fe3f37ea"
        ],
        "x": 4315,
        "y": 1000,
        "wires": []
    },
    {
        "id": "76877357b0cb15d2",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "salida server",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4330,
        "y": 1180,
        "wires": []
    },
    {
        "id": "fb6b2f1f9986070e",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "camara flag",
        "mode": "link",
        "links": [
            "9b16b5291a12f8d7"
        ],
        "x": 4315,
        "y": 1080,
        "wires": []
    },
    {
        "id": "ea8197b45f511dd7",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "detector",
        "func": "var distance = msg.payload[0]; var msg_vehicle = {payload: \"\"};  var msg_totem = {};\nflow.set(\"distance\", distance); var input_flag\nif (distance < flow.get(\"distance_min\") && distance > 0){\n    flow.set(\"dist_in_counter\", flow.get(\"dist_in_counter\" ) +1)\n    flow.set(\"dist_out_counter\",0) // init counter for detecting out of range the object\n    if (flow.get(\"dist_in_counter\") >= flow.get(\"min_cnt_in_ok\")){ \n        if (!flow.get(\"distance_flag_SET\")){// enviamos el flag true una sola vez\n            var timenow = new Date().getTime()\n            flow.set(\"distance_flag\", true) // este flag indica si el sensor local ha detectado un vehiculo\n            flow.set(\"distance_flag_SET\", true)\n            flow.set(\"distance_flag_ONtime\",timenow) // marcamos tiempo de deteccion\n            flow.set(\"dist_out_counter\",0) // init counter for detecting out of range the object\n            \n            //========================chequeo del sentido del vehiculo ========================================= agregado el 1/05/2024 10:30\n            input_flag = false //inicializamos el flag que indica sentido del vehiculo en el detector remoto\n            flow.set(\"sentido\", \"Salida de bascula\")\n            if(flow.get(\"peso_bajo_flag\")){ // si no hay peso en la bascula el vehiculo esta entrando\n                input_flag = true\n                flow.set(\"sentido\", \"Entrada en bascula\")\n            }\n            else{ // hay peso en la bascula\n                if((timenow - flow.get(\"peso_min_time\")) < 3000){ // si la bascula esta activa desde menos de 3 segundos, el vehiculo esta entrando\n                    input_flag = true\n                    flow.set(\"sentido\", \"Entrada en bascula\")\n                }\n            }\n            //================================= Envia mensaje de deteccion de vehiculo al TOTEM remoto =========================================================================\n            \n            msg_totem.payload = {totem_id:flow.get(\"totem_id\"), sentido:flow.get(\"sentido\"), topic:\"detector_ON\", input_flag:input_flag}\n            msg_totem.url = \"http://\" + flow.get(\"ip_totem_pareja\") + \":1880/infodetector\"\n            node.status({fill:\"red\",shape:\"dot\",text:\"Vehiculo cerca\"});\n            msg_vehicle.payload = {\"numero_vial\":flow.get(\"numero_vial\"),\"totem_id\":flow.get(\"totem_id\"), \"deteccionvehiculo_flag\": true}\n            if(flow.get(\"totem_qty\") == 2){\n                node.send([null, msg_vehicle, null, msg_totem]) // enviamos al otro totem, via red, la deteccion del vehiculo\n            }\n            else if(flow.get(\"totem_qty\") == 1){\n                node.send([null, msg_vehicle, {topic:\"detector_ON\", sentido: flow.get(\"sentido\")}, null])\n            }\n        }\n    }\n    else{\n        //flow.set(\"distance_flag\", false)\n        flow.set(\"dist_out_counter\",0) // init counter for detecting out of range the object\n        //flow.set(\"distance_flag_SET\", false)\n        \n    }\n}\nelse{\n    if (flow.get(\"distance_flag_SET\")){// enviamos el flag false  una sola vez\n        flow.set(\"dist_out_counter\", flow.get(\"dist_out_counter\" ) +1);\n        if (flow.get(\"dist_out_counter\") >= flow.get(\"min_cnt_out_ok\")){ \n            flow.set(\"distance_flag\", false);\n            flow.set(\"distance_flag_SET\", false);\n            flow.set(\"distance_flag_OFFtime\",new Date().getTime()) // marcamos tiempo de deteccion\n            \n            msg_totem.payload = {totem_id:flow.get(\"totem_id\"), topic:\"detector_OFF\"}\n            msg_totem.url = \"http://\" + flow.get(\"ip_totem_pareja\") + \":1880/infodetector\"\n            msg_vehicle.payload = {\"numero_vial\":flow.get(\"numero_vial\"),\"totem_id\":flow.get(\"totem_id\"), \"deteccionvehiculo_flag\": false}\n            \n            node.status({fill:\"green\",shape:\"dot\",text:\"LIBRE\"});\n            if(flow.get(\"totem_qty\") == 2){\n                node.send([null, msg_vehicle, null, msg_totem])\n            }\n            else if(flow.get(\"totem_qty\") == 1){\n                node.send([null, msg_vehicle, {topic:\"detector_OFF\", sentido: flow.get(\"sentido\")}, null])\n            }\n        }\n    }\n    flow.set(\"dist_in_counter\", 0);\n    \n}\nmsg.payload = flow.get(\"distance\")\nmsg.topic = \"distance\"\nreturn [msg, null, null, null];",
        "outputs": 4,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 4120,
        "y": 1040,
        "wires": [
            [
                "f730c0726e257f7f"
            ],
            [
                "20fe9f7863d4d4ec"
            ],
            [],
            [
                "76877357b0cb15d2",
                "55d3cd43f7070262"
            ]
        ]
    },
    {
        "id": "55d3cd43f7070262",
        "type": "http request",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "send detector",
        "method": "POST",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "basic",
        "senderr": false,
        "headers": [],
        "x": 4380,
        "y": 1140,
        "wires": [
            []
        ]
    },
    {
        "id": "5235b52ecb18fecd",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "sensor ?",
        "property": "sensor_distancia_local",
        "propertyType": "flow",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 3700,
        "y": 1080,
        "wires": [
            [
                "8b5516b1119c997f"
            ]
        ]
    },
    {
        "id": "67cd75822b6c9afd",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "analisis detector",
        "info": "",
        "x": 4420,
        "y": 1080,
        "wires": []
    },
    {
        "id": "93ee99e8761e6fbc",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "-> entrada analisis",
        "info": "",
        "x": 4430,
        "y": 1040,
        "wires": []
    },
    {
        "id": "06adfd5485a893aa",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "6443f177eec330e4",
        "name": "envia BLE",
        "info": "",
        "x": 4400,
        "y": 1000,
        "wires": []
    },
    {
        "id": "f1e1c7ed9ae455b4",
        "type": "http in",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "KeepAlive",
        "url": "/NotificationInfo/KeepAlive",
        "method": "post",
        "upload": true,
        "swaggerDoc": "",
        "x": 3500,
        "y": 1300,
        "wires": [
            [
                "a8776f6f1f238fcb"
            ]
        ]
    },
    {
        "id": "a8776f6f1f238fcb",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "function 7",
        "func": "\nvar DeviceID = msg.payload.DeviceID\nif (msg.payload.hasOwnProperty(\"Active\")){\n    msg.payload = {\"Active\": msg.payload.Active, \"Result\": true, \"DeviceID\": DeviceID}\n}\nelse{\n    msg.payload = {\"Result\": true, \"DeviceID\": DeviceID}\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3740,
        "y": 1340,
        "wires": [
            [
                "18cab601c9919c4d"
            ]
        ]
    },
    {
        "id": "18cab601c9919c4d",
        "type": "http response",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 3930,
        "y": 1380,
        "wires": []
    },
    {
        "id": "14d1a0196daddc64",
        "type": "http in",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "DeviceInfo",
        "url": "/NotificationInfo/DeviceInfo",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 3500,
        "y": 1360,
        "wires": [
            [
                "a8776f6f1f238fcb"
            ]
        ]
    },
    {
        "id": "729b5c7d2d5da9a5",
        "type": "http in",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "TollgateInfo",
        "url": "/NotificationInfo/TollgateInfo",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 3510,
        "y": 1420,
        "wires": [
            [
                "a8776f6f1f238fcb",
                "5b23fa622cf4d01b",
                "ade2db3a9cf2dab1"
            ]
        ]
    },
    {
        "id": "9b16b5291a12f8d7",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "camara trigger",
        "links": [
            "fb6b2f1f9986070e"
        ],
        "x": 3455,
        "y": 1640,
        "wires": [
            [
                "76b2fa93b3b5d48a"
            ]
        ]
    },
    {
        "id": "d3223fe2481ad802",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "bascula IN",
        "links": [
            "2f57175a6c859157"
        ],
        "x": 3455,
        "y": 1680,
        "wires": [
            [
                "5c93493bec44bb46"
            ]
        ]
    },
    {
        "id": "f04db2f5954d97a7",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "analisis de camara",
        "func": "// mensaje viene de la camara\n    var timestamp_now = new Date().getTime()\n    flow.set(\"timestamp_now\", timestamp_now)\n    var plate; var format_result;\n    \n    //node.warn(\"peso bajo = \" + flow.get(\"peso_bajo_flag\")  + \" distance_flag = \" + flow.get(\"distance_flag_remote\"))\n//Si entra en este nodo es porque se ha recibido una matricula de la camara ( o del nodo que simula la camara)\n    //---------------------------------- Caso en donde no hay bascula activada ni sensor activado ------------------------------------------------------\n    if(flow.get(\"peso_bajo_flag\") && !flow.get(\"distance_flag_remote\") ){ // revisa si camion no esta en bascula y no esta frente a detector\n        \n        //node.warn(\"anpr0 received\")\n        flow.set(\"anpr0\", msg) // guardamos objeto anpr\n        \n        //******************Counting number of ANPR0 received. for debbuging ************\n        //var temp_array = flow.get(\"anpr0_cnt\")\n        //temp_array.push(timestamp_now)\n        //flow.set(\"anpr0_cnt\", temp_array)\n        //****************************************************************************\n        \n        flow.set(\"anpr0_flag\", true) // marcamos que hemos leido un objeto anpr0 \n        flow.set(\"anpr0_time\", timestamp_now) // marcamos el momento de adquisicion de la matricula\n        plate = msg.payload.Picture.Plate.PlateNumber // guardamos el numero de matricula\n        format_result = checkPlateFormat(plate) // chequeamos validez de la matricula\n        if (format_result.isValid){\n            flow.set(\"valid_anpr0_flag\", true) // marcamos que hemos leido un objeto anpr0 con matricula valida\n            flow.set(`anpr0.payload.Picture.Plate.PlateNumber`, format_result.formattedText) // guardamos matricula en el objeto en caso que haya sido modificada (elininado SP o cualquier otro caracter adicional)\n        }\n        else{\n            if(plate == \"\"){\n                flow.set(\"anpr0_flag\", false) // matricula vacia, por lo tanto el anpr0 no es valido\n            }\n        }\n    }// end of peso > peso_minimo\n    //------------------------------------ Caso en donde hay evento de bascula activada o de sensor remoto activado ------------------------------------------------------------\n    else {  //aseguramos que hay un vehiculo en la bascula.\n        if(flow.get(\"snapshot_rq_flag\")){ //revisamos si hemos solicitado un snapshot. Este se solicita  al pasar por el detector o al activar la bascula\n            // si hay un objeto recibido entonces es el correspondiente al solicitado en el snapshot request\n            flow.set(\"anpr1\", msg) // guardamos objeto en anpr1\n            flow.set(\"anpr1_time\", timestamp_now) // marcamos el momento de adquisicion de la matricula\n            if (msg.payload.hasOwnProperty(\"Picture\")){\n                flow.set(\"plate_from_snapshot\", \"vacio\")\n                var anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // tiempo entre anpr0 y activacion de bascula en caso de haber recibido un anpr0\n                if(msg.payload.Picture.Plate.PlateNumber != \"\"){\n                    flow.set(\"anpr1_flag\", true) // marcamos que hemos leido un objeto anpr1 con estructura\n                    plate = msg.payload.Picture.Plate.PlateNumber // guardamos el numero de matricula\n                    flow.set(\"plate_from_snapshot\", plate)\n                    format_result = checkPlateFormat(plate) // chequeamos validez de la matricula\n                    if (format_result.isValid){\n                        flow.set(\"valid_anpr1_flag\", true) // marcamos que hemos leido un objeto anpr0 con matricula valida\n                        flow.set(`anpr1.payload.Picture.Plate.PlateNumber`, format_result.formattedText) // guardamos matricula en el objeto en caso que haya sido modificada\n                        flow.set(\"last_plate\", flow.get(\"anpr1\")) // guardamos objeto de anpr recibido\n                        flow.set(\"called_from\", flow.get(\"called_from\") + \"-camara-snapshot-valid\") // esta linea es para debug\n                    }\n                    else{ // la matricula recibida no es de formato valido\n                        if(flow.get(\"valid_anpr0_flag\") && ( anpr0_2_bascula < flow.get(\"time_max_2_bascula\")) && anpr0_2_bascula >= 0){ // chequea que el anpr0 se tomo antes de time_max_2_bascula segundos (10 segundos)\n                            flow.set(\"last_plate\", flow.get(\"anpr0\")) // guardamos objeto de anpr0 recibido anteriormente \n                            flow.set(\"called_from\", flow.get(\"called_from\") + \"-camara-snapshot-anpr0\") // esta linea es para debug\n                        }\n                        else{// el anpr0 tampoco es de formato valido\n                            flow.set(\"anpr1_flag\", true)\n                            flow.set(\"last_plate\", flow.get(\"anpr1\")) // guardamos objeto de anpr recibido a pesar que no tenia formato valido pero es la ultima matricula recibida\n                            flow.set(\"called_from\", flow.get(\"called_from\") + \"-camara-snapshot-anpr1\") // esta linea es para debug\n                            node.send([msg, null]) // we do save the whole object received from camera (snapshot)\n                        }\n                    }\n                    //clearTimeout(flow.get(\"timer_snapshot\")); // Eliminamos el timer que habiamos arrancado en espera del snapshot\n                    //flow.set(\"timer_snapshot\", 0)\n                    //node.send([null, { payload: 1 }]) // enviamos al siguiente nodo que hemos tenemos matricula que puede seguir con el proceso\n                    node.send([null, { payload: 1, topic:\"cancel_snapshot_timer\"}]) // enviamos al siguiente nodo que hemos tenemos matricula que puede seguir con el proceso\n                }\n                else{ // la matricula recibida esta vacia\n                    if(flow.get(\"anpr0_flag\") && ( anpr0_2_bascula < flow.get(\"time_max_2_bascula\")) && anpr0_2_bascula >= 0){ // chequea que el anpr0 se tomo antes de time_max_2_bascula segundos (10 segundos)){\n                        flow.set(\"last_plate\", flow.get(\"anpr0\")) // asignamos anpr0 al objeto que se va a enviar\n                        flow.set(\"called_from\", flow.get(\"called_from\") + \"-camara-snapshot-empty-anpr0\")\n                    }\n                    else{ // la matricula recibida antes de entrar en bascula no es apta para la pesada\n                        flow.set(\"anpr1_flag\", true) // vamos a asignar el objeto recibido del snapshot y colocaremos NOT FOUNd en la matricula\n                        var temp = flow.get(\"anpr1\") // snapshot recibido\n                        temp.payload.Picture.Plate.PlateNumber = \"NOT FOUND\"\n                        flow.set(\"last_plate\", temp) // guardamos objeto de anpr recibido y modificado\n                        flow.set(\"called_from\", flow.get(\"called_from\") + \"-camara-snapshot-anpr1-plate_empty\") // esta linea es para debug\n                    }\n                    //clearTimeout(flow.get(\"timer_snapshot\"))\n                    //flow.set(\"timer_snapshot\", null)\n                    //node.send([msg, { payload: 1 }]) // enviamos al siguiente nodo que hemos tenemos matricula que puede seguir con el proceso\n                    node.send([msg, { payload: 1, topic: \"cancel_snapshot_timer\" }]) // we do save the whole object received from camera (snapshot) . Just for debugging\n                }\n                \n            }\n            \n        }\n        else{\n            /*\n            // este es un ANPR enviado por la camara despues de activar la bascula o el sensor\n            flow.set(\"last_plate\", msg) // guardamos objeto de anpr recibido\n            flow.set(\"last_plate_flag\") // marcamos que hemos leido una matricula con camion dentro de la bascula \n            var new_plate = msg.payload.Picture.Plate.PlateNumber // guardamos el numero de matricula \n            var format_check = checkPlateFormat(new_plate)\n            //var time_bascula_to_sensor = Math.abs(flow.get(\"distance_flag_ONtime\") - flow.get(\"peso_min_time\"))\n            // este chequeo es para el caso que recibamos una foto de un vehiculo saliendo. Lo descartamos porque el tiempo\n            //entre la deteccion de peso > peso_min y la deteccion de vehiculo en este totem sera mayor de 3 s. Mientras que en la otra \n            //dirección ese tiempo esta entre 1 y 2 s. Esto en el caso de la balanza de Algimia\n            if (flow.get(\"input_flag\")){\n            //if (flow.get(\"distance_flag_remote\") && time_bascula_to_sensor < flow.get(\"max_time_bascula_sensor\")){ // existe un objeto frente al totem\n                //node.warn(\"objeto + bascula\")\n                if (format_check.isValid) {\n                    flow.set(`last_plate.payload.Picture.Plate.PlateNumber`, format_check.formattedText) // guardamos matricula en el objeto en caso que haya sido modificada\n                }\n                if (format_check.isValid || !flow.get(\"anpr0_flag\")){ // enviamos matricula si es valida o enviamos de todas formas si no tenemos ninguna anpr0\n                    clearTimeout(flow.get(\"timer_snapshot\"));\n                    flow.set(\"timer_snapshot\", 0)\n                    flow.set(\"called_from\", flow.get(\"called_from\") + \"-camara\")\n                    node.send({payload:1})\n                }\n            }\n            */\n        }\n        \n    }// end of checking if peso < peso min AND distance_flag\n\n\nreturn;\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": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3890,
        "y": 1560,
        "wires": [
            [],
            [
                "67d5b1beafb80c7d",
                "cbea4a6fab6a2384",
                "0e5b5bc676912875"
            ]
        ]
    },
    {
        "id": "76b2fa93b3b5d48a",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "sensor ?",
        "property": "sensor_distancia_remoto",
        "propertyType": "flow",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 3560,
        "y": 1620,
        "wires": [
            [
                "4a51d53512daec64"
            ]
        ]
    },
    {
        "id": "5b23fa622cf4d01b",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "camara ?",
        "property": "camara_matricula",
        "propertyType": "flow",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 3680,
        "y": 1560,
        "wires": [
            [
                "f04db2f5954d97a7"
            ]
        ]
    },
    {
        "id": "5c93493bec44bb46",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "camara ?",
        "property": "camara_matricula",
        "propertyType": "flow",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 3700,
        "y": 1680,
        "wires": [
            [
                "0e5b5bc676912875"
            ]
        ]
    },
    {
        "id": "4a51d53512daec64",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "camara ?",
        "property": "camara_matricula",
        "propertyType": "flow",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 3700,
        "y": 1620,
        "wires": [
            [
                "cbea4a6fab6a2384"
            ]
        ]
    },
    {
        "id": "e49672ad6c84b341",
        "type": "http request",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "rq snapshot",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "digest",
        "senderr": false,
        "headers": [],
        "x": 4330,
        "y": 1660,
        "wires": [
            []
        ]
    },
    {
        "id": "ead36b6ad9ef17b8",
        "type": "http in",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "infodetector",
        "url": "/infodetector",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 3510,
        "y": 1500,
        "wires": [
            [
                "ee2d8058d3e40c85"
            ]
        ]
    },
    {
        "id": "ee2d8058d3e40c85",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "detector rx",
        "func": "if(msg.payload.totem_id == flow.get(\"totem_id_remoto\")){\n    if(msg.payload.topic == \"detector_ON\"){\n        var timenow = new Date().getTime()\n        flow.set(\"distance_flag_remote\", true)\n        if(flow.get(\"totem_qty\") == 2 && !flow.get(\"distance_flag\")){\n            flow.set(\"totem_master\", true)// marcamos que el detector remoto detecto vehiculo en esta pesada y tenemos esquema de 2 totem.\n                                          // esto indica que este es debe ser el Totem Master en esta pesada y debera enviar el movimiento al Supervisor\n            node.status({ fill: \"yellow\", shape: \"dot\", text: \"ESPERANDO Bascula\" });\n            setTimeout(function () { // esperamos \"confirm_master_time\" milisegundos para ver si la bascula tambien indica que hay vehiculo\n                if (!flow.get(\"peso_bajo_flag\")){\n                //el vehiculo esta en la bascula por lo tanto este Totem es el master\n                    node.status({ fill: \"green\", shape: \"dot\", text: \"TOTEM Maestro\" });\n                }\n                else{\n                   flow.set(\"totem_master\", false) // hemos detectado que alguien / algo paso delante del sensor y no activo la bascula\n                                                    // por la tanto indicamos que este totem NO es master.\n                    node.status({ fill: \"red\", shape: \"dot\", text: \"TOTEM NO es Maestro\" });\n                    //En este caso hay que enviar un evento al nodo peso con el flag peso_bajo_flag = false \n                    //para forzar un reset de todas las variables ya que en este instante existe imagen recibida\n                    // y por tanto flags que estan en posiciones no iniciales y en principio el peso no se ha movido\n                    // por lo que cuando venga un vehiculo las variables tienen valores indefinidos y los resultados pueden ser erroneos\n                    flow.set(\"peso_bajo_flag\", false) // marcamos peso > pmin para forzar la inicializacion de todos parametros y variables del totem\n                    node.send([null, {payload:1}, null]) // enviamos un evento al nodo \"peso\" para que inicialice las variables\n                }\n            }, flow.get(\"confirm_master_time\"))\n\n\n            \n        }\n        flow.set(\"distance_flag_ONtime\",timenow)\n        flow.set(\"input_flag\", msg.payload.input_flag)\n        var sentido = msg.payload.sentido\n        msg.payload = {\"Result\": true, \"totem_id\": flow.get(\"totem_id\")} // it responds to the server\n        node.send([msg, null, {topic:\"detector_ON\", sentido:sentido}])\n    }\n    else if(msg.payload.topic == \"detector_OFF\"){\n        flow.set(\"distance_flag_remote\", false)\n        msg.payload = {\"Result\": true, \"totem_id\": flow.get(\"totem_id\")} // it responds to the server\n        node.send([msg, null, null])\n    }\n}\nreturn ;",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3710,
        "y": 1500,
        "wires": [
            [
                "18cab601c9919c4d"
            ],
            [
                "1c98099a9069bb98",
                "ae0cbe34a4030d33"
            ],
            [
                "76b2fa93b3b5d48a"
            ]
        ]
    },
    {
        "id": "e978bcd8c43a62b1",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "-> entrada analisis",
        "info": "",
        "x": 4650,
        "y": 1620,
        "wires": []
    },
    {
        "id": "92309a9f0b0f2c29",
        "type": "function",
        "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        // 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,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 4360,
        "y": 1600,
        "wires": [
            [],
            [
                "dba345c891c1fb51"
            ],
            [
                "41cb4ca9c6c75818",
                "4e1d349cc2c30e66"
            ]
        ]
    },
    {
        "id": "dba345c891c1fb51",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "matricula_leida",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 4525,
        "y": 1620,
        "wires": []
    },
    {
        "id": "fe75553cfb61d760",
        "type": "file",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "save picture",
        "filename": "filename",
        "filenameType": "msg",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 4610,
        "y": 1540,
        "wires": [
            []
        ]
    },
    {
        "id": "108fa8de0b9851c3",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "request_snapshot",
        "links": [
            "72222dcbbfea7534"
        ],
        "x": 4175,
        "y": 1700,
        "wires": [
            [
                "e49672ad6c84b341"
            ]
        ]
    },
    {
        "id": "34b97be83a87f482",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "prueba camara",
        "links": [
            "9db92332012fa977"
        ],
        "x": 3455,
        "y": 1580,
        "wires": [
            [
                "5b23fa622cf4d01b"
            ]
        ]
    },
    {
        "id": "fc2c3f9d0fd1f0f7",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "-> envia a supervisor",
        "info": "",
        "x": 4650,
        "y": 1700,
        "wires": []
    },
    {
        "id": "41cb4ca9c6c75818",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "matricula_out",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 4525,
        "y": 1700,
        "wires": []
    },
    {
        "id": "85fe3844a73d42e6",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "detector rx",
        "links": [
            "de4c24109d3a3cec"
        ],
        "x": 3455,
        "y": 1540,
        "wires": [
            [
                "ee2d8058d3e40c85"
            ]
        ]
    },
    {
        "id": "ade2db3a9cf2dab1",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "salida camara",
        "mode": "link",
        "links": [
            "7058b9e632aa584b"
        ],
        "x": 3705,
        "y": 1420,
        "wires": []
    },
    {
        "id": "e771c9458e352071",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "menu-ute",
        "links": [
            "55be72af5b5206e5"
        ],
        "x": 2225,
        "y": 200,
        "wires": [
            [
                "18baf4cac7c5059f"
            ]
        ]
    },
    {
        "id": "7e46c4508c5c2f47",
        "type": "ui_text",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "group": "5b5c29c99e5eb902",
        "order": 2,
        "width": "11",
        "height": 1,
        "name": "Encabezado",
        "label": "<font color={{msg.color}}>{{msg.title}}",
        "format": "",
        "layout": "row-center",
        "className": "title_class",
        "x": 2790,
        "y": 80,
        "wires": []
    },
    {
        "id": "7881be0e538c924f",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "fin seleccion",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 3815,
        "y": 40,
        "wires": []
    },
    {
        "id": "31dd60651ad6ac63",
        "type": "ui_ui_control",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "",
        "events": "all",
        "x": 3870,
        "y": 100,
        "wires": [
            []
        ]
    },
    {
        "id": "95c29990fdb60d16",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "end menu",
        "func": "if(msg.payload == \"send_data\"){\n    \n        //*************************************************************************************************************** */\n        //Estas 5 lineas se han agregado para modificar el mensaje enviado. Anteriormente se enviaban todos GUUID seleccionados\n        //lo cual estaba en WS2Supervisor, ahora solo es necesario enviar el ultimo GUUID seleccionado.\n        let selections = flow.get(\"selections\") // contiene todas las selecciones realizadas\n        let lastitems = selections.split(\",\").filter(e => e);  // Divide y elimina posibles vacíos\n        let last = lastitems[lastitems.length - 1];  // Toma el último GUUID de la seleccion realizada\n        let ws2supervisor = { \"numero_vial\": flow.get(\"numero_vial\"), \"totem_id\": flow.get(\"totem_id\"), \"id_seleccionado\": last }\n        msg.payload = ws2supervisor // colocamos el mensaje a enviar al supervisor\n        flow.set(\"id_seleccionado\", last)\n        flow.set(\"menu_seleccionado_flag\", true) // indica que el conductor ha hecho la seleccion en el menu\n        \n        //*************************************************************************************************************** */\n        //msg.payload = flow.get(\"WS2Supervisor\") // recuperamos el mensaje a enviar al supervisor\n        flow.set(\"WS2Supervisor\", \"\")// inicializamos el mensaje para el servidor\n        flow.set(\"selections\", \"\") // inicializamos las seleccionesrealizadas\n        if (flow.get(\"supervisor)\")){\n                node.send([msg, { payload: { tab: \"Totem\" } }, null]) // envia seleccion del material al supervisor\n        }\n        else{                \n                node.send([null, { payload: { tab: \"Totem\" } }, {payload: 1}]) // envia mensaje al nodo analisis del peso para rechequear todos los flags del estado del sistema\n                \n                /* Esto lo hacemos en el modo correspondiente\n                let new_register = flow.get(\"register\")\n                new_register.id_seleccionado = flow.get(\"id_seleccionado\") \n                flow.set(\"register\", new_register)\n                //flow.set(\"movimiento_registrado_flag\", true) // el registro ya contiene todos los datos a enviar\n                */\n        }\n    \n}\nreturn ;",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3650,
        "y": 60,
        "wires": [
            [
                "7881be0e538c924f"
            ],
            [
                "31dd60651ad6ac63"
            ],
            [
                "22fabd2601b01d02",
                "b4529cd7850cf166"
            ]
        ]
    },
    {
        "id": "813259300549debc",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "analyzing selection",
        "func": "\n//var index_nivel1 = msg.payload\nlet level = flow.get(\"level\")\nlet selections = flow.get(\"selections\")\n// la salida de la tabla y/o del boton back traen en msg.topic el tipo de boton que ha pulsado el usuario:\n// msg.topic = \"atras\" -> se ha pulsado el boton de \"atras\"\n// msg.topic = \"button\" -> se ha pulsado el boton \"OK\" de la ventana de confirmación de parametros seleccionados.\n// msg.topic = cualquier otra texto -> se ha pulsado algun parametro del menu presentado\n\n/*\n//Boton ATRAS regresando siempre al menu inicial (nivel 0)\nif (msg.topic == \"atras\"){\n    msg.payload = \"OK\"\n    flow.set(\"level\", 0)\n    msg.topic = \"menu\"\n    flow.set(\"selections\", \"\")\n    flow.set(\"startmenu_flag\", false)// permitimos hacer display nuevamente del boton para solicitar menu\n    node.send([null, msg])\n}\n*/\n//Boton ATRAS considerando todos los niveles\nif (msg.topic == \"atras\"){\n    msg.payload = \"OK\"\n    if (level > flow.get(\"maxlevel\") || level <= 0){\n        level = 1\n    }\n    level -= 1\n    flow.set(\"level\", level)\n    msg.topic = \"menu\"\n    //eliminamos el ultimo id seleccionado\n    if (selections.length > 0) {\n    \n    let ultimoCaracter = selections.charAt(selections.length - 1);\n    if (ultimoCaracter != \",\"){\n        selections = selections + \",\"\n    }\n    // Dividir la cadena por las comas en un array \n    var partes = selections.split(','); //convertimos el string a un array\n    flow.set(\"partes\", partes)\n    \n    selections = partes.slice(0, -2).join(',') // eliminamos la coma y el ultimo GUID y convertimos en string nuevamente\n    flow.set(\"selections\", selections + ',') // agregamos la coma al final para dejar selections con su formato original\n    }\n    \n    flow.set(\"startmenu_flag\", false)// permitimos hacer display nuevamente del boton para solicitar menu\n    partes = selections.split(',') // la variable selections no tiene la ultima coma. el selections guardado en flow.set si la tiene\n    msg.id_padre = partes.pop()\n    node.send([null, msg])\n}\nelse{\n    if(msg.topic != \"button\"){ // \"button\" se utiliza cuando en el menu se presenta el boton de \"OK\" al final de todas las selecciones\n        const index_menu = msg.index // contiene el index del menu seleccionado es decir el index del ultimo array presentado al usuario\n        var seleccion = []\n        var WS2Supervisor = {}\n        \n        const menu = flow.get(\"menu\"+level) // ultimo menu (array) presentado al usuario\n        const id_padre = menu[index_menu].id // recuperamos el id seleccionado y lo asignamos a variable id_padre para proxima busqueda\n        flow.set(\"selections\", flow.get(\"selections\")  + id_padre + \",\") //agregamos id padre del item seleccionado a la variable donde vamos guardando todas las selecciones\n        //node.warn(flow.get(\"selections\"))\n        flow.set(\"index_menu\", index_menu) // guardamos index del item seleccionado\n        if (level >= flow.get(\"maxlevel\")){\n            flow.set(\"level\", 0)\n        }\n        else {\n            flow.set(\"index_menu\"+level, index_menu) // guardamos el index de la ultima seleccion \n            level += 1 \n            flow.set(\"level\", level) // pasamos al proximo nivel del menu\n        }\n    \n        if (level >= flow.get(\"maxlevel\")){ // si llegamos al ultimo nivel enviamos informacion al supervisor\n            //flow.set(\"selections\", flow.get(\"selections\") + menu[index_menu].id) //agregamos id padre del item seleccionado a la variable donde vamos guardando todas las selecciones\n            //selections = flow.get(\"selections\")\n            //selections = selections.slice(0, -1) // eliminamos la coma al fimal de la ultima seleccion anotada\n            //flow.set(\"selections\", selections)\n            WS2Supervisor = {\"numero_vial\":flow.get(\"numero_vial\"),\"totem_id\":flow.get(\"totem_id\"),\"id_seleccionado\":flow.get(\"selections\")}\n            flow.set(\"WS2Supervisor\", WS2Supervisor) // datos para enviar al supervisor una vez confirmado por el usuario\n        \n        /*\n            WS2Supervisor = {\"numero_vial\":flow.get(\"numero_vial\"),\"totem_id\":flow.get(\"totem_id\"),\"id_seleccionado\":menu[index_menu].id, \n            \"id_padre_seleccionado\":menu[index_menu].id_padre, \"matricula\":flow.get(\"matricula\"), \"fin_pesada_flag\": true}\n            flow.set(\"WS2Supervisor\", WS2Supervisor) // datos para enviar al supervisor una vez confirmado por el usuario\n        */\n            node.send([{payload: \"end\", topic:\"table\"}, null])\n        }\n        else{\n            node.send([{payload: 1, id_padre:id_padre, topic:\"menu\"}, null])\n        }\n    }\n    else if(msg.topic == \"button\"){// el usuario ha seleccionado el boton \"OK\"\n        node.send([{payload: \"button_ok\"}, null])\n        \n    }\n    \n}\n\nreturn ;\n",
        "outputs": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3070,
        "y": 140,
        "wires": [
            [
                "fe5e2d99339ebc08",
                "c08676e7c1b4e6b4"
            ],
            [
                "c08676e7c1b4e6b4"
            ]
        ]
    },
    {
        "id": "19709ef01b428164",
        "type": "ui_button",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "back",
        "group": "5b5c29c99e5eb902",
        "order": 1,
        "width": "3",
        "height": 1,
        "passthru": false,
        "label": "<font color='#dc8903'; size='6px';font-weight='bold';text-align:'left'>Atras</font>",
        "tooltip": "",
        "color": "red",
        "bgcolor": "#015c52",
        "className": "",
        "icon": "fa-arrow-left fa-3x",
        "payload": "back",
        "payloadType": "str",
        "topic": "atras",
        "topicType": "str",
        "x": 2890,
        "y": 200,
        "wires": [
            [
                "813259300549debc"
            ]
        ]
    },
    {
        "id": "18baf4cac7c5059f",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "build table",
        "func": " var msg_head = {payload:0}\n var message = {\"payload\":[], \"button\":\"nobutton\"} // hace display del menu sin mostrar boton de OK\n var id_padre = msg.id_padre\n var i = 0; var j = 0\n \n if (msg.topic == \"menu\"){\n   \n    msg.payload = \"\"\n   \n    var lista = flow.get(\"lista_array\")\n    var size = lista.length\n    var level = flow.get(\"level\")\n    var label\n    msg_head.color = \"blue\"\n    if (level == 0){\n        msg_head.title = \"NOMBRE DEL CLIENTE\"\n    }\n    else if(level == 1){\n        msg_head.title = \"TIPO DE RESIDUOS\"\n    }\n    else if(level == 2){\n        msg_head.title = \"ORIGEN DE LOS RESIDUOS\"\n    }\n    var thismenu = []\n    if (level == 0){ // Caso para cuando empezamos el menu por primera vez\n        flow.set(\"selections\", \"\")\n       for ( i = 0; i < size; i++){\n            if(lista[i].nivel == level){\n                label = lista[i].descripcion // en el primer menu se ponen solamente los que correspondan a nivel 0 \n                message.payload[j] = {\"item\":label, \"parity\":i % 2}\n                thismenu[j] = {\"nivel\": level, \"descripcion\": label, \"id\":lista[i].id, \"id_padre\": lista[i].id_padre}\n                j += 1\n            }\n        } \n    }\n    else {\n        for ( i = 0; i < size; i++){\n            if(lista[i].nivel == level && lista[i].id_padre == id_padre){ //id_padre = id seleccionado en el menu anterior\n                label = lista[i].descripcion // en el primer menu se ponen solamente los que correspondan a nivel 0 \n                message.payload[j] = {\"item\":label, \"parity\":i % 2}\n                thismenu[j] = {\"nivel\": level, \"descripcion\": label, \"id\":lista[i].id, \"id_padre\": lista[i].id_padre}\n                j += 1\n                //thismenu[x] es un array que al salir del lazo contiene los elementos del proximo menu a mostrar\n            }\n        }\n    }\n    flow.set(\"menu\"+level, thismenu) // guardamos el menu del nivel que estemos con el nombre menux donde x es el numero del nivel correspondiente\n    msg.ui_control = {\"place\":\"\"}\n    //msg.payload = 0\n    //msg.className = \"lista_class\"\n    //msg_head.className = \"title_class\"\n    node.send([msg_head, message, null]) // msg_head contiene el titulo del menu. message contiene el menu que sera mostrado\n }\n\nreturn ;\n",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2450,
        "y": 180,
        "wires": [
            [
                "7e46c4508c5c2f47"
            ],
            [
                "dd95d08b4e8afe26"
            ],
            [
                "eba5e6108a6457db"
            ]
        ]
    },
    {
        "id": "eba5e6108a6457db",
        "type": "ui_ui_control",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "",
        "events": "all",
        "x": 2780,
        "y": 160,
        "wires": [
            []
        ]
    },
    {
        "id": "dd95d08b4e8afe26",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "group": "5b5c29c99e5eb902",
        "name": "tabla menu",
        "order": 3,
        "width": "14",
        "height": "7",
        "format": "<style>\n.buttondiv {\n        /*background-color: #cccccc;*/\n        width: 100%;\n        margin-left: 1%;\n        height: 80px;\n        margin-top:1%;\n        margin-right:1%;\n        display: flex;\n        justify-content: center;\n        align-items: center;\n}\n.button_cambio{\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        background-color: #015c52;\n        border: 3;\n        color: #dc8903;\n        padding: 1px 2px;\n        text-align: center;\n        font-weight: bold;\n        /*display: inline-block;*/\n        font-size: 50px;\n        width:20%;\n        height:100%;\n        margin-left:1%;\n        margin-top:1%;\n        border-radius: 12px;\n}\n</style>\n\n<div style=\"height:95%;width:100%;\" ng-if=\"msg.button === 'nobutton'\">\n<table style=\"width:100%\" id=\"table1\" border=\"2\">\n    \n    <tbody>\n        \n        <tr ng-repeat=\"line in msg.payload\" >\n            \n            <td  ng-class=\"($index % 2 === 0) ? 'row_dark' : 'row_light'\" align=center style=font-size:40px;color:black; ng-click=\"selected($index)\">{{line.item}}</td>\n\n        </tr>\n        \n    </tbody>\n</table>\n</div>\n\n<div style=\"height:95%;width:100%;\" ng-if=\"msg.button === 'button'\">\n<table style=\"width:100%\" id=\"table1\" border=\"2\">\n    \n    <tbody>\n        \n        <tr ng-repeat=\"line in msg.payload\" >\n            \n            <td  ng-class=\"line.parity == 0 ? 'row_dark' : 'row_light'\" align=center style=font-size:40px;color:black; ng-click=\"selected($index)\">{{line.item}}</td>\n\n        </tr>\n        \n    </tbody>\n</table>\n<table style=\"height:40%; width:100%\">\n        <tr style=\"height:100%;font-size:32px;width:100%\">\n            <td style=\"width:50%;height:100%\">\n                <div  class=\"buttondiv\">\n                    <button class=\"button_cambio\" id=\"cambioBtn\" ng-click=\"button_cambio()\">{{msg.button_txt}}</button>\n                </div>\n            </td>\n            \n        </tr>\n</table>\n</div>\n<script>\n(function($scope) {\n\n$scope.selected = function(obj) {\n$scope.send({ \"payload\": (document.getElementById(\"table1\").rows[obj].innerText), \"topic\":\"selected\", \"index\":obj});\n\nreturn;\n}\n$scope.button_cambio = function(){\n    $scope.send({\"payload\": \"button_ok\", topic:\"button\"});\n}\nreturn;\n})(scope);\n\n/*\nfunction getColorForRow(row) {\n    if (row % 2 == 0) {\n        return 'row_dark'; \n    } \n    else {\n        return 'row_light'; \n    }\n}\n*/\n</script>",
        "storeOutMessages": false,
        "fwdInMessages": false,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 2790,
        "y": 120,
        "wires": [
            [
                "813259300549debc"
            ]
        ]
    },
    {
        "id": "c08676e7c1b4e6b4",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "menu-loop",
        "mode": "link",
        "links": [
            "d6ce579c7544f360"
        ],
        "x": 3235,
        "y": 260,
        "wires": []
    },
    {
        "id": "d6ce579c7544f360",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "in-menu-loop",
        "links": [
            "c08676e7c1b4e6b4"
        ],
        "x": 2315,
        "y": 120,
        "wires": [
            [
                "18baf4cac7c5059f"
            ]
        ]
    },
    {
        "id": "fe5e2d99339ebc08",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "verificacion",
        "func": "var message = {payload:[]}\nlet index0; let menu0;let index1; let menu1;let index2; let menu2;let index3; let menu3;let index4; let menu4;\n\nif (msg.payload == \"end\"){\n    index0 = flow.get(\"index_menu0\")\n    index1 = flow.get(\"index_menu1\")\n    menu0 = flow.get(\"menu0\")\n    menu1 = flow.get(\"menu1\")\n    message.payload[0] = {\"item\":menu0[index0].descripcion, \"parity\":0 % 2}\n    message.payload[1] = {\"item\":menu1[index1].descripcion, \"parity\":0 % 2}\n    \n    if(flow.get(\"maxlevel\") == 3){\n        index2 = flow.get(\"index_menu2\")\n        menu2 = flow.get(\"menu2\")\n        message.payload[2] = {\"item\":menu2[index2].descripcion, \"parity\":0 % 2}\n    }\n    if(flow.get(\"maxlevel\") == 4){\n        index2 = flow.get(\"index_menu2\")\n        menu2 = flow.get(\"menu2\")\n        index3 = flow.get(\"index_menu3\")\n        menu3 = flow.get(\"menu3\")\n        message.payload[2] = {\"item\":menu2[index2].descripcion, \"parity\":0 % 2}\n        message.payload[3] = {\"item\":menu3[index3].descripcion, \"parity\":0 % 2}\n    }\n    message.title = \"USTED HA SELECCIONADO:\"\n    message.color = \"red\"\n    message.button = \"button\"\n    message.button_txt = \"OK\"\n    node.send([null, message])\n}\nelse if(msg.payload == \"button_ok\"){\n    node.send([{payload: \"send_data\"}, null])\n}\nreturn ;",
        "outputs": 2,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3440,
        "y": 120,
        "wires": [
            [
                "95c29990fdb60d16"
            ],
            [
                "f2352fbfa81f1fbd"
            ]
        ]
    },
    {
        "id": "8ee5165edf4c80fe",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "in-menu-tabla",
        "links": [
            "f2352fbfa81f1fbd"
        ],
        "x": 2645,
        "y": 220,
        "wires": [
            [
                "dd95d08b4e8afe26",
                "7e46c4508c5c2f47"
            ]
        ]
    },
    {
        "id": "f2352fbfa81f1fbd",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "menu-tabla",
        "mode": "link",
        "links": [
            "8ee5165edf4c80fe"
        ],
        "x": 3645,
        "y": 180,
        "wires": []
    },
    {
        "id": "22fabd2601b01d02",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "22864a680b3389c9",
        "name": "salida menu",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 3805,
        "y": 160,
        "wires": []
    },
    {
        "id": "641f679340b3e774",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "group": "ac875e596615614c",
        "name": "Peso",
        "order": 1,
        "width": "10",
        "height": "4",
        "format": "<style>\n\n    body {overscroll-behavior: none;}\n    html { cursor: none; }\n   \n    .peso{\n        \n        font-size:155px;\n        text-align:center;\n        font-weight: bold;\n        vertical-align:middle;\n        color:black;\n        height=100%;\n        padding-right:30px;\n        \n        border:1px;\n        border-color:black;\n        overflow-y: hidden;\n        overflow-x: hidden;\n        \n    }\n</style>\n<div class=\"peso\">\n    <p>\n        {{msg.peso}}\n    </p>\n</div>\n",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 2550,
        "y": 1080,
        "wires": [
            []
        ]
    },
    {
        "id": "338d3caabc377186",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "group": "9ef57be94bfc3d3f",
        "name": "semaforo",
        "order": 1,
        "width": "4",
        "height": "4",
        "format": "<style >\n.dot {\n  height: 180px;\n  width: 180px;\n  background-color: {{msg.semaforo}};\n  border-radius: 50%;\n  display: inline-block;\n  \n}\n.center {\n  text-align: center;\n  height:100%;\n  width:100%;\n  \n}\n</style>\n<div class=\"center\">\n  <br>\n    <span class=\"dot\"></span>\n</div>",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "widget_semaforo",
        "x": 2560,
        "y": 1040,
        "wires": [
            []
        ]
    },
    {
        "id": "d4f51c3e79849570",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "group": "15b0f8318476dc16",
        "name": "Matricula",
        "order": 1,
        "width": "4",
        "height": "4",
        "format": "<style>\n    \n    .matricula{\n        font-size: 24px;\n        text-align:center;\n        vertical-align:0;\n        height:100%;\n        word-wrap: break-word;\n        padding:0;\n        color:yellow;\n    }\n    .title{\n        height:25%;\n        font-size:24px;\n        font-weight: bold;\n        text-align:center;\n        color:{{msg.titulo_matricula_color}};\n    }\n    .value{\n        height:25%;\n        font-size:24px;\n        font-weight: bold;\n        text-align:center;\n        color:{{msg.valor_matricula_color}};\n    }\n</style>\n<script type=\"text/javascript\">\n    (function($scope) {\n        $scope.clickTitulo = function() {\n            $scope.send({payload: {\"tab\": \"Mante2\"}, topic: \"config\"});\n        };\n        $scope.clickTarjeta = function() {\n        $scope.send({payload: \"shutdown\", topic: \"tarjetaid\"});\n        };\n    })(scope);\n</script>\n\n<body>\n\n    <table style=\"height:100%\">\n        <tr class=title id=\"titulo_matricula\" ng-click=\"clickTitulo()\">\n            <td>\n                {{msg.id_m1}}\n            </td>\n        </tr>\n    \n        <tr class=value>\n            <td>\n                {{msg.id_m2}}\n            </td>\n        </tr>\n        <tr class=title id=\"titulo_tarjeta\" ng-click=\"clickTarjeta()\"\">\n            <td>\n                {{msg.id_m3}}\n            </td>\n        </tr>\n        <tr class=value>\n            <td>\n                {{msg.id_m4}}\n            </td>\n        </tr>\n    \n    \n    </table>\n    \n\n</body>\n\n",
        "storeOutMessages": false,
        "fwdInMessages": true,
        "resendOnRefresh": false,
        "templateScope": "local",
        "className": "widget_matricula",
        "x": 2560,
        "y": 1160,
        "wires": [
            [
                "be06f3844edc5a69"
            ]
        ]
    },
    {
        "id": "2f88b8abeda0f377",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "group": "eabd81899686a562",
        "name": "msg&button",
        "order": 0,
        "width": "10",
        "height": "4",
        "format": "<style>\n    .mensaje{\n        font-size: 34px;\n        text-align:center;\n        color:red;\n        vertical-align:0;\n        height=100%;\n        word-wrap: break-word;\n        padding:0;\n        background: yellow;\n    }\n    \n    .two {\n        margin-left: 1%;\n        height: 70%;\n        width:98%;\n        margin-top:1%;\n        /*background: orange;*/\n        font-size: 34px;\n        text-align:center;\n        color:red;\n        vertical-align:0;\n        word-wrap: break-word;\n    }\n    .first{\n        font-size: 34px;\n        text-align:center;\n        font-weight: bold;\n        height: 33%;\n        width:100%;\n        color:{{msg.men_m1_color}};\n    }\n    .second{\n        font-size: 34px;\n        text-align:center;\n        font-weight: bold;\n        height: 33%;\n        width:98%;\n        color:{{msg.men_m2_color}};\n    }\n    .third{\n        font-size: 34px;\n        text-align:center;\n        font-weight: bold;\n        height: 33%;\n        width:98%;\n        color:{{msg.men_m3_color}};\n    }\n    .buttondiv_validacion {\n        /*background-color: #cccc00;*/\n        width: 100%;\n        margin-left: 1%;\n        height: 100px;\n        margin-top:1%;\n        margin-right:1%;\n    }\n  \n    .buttondiv_cambio {\n        /*background-color: #cccc00;*/\n        width: 100%;\n        margin-left: 1%;\n        height: 80px;\n        margin-top:1%;\n        margin-right:1%;\n    }\n    .buttondiv_nocambio {\n        /*background-color: #cccccc;*/\n        width: 100%;\n        margin-left: 1%;\n        height: 80px;\n        margin-top:1%;\n        margin-right:1%;\n    }\n    .button_cambio{\n        background-color: #FAB570;\n        border: 3;\n        color: black;\n        padding: 1px 2px;\n        text-align: center;\n        font-weight: bold;\n        /*display: inline-block;*/\n        font-size: 30px;\n        width:70%;\n        height:80%;\n        margin-left:15%;\n        margin-top:1%;\n        border-radius: 12px;\n    }\n    .button_nocambio{\n        background-color: #02ADD3;\n        border: 3;\n        color: black;\n        padding: 1px 2px;\n        text-align: center;\n        font-weight: bold;\n        /*display: inline-block;*/\n        font-size: 30px;\n        width:70%;\n        height:80%;\n        margin-left:15%;\n        margin-top:1%;\n        border-radius: 12px;\n    }\n    .button_validacion{\n        background-color:{{msg.botonvalidacion_background}};\n        border: 3;\n        color:{{msg.botonvalidacion_foreground}};\n        padding: 1px 2px;\n        text-align: center;\n        font-weight: bold;\n        /*display: inline-block;*/\n        font-size: 50px;\n        width:70%;\n        height:100%;\n        margin-left:30%;\n        margin-top:1%;\n        border-radius: 12px;\n    }\n\n</style>\n\n<body>\n<!--<div ng-if=\"selectedTemplate === 'msg_button1'\">-->\n<div style=\"height:95%;width:100%\" ng-if=\"msg.button === 'message'\">\n    <table style=\"height:100%;width:100%\">\n        <tr class=first>\n            <td>\n                {{msg.men_m1}}\n            </td>\n        </tr>\n        <tr id = \"mantenimiento\" class=second>\n            <td>\n                {{msg.men_m2}}\n            </td>\n        </tr>\n        <tr class=third>\n            <td>\n                {{msg.men_m3}}\n            </td>\n        </tr>\n    </table>\n</div>\n\n\n\n<!-- Tratamiento para DOS BOTONES -->\n<div style=\"height:95%\" ng-if=\"msg.button === 'button2'\">\n    <table style=\"height:30%; width:100%\">\n        <tr style=\"font-size: 32px;\">\n            <td style=\"width:100%;color:{{msg.men_m1_color}};text-align:center;font-weight: bold;\">\n                 {{msg.men_m1}}\n            </td>\n        </tr>\n    </table>\n    <table style=\"height:30%; width:100%\">\n        <tr style=\"font-size: 32px;\">\n            <td style=\"width:100%;color:{{msg.men_m2_color}};text-align:center;font-weight: bold;\">\n                 {{msg.men_m2}}\n            </td>\n        </tr>\n    </table>\n    <table style=\"height:40%; width:100%;\">\n        <tr style=\"height:100%;font-size: 32px;width:100%\">\n            <td style=\"width:50%;height:100%\">\n                <div class=\"buttondiv_cambio\">\n                    <button class=\"button_cambio\" id=\"cambioBtn\" ng-click=\"button_cambio()\">{{msg.button1}}</button>\n                </div>\n            </td>\n            <td style=\"width:50%;height:100%\">\n                <div class=\"buttondiv_nocambio\">\n                    <button class=\"button_nocambio\" id=\"nocambioBtn\" ng-click=\"button_nocambio()\">{{msg.button2}}</button>\n                </div>\n            </td>\n        </tr>\n    </table>\n</div>\n\n<!-- Tratamiento para UN BOTON -->\n\n<div style=\"height:95%;\" ng-if=\"msg.button === 'button1'\">\n    <table style=\"height:30%; width:100%\">\n        <tr style=\"font-size: 32px;height:100%;\">\n            <td style=\"width:100%;color:{{msg.men_m1_color}};text-align:center;font-weight: bold;\">\n                 {{msg.men_m1}}\n            </td>\n        </tr>\n    </table>\n    <table style=\"height:30%; width:100%\">\n        <tr style=\"font-size: 32px;height:100%;\">\n            <td style=\"width:100%;color:{{msg.men_m2_color}};text-align:center;font-weight: bold;\">\n                 {{msg.men_m2}}\n            </td>\n        </tr>\n    </table>\n    <table style=\"height:40%; width:100%\">\n        <tr style=\"height:100%;font-size: 32px;width:100%\">\n            <td style=\"width:50%;height:100%\">\n                <div class=\"buttondiv_cambio\">\n                    <button class=\"button_cambio\" id=\"cambioBtn\" ng-click=\"button_cambio()\">{{msg.button1}}</button>\n                </div>\n            </td>\n            \n        </tr>\n    </table>\n</div>\n\n<!-- Tratamiento para BOTON DE VALIDACION -->\n\n<div style=\"height:95%;\" ng-if=\"msg.button === 'button3'\">\n    <table style=\"height:30%; width:100%\">\n        <tr style=\"font-size: 32px;height:100%;\">\n            <td style=\"margin-left:30%;width:100%;color:{{msg.men_m1_color}};text-align:center;font-weight: bold;\">\n                 {{msg.men_m1}}\n            </td>\n        </tr>\n    </table>\n    \n    <table style=\"height:70%; width:80%\">\n        <tr style=\"height:100%;font-size: 32px;width:100%\">\n            <td style=\"width:50%;height:100%\">\n                <div class=\"buttondiv_validacion\">\n                    <button class=\"button_validacion\" id=\"cambioBtn3\" ng-click=\"button_cambio3()\">{{msg.button3}}</button>\n                </div>\n            </td>\n            \n        </tr>\n    </table>\n</div>\n\n<script type='text/javascript'>\n\n(function(scope) {\n    scope.button_cambio = function(){\n        //console.log(\"cambio\")\n        scope.send({\"payload\": \"cambiar\"});\n        //scope.msg.button = \"button2\"\n        //scope.msg.men_m1 = \"hello\"\n    }\n    scope.button_nocambio = function(){\n        //console.log(\"NO cambio\")\n        scope.send({\"payload\": \"nocambiar\"});\n        //scope.msg.button = \"button2\"\n    }\n    scope.button_cambio3 = function(){\n        \n        scope.send({\"payload\": \"validar\"});\n        \n    }\n})(scope);\n\n\n</script>\n</body>",
        "storeOutMessages": true,
        "fwdInMessages": false,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 2570,
        "y": 1120,
        "wires": [
            [
                "6fcda8c89afa5620"
            ]
        ]
    },
    {
        "id": "6fcda8c89afa5620",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "salida botones",
        "func": "if(flow.get(\"botonsalida\")){\n    if (msg.payload == \"validar\"){\n        flow.set(\"botonsalida\", false) // dehabilita boton de salida\n        flow.set(\"salida_valida_flag\", true) // indica que el boton de salida ha sido pulsado\n        //flow.set(\"fin_pesada_flag\", true) // activa flag de fin de pesada para enviar mensaje a conductor\n        if (flow.get(\"supervisor\")){\n        let msg_ws ={}\n            msg_ws.payload = {numero_vial:flow.get(\"numero_vial\"), totem_id:flow.get(\"totem_id\"), validacion: \"ok\"}\n            node.send([null, msg_ws, null]) // si el modo supervisor remoto esta activo enviamos informacion de boton pulsado\n        }\n        else{\n            node.send([null, null, { payload: 1 }]) // enviamos un evento para que se tome en cuenta el boton recien pulsado\n        }\n    }\n}\nelse{\n    if (msg.payload == \"cambiar\"){ // esto es para el boton que saca el menu en pantalla\n        flow.set(\"level\", 0) // colocamos el nivel en el primer menu\n        flow.set(\"vehiculo_en_posicion\", true) // indica que el conductor pulso el boton para acceder al menu\n        flow.set(\"startmenu_flag\", false) // boton de solicitud de primer menu ejecutado\n        node.send([{payload:{tab:\"Lista\"}, topic:\"menu\"}], null)\n    }\n}\nreturn ;",
        "outputs": 3,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2760,
        "y": 1060,
        "wires": [
            [
                "55be72af5b5206e5"
            ],
            [
                "b010f913d811a8e4"
            ],
            [
                "5d188f7a3a6eaa62",
                "b5e1afed9e1c8376"
            ]
        ]
    },
    {
        "id": "b010f913d811a8e4",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "salida boton validacion",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 2905,
        "y": 1060,
        "wires": []
    },
    {
        "id": "b06ca493e3ecfb9b",
        "type": "ui_template",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "group": "ac875e596615614c",
        "name": "classes",
        "order": 2,
        "width": 0,
        "height": 0,
        "format": "<style>\nbody, * {\ncursor: none !important; /* Oculta el cursor en todo el cuerpo */\n}\n\n.ipactual_class {\n    color: blue !important;\n    background-color: #FFCC00 !important;\n    text-align: left;\n    font-size: 20px;\n    font-weight: bold;\n    }\n.idactual_class {\n    color: yellow !important;\n    background-color: #00247D !important;\n    text-align: left;\n    font-size: 20px;\n    font-weight: bold;\n    }\n.vialactual_class {\n    color: yellow !important;\n    background-color: #CF142B !important;\n    text-align: center;\n    font-size: 20px;\n    font-weight: bold;\n    }\n.valoresactuales_class {\n    background-color: #00247D !important;\n    }\n.cpu_temp{\n    background-color: blue !important;\n}\n.buttonkey_class {\n    color: black !important;\n    background-color: #00247D !important;\n    text-align: center;\n    font-size: 20px;\n    font-weight: bold;\n    }\n\n.md-primary.md-confirm-button.md-button.md-ink-ripple.md-default-theme{ /* boton de ok de la notificacion*/\n    height: 60px !important;\n    width: 110px !important;\n    background-color: #393A3A !important;\n    color: lime !important;\n    font-size: 30px !important; \n    font-weight: bold !important;\n}\n.md-title{ /* barra del titulo dentro de la notificacion*/\n    font-size: 20px !important; \n    color: black !important;\n    font-weight: bold !important;\n    background-color: #feb71f !important;\n    text-align: center !important;\n}\n._md.md-default-theme.md-transition-in{/* color de las barras dentro de la notificacion*/\n    /*background-color: #79d6fb !important;*/\n    background-color: #F0FFFF !important;\n    border: 3px solid #ff943b;!important\n}\n.md-dialog{\n    border: 3px solid #f00;!important\n}\n.md-dialog-content-body{/* espacio del texto de la notificacion*/\n    /*background-color: #79d6fb !important;*/\n    background-color: #F0FFFF !important;\n    font-size: 28px !important; \n    text-align: center !important;\n    color: #000000;\n    font-weight: bold !important;\n}\n.md-primary.md-cancel-button.md-button.md-default-theme.md-ink-ripple{/*boton de cancel de la notificacion*/\n    height: 60px !important;\n    width: 130px !important;\n    background-color: #393A3A !important; \n    color: yellow !important;\n    font-size: 30px !important; \n    font-weight: bold !important;\n}\n.row_dark{ /* tabla del menu */\n    background-color: #D4EFDF !important;\n}\n.row_light{ /* tabla del menu */\n    background-color: #ffffff !important;\n}\n.title_class{ /* Titulo de la pagina del menu */\n  background-color: #9EE7FD !important; \n  border: none;\n  color: #0000ff;\n  text-align: center;\n  font-weight: bold;\n  font-size: 40px;\n  }\n.edit_plate{\n  color: blue !important;\n  background-color: orange !important;\n  text-align: left;\n  font-size: 20px;\n  font-weight: bold; \n   \n}\n\nmd-toolbar.md-default-theme, md-toolbar {\ndisplay: none !important;\n}\n</style>\n",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "global",
        "className": "",
        "x": 2560,
        "y": 1200,
        "wires": [
            []
        ]
    },
    {
        "id": "774eadbf0b93e9e5",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "function 1",
        "func": "flow.set(\"myip\", msg.payload)\nmsg = {}\nmsg.id_m1 = flow.get(\"id_titulo_matricula\");\nmsg.id_m2 = flow.get(\"matricula\");\n//msg.id_m3 = flow.get(\"id_titulo_id\");\nmsg.id_m3 = \"TOTEM IP\";\nmsg.id_m4 = flow.get(\"myip\");\nmsg.titulo_matricula_color = flow.get(\"titulo_matricula_color\") // color del texto del titulo de Matricula y del ID. Definido en el archivo de configuracion\nmsg.valor_matricula_color = flow.get(\"valor_matricula_color\") \nnode.send([null, {payload: {\"tab\": \"Main\"}, topic: \"mante2\"}])\nsetTimeout(function () {\n    msg.id_m1 = flow.get(\"id_titulo_matricula\");\n    msg.id_m2 = flow.get(\"matricula\");\n    msg.id_m3 = flow.get(\"id_titulo_id\");\n    msg.id_m4 = flow.get(\"window_id_m4\");\n    msg.titulo_matricula_color = flow.get(\"titulo_matricula_color\") // color del texto del titulo de Matricula y del ID. Definido en el archivo de configuracion\n    msg.valor_matricula_color = flow.get(\"valor_matricula_color\") \n    node.send(msg)\n}, 2500)\nreturn msg;",
        "outputs": 2,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3200,
        "y": 1080,
        "wires": [
            [
                "d4f51c3e79849570"
            ],
            []
        ]
    },
    {
        "id": "feceb805a5f051dd",
        "type": "exec",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "command": "ip addr show eth0 | grep -oE 'inet ([0-9]{1,3}\\.){3}[0-9]{1,3}' | awk '{print $2}' | tr -d '\\n'",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "ip eth0",
        "x": 3210,
        "y": 1140,
        "wires": [
            [
                "774eadbf0b93e9e5"
            ],
            [],
            []
        ]
    },
    {
        "id": "55be72af5b5206e5",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "salida botones",
        "mode": "link",
        "links": [
            "8356acc61776cae7",
            "e0b4dd6107f95f28",
            "e771c9458e352071"
        ],
        "x": 2905,
        "y": 1020,
        "wires": []
    },
    {
        "id": "8ee7b0e07937fde7",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "link out 2",
        "mode": "link",
        "links": [
            "8356acc61776cae7"
        ],
        "x": 2985,
        "y": 1180,
        "wires": []
    },
    {
        "id": "be06f3844edc5a69",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "click detect",
        "func": "if(msg.topic == \"config\" && !flow.get(\"config_flag\")){\n    setTimeout(function () {\n        //node.warn(\"tiempo1 = \" + new Date().getTime())\n        if(flow.get(\"config_count\") == flow.get(\"numero_clicks\")){\n            \n            //flow.set(\"password_flag\", false)  // detectamos que se han aplicado los clicks previstos para entrar en la pagina\n            //flow.set(\"password\", \"\")\n            var msg2 ={}\n            //msg2.payload = \"Debe Introducir un pin de autorizacion <br> Utilice la tecla de papelera para borrar\"\n            node.send([null, {payload:{tab:\"Main\"}}, null]) // cambiamos a la pagina de configuracion\n            /*\n            flow.set(\"pin_timer\",setTimeout(function(){ //establecemos un time out en la variable pin_timer\n                flow.set(\"password_flag\", false)\n                node.send([null, {payload:{tab:\"Totem\"}}, null])\n            }, flow.get(\"pin_timeout\")))\n            */\n        }\n        flow.set(\"config_count\", 0)\n        flow.set(\"config_flag\", false)\n        //node.warn(\"config_window_time = \" + flow.get(\"config_window_time\"));\n        //node.warn(\"tiempo2 = \" + new Date().getTime())\n    }, flow.get(\"config_window_time\"));//tiempo para detectar el numero de clicks para entrar en la pantalla de mantenimiento\n\n    node.send([{payload:1}, null, null]) // leemos la ip del dispositivo\n    flow.set(\"config_flag\", true)\n    flow.set(\"config_count\", 1)\n}\n\nelse if(msg.topic == \"config\" && flow.get(\"config_flag\")){\n    flow.set(\"config_count\", flow.get(\"config_count\") + 1)\n    \n    \n}\nelse if (msg.topic == \"tarjetaid\" && !flow.get(\"shut_flag\") ) {\n    setTimeout(function () {\n        \n        if (flow.get(\"shut_count\") == 3) {\n            node.send([null, null, {\n                payload:\n                    \"<div style='padding:0; margin:0; line-height:1.1;'>\" +\n                    \"<p style='margin:0; padding:0; font-size:42px; color:#ff0000; font-weight:bold;'>ATENCION</p>\" +\n                    \"<p style='margin:0; padding:20px; font-size:32px; color:#000000;'>\" + \"DESEA APAGAR EL DISPOSITIVO ?\" + \"</p>\" +\n                    \"</div>\"\n            }])\n           \n        }\n        flow.set(\"shut_count\", 0)\n        flow.set(\"shut_flag\", false)\n          \n    }, 1000);//tiempo para detectar el numero de clicks para entrar en la pantalla de mantenimiento\n    flow.set(\"shut_flag\", true)\n    flow.set(\"shut_count\", 1)\n     \n}\nelse if (msg.topic == \"tarjetaid\" && flow.get(\"shut_flag\")) {\n    flow.set(\"shut_count\", flow.get(\"shut_count\") + 1)\n}\n \nreturn null;",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2730,
        "y": 1160,
        "wires": [
            [
                "feceb805a5f051dd"
            ],
            [
                "8ee7b0e07937fde7"
            ],
            [
                "43ae11415631042a"
            ]
        ]
    },
    {
        "id": "9d3fe180d1427902",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "7",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 3090,
        "y": 1160,
        "wires": [
            [
                "feceb805a5f051dd"
            ]
        ]
    },
    {
        "id": "60ccc5ce.32a35c",
        "type": "websocket in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "servidor",
        "server": "",
        "client": "be0e1690e4e13e0d",
        "x": 150,
        "y": 1160,
        "wires": [
            [
                "3bd15195.2069fe"
            ]
        ]
    },
    {
        "id": "d27e3fef.df3b1",
        "type": "websocket in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "ws/test",
        "server": "eb652223.5293e",
        "client": "",
        "x": 150,
        "y": 1240,
        "wires": [
            [
                "3bd15195.2069fe"
            ]
        ]
    },
    {
        "id": "3bd15195.2069fe",
        "type": "json",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 330,
        "y": 1200,
        "wires": [
            [
                "197dfc68.458e54",
                "f0e588f6149dad6d",
                "e7cb71b1e09afda6"
            ]
        ]
    },
    {
        "id": "197dfc68.458e54",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "Check JSON content",
        "func": "if ((msg.payload.totem_id == flow.get(\"totem_id\") || msg.payload.totem_id == \"*\") && msg.payload.numero_vial == flow.get(\"numero_vial\")) { // verifica que los datos son para este totem\n//if (msg.payload.numero_vial == flow.get(\"numero_vial\")) { // verifica que los datos son para este vial   \n    //guarda los elementos JSON recibidos del supervisor\n    /*\n    if (msg.payload.hasOwnProperty(\"numero_vial\")){\n        flow.set(\"numero_vial\", msg.payload.numero_vial);\n    }\n    */\n    if (msg.payload.hasOwnProperty(\"peso\")){\n        flow.set(\"peso\", msg.payload.peso);\n        \n    }\n    if (flow.get(\"supervisor\")) {\n        if (msg.payload.hasOwnProperty(\"peso_estable\")) {\n            flow.set(\"peso_estable\", msg.payload.peso_estable);\n        }\n        if (msg.payload.hasOwnProperty(\"peso_estable_flag\")) {\n            flow.set(\"peso_estable_flag\", msg.payload.peso_estable_flag);\n        }\n        if (msg.payload.hasOwnProperty(\"fin_pesada_flag\")) {\n            flow.set(\"fin_pesada_flag\", msg.payload.fin_pesada_flag);\n        }\n        if (msg.payload.hasOwnProperty(\"firma_cmd_flag\")) {\n            flow.set(\"firma_cmd_flag\", msg.payload.firma_cmd_flag);\n            flow.set(\"firma_flag\", true);\n        }\n        if (msg.payload.hasOwnProperty(\"remover_firma\")) {\n            flow.set(\"remover_firma\", msg.payload.remover_firma);\n        }\n        if (msg.payload.hasOwnProperty(\"id_leido\")) {\n            flow.set(\"id_leido\", msg.payload.id_leido);\n        }\n        if (msg.payload.hasOwnProperty(\"id_no_leido\")) {\n            flow.set(\"id_no_leido\", msg.payload.id_no_leido);\n        }\n        if (msg.payload.hasOwnProperty(\"id_tarjeta\")) {\n            flow.set(\"id_tarjeta\", msg.payload.id_tarjeta);\n        }\n        if (msg.payload.hasOwnProperty(\"matricula\")) {\n            flow.set(\"matricula\", msg.payload.matricula);\n        }\n        \n    }\n    //****************************** RESPUESTA DE LA REVISION DE LA UBICACION INTERNA EN BD *******************************************\n    if (msg.payload.hasOwnProperty(\"ubicacion_interna\")) {\n        flow.set(\"ubicacion_recibida\", true)\n        flow.set(\"ubicacion_interna\", msg.payload.ubicacion_interna) // si true -> se encontro ubicacion, si false -> No hay ubicacion en BD\n        flow.set(\"dirigirse\", msg.payload.descripcion) // Sitio donde debe dirigirse\n\n    }\n    //****************************** RESPUESTA DE LA REVISION DE LA MATRICULA EN BD *******************************************\n    if (msg.payload.hasOwnProperty(\"registrada\")) {\n        //node.warn(\"por aqui paso\")\n        flow.set(\"matricula_revisada\", true);\n        flow.set(\"matricula_autorizada\", msg.payload.registrada)\n        if(msg.payload.registrada){ // la matricula esta registrada y por lo tanto autorizada\n            //node.warn(\"pma = \" + msg.payload.pma + \"  tara = \" + msg.payload.tara)\n            flow.set(\"pma\", msg.payload.pma)\n            flow.set(\"tara\", msg.payload.tara)\n            flow.set(\"entransito\", msg.payload.transito)\n            flow.set(\"tipo_movimiento\", msg.payload.transito ? \"final\" : \"inicial\");// Si esta en transito = final en caso contrario = inicial\n            if (msg.payload.aviso_identificacion){ // si la matricula existe una sola vez en Matriculas_Identificadores y la descripcion tiene coma, hay que sacar anuncio de identificacion\n                flow.set(\"window_men_m3\", flow.get(\"men_identificacion\")); \n            }\n            if (!flow.get(\"entransito\")){\n                \n            }\n        }\n    }\n   \n    //******************************** RESPUESTA DE REVISION DEL CODIGO DE LA TARJETA RFID EN BD *****************************\n    if (msg.payload.hasOwnProperty(\"rfid_revisado\")) {\n        flow.set(\"RFID_revisado\", msg.payload.rfid_revisado);\n        flow.set(\"RFID_verificado\", msg.payload.rfid_verificado);\n        //*************Si la tarjeta existe recuperamos los datos asociados ***************************\n        if(msg.payload.rfid_verificado){\n            flow.set(\"cliente\", msg.payload.cliente)\n            flow.set(\"residuo\", msg.payload.residuo)\n            //Para el caso de RFID recibimos la matricula asociada a la tarjeta y no importa ahora la matricula leida de la camara ANPR\n            //por eso a continuación comenzamos a buscar en BD si esta matricula tiene transito. (Ya sabemos que existe en BD y esta autorizada) \n            flow.set(\"matricula\", msg.payload.matricula)// Toda tarjeta debe tener una matricula asociada en BD. Leemos y guardamos dicha matricula recibida\n            if (flow.get(\"matricula\")) {\n                //armamos los flags para que el nodo matricula 2 pueda solicitar a BD la verificacion de esta matricula, si esta en transito o si no lo esta\n                flow.set(\"matricula_leida_flag\", true)\n                flow.set(\"matricula_formato_flag\", true)\n                flow.set(\"matricula_ok_flag\", true)\n                flow.set(\"matricula_revisada\", false)\n                flow.set(\"matriculacheck_solicitada\", false)\n            }\n            \n        }\n    }\n\n    //******************************** RESPUESTA DE REVISION DEL CODIGO QR EN BD *****************************\n    if (msg.payload.hasOwnProperty(\"qr_revisado\")) {\n        flow.set(\"QR_revisado\", msg.payload.qr_revisado);\n        flow.set(\"QR_verificado\", msg.payload.qr_verificado);\n        flow.set(\"ID_obligatorio_tmp\", false) // en caso que ID_obligatorio = false, pero usuario presenta un QR entonces hay que verificar que esta en BD y puede pasar a ser obligatorio para verificar que esta en BD\n        //*************Si el codigo QR existe recuperamos los datos asociados ***************************\n        if(msg.payload.qr_verificado){\n            flow.set(\"cliente\", msg.payload.cliente)\n            flow.set(\"residuo\", msg.payload.residuo)\n            \n            /* Este codigo era para verificar la matricula pero ahora el QR no esta asociado a matricula\n            if (flow.get(\"matricula\") == msg.payload.matricula){ // si la matricula coincide con la leida damos por finalizado el chequeo de matricula\n                flow.set(\"matricula_formato_flag\", true) // indica matricula esta en formato\n                flow.set(\"matricula_ok_flag\", true)\n                flow.set(\"matricula_revisada\", true) //se ha revisado la existencia de la matricula en BD\n                flow.set(\"matricula_autorizada\", true) // matricula autorizada\n                \n            }\n            else{\n                // Si la matricula es diferente tomamos como matricula la recibida de la BD\n                // Ahora tenemos que confirmar si esa matricula esta en transito para saber el tipo de movimiento que vamos agestionar en esta pesada\n                //por lo tanto ponemos los flags para que pueda volver a buscar la matricula en BD.\n                flow.set(\"matricula_revisada\", false) //  Se pone en false para volver a revisar la matricula correcta\n                //node.warn(\"matricula leida = \" + flow.get(\"matricula\") + \" ; matricula del qr = \" + msg.payload.matricula)\n                flow.set(\"matricula_autorizada\", false) // matricula autorizada\n                flow.set(\"matriculacheck_solicitada\", false)  // para poder volver a consultar una matricula\n                flow.set(\"matricula_formato_flag\", true) // indica matricula esta en formato\n                flow.set(\"matricula_ok_flag\", true) // aseguramos que la matricula puede ser buscada en la base de datos en el nodo matricula\n                flow.set(\"matricula_leida_flag\", true) // aseguramos que la matricula puede ser buscada en la base de datos en el nodo matricula\n                flow.set(\"matricula\", msg.payload.matricula) // guardamos la matricula correcta\n            }\n            */\n        }\n        else{\n            flow.set(\"ID_obligatorio_tmp\", true)// en caso que ID_obligatorio = false, pero usuario presenta un QR entonces hay que verificar que esta en BD asi que temporalmente el QR pasa a obligatorio\n        }\n    }\n\n    //--------------------- Recepcion de la lista de materiales para el menu -------------------------\n\n    if (msg.payload.hasOwnProperty(\"lista\") && msg.payload.hasOwnProperty(\"encabezado_lista\") && flow.get(\"menu_activo\")) {\n        flow.set(\"lista_flag\", true)\n        flow.set(\"encabezado_lista\", msg.payload.encabezado_lista)\n        flow.set(\"lista_array\", msg.payload.lista)\n        flow.set(\"menu_inicio\", msg.payload.menu_inicio)\n\n        flow.set(\"window_men_m3\", \"\"); // eliminamos mensaje de \"Acerque QR o Tarjeta\" de la pantalla en caso que este puesto\n    }\n    else {\n    \n        flow.set(\"lista_flag\", false)\n        \n    }\n    //---------------------- FIN de Recepcion de la lista de materiales para el menu ----------------------\n\n    //---------------------- BOTONES EN LA PANTALLA -------------------------------------------------------\n    if (msg.payload.hasOwnProperty(\"button\")) {\n        flow.set(\"button\", msg.payload.button);\n    }\n    if (msg.payload.hasOwnProperty(\"button1\")) {\n        flow.set(\"button1\", msg.payload.button1);\n    }\n    if (msg.payload.hasOwnProperty(\"button2\")) {\n        flow.set(\"button2\", msg.payload.button2);\n    }\n    //************** APAGADO / ENCENDIDO DE LA PANTALLA ******************************** \n    if (msg.payload.hasOwnProperty(\"screenOn_flag\")){\n        flow.set(\"screenOn_flag\", msg.payload.screenOn_flag);\n        if(msg.payload.screenOn_flag){\n            \n            node.send([null, {\"payload\":\"sudo echo 0 > /sys/class/backlight/rpi_backlight/bl_power\"}]);\n            //node.send([null, {\"payload\":\"xset -display :0 dpms force on\"}]);\n        }\n        else{\n            node.send([null, {\"payload\":\"sudo echo 1 > /sys/class/backlight/rpi_backlight/bl_power\"}]);\n            //node.send([null, {\"payload\":\"xset -display :0 dpms force off\"}]);\n        }\n    }\n    // **********************************************************\n    if (msg.payload.hasOwnProperty(\"modo_totem\")){ // chequea si cambiamos el modo de trabajo del totem\n        flow.set(\"modo_totem\", msg.payload.modo_totem);\n    }\n    if (msg.payload.hasOwnProperty(\"uso_detector_ok\")){ \n        flow.set(\"uso_detector_ok\", msg.payload.uso_detector_ok);\n    }\n    \n    \n    if (msg.payload.hasOwnProperty(\"badplate\")){ // chequea si tenemos que pedir corrección de matricula\n        flow.set(\"badplate\", msg.payload.badplate);\n    }\n    \n    //------------------------ Hacer display de un BOTON de VALIDACION a la salida ------------------------\n/*    \n    if (msg.payload.hasOwnProperty(\"botonsalida\")){ // chequea si tenemos que pedir boton de salida en pantalla\n        flow.set(\"botonsalida\", msg.payload.botonsalida);\n        if (msg.payload.hasOwnProperty(\"botonsalida_txt\")){\n            flow.set(\"botonsalida_txt\", msg.payload.botonsalida_txt);\n        }\n        if (msg.payload.hasOwnProperty(\"botonsalida_color\")){\n            flow.set(\"botonsalida_color\", msg.payload.botonsalida_color);\n        }\n        if (msg.payload.hasOwnProperty(\"botonsalida_txt_color\")){\n            flow.set(\"botonsalida_txt_color\", msg.payload.botonsalida_txt_color);\n        }\n        if (msg.payload.hasOwnProperty(\"botonsalida_mensaje1\")){\n            flow.set(\"botonsalida_mensaje1\", msg.payload.botonsalida_mensaje1);\n        }\n    }\n*/\n    //*********************************** RESET REMOTO *****************************************************\n    if (msg.payload.hasOwnProperty(\"reboot_flag\")){ // comando para resetear el dispositivo desde el supervisor\n        \n        if(msg.payload.reboot_flag){   \n            node.send([null, {payload:\"sudo shutdown -r now\"}]);\n        }\n        \n    }\n    //===================== deteccion de comando para iniciar la configuración ===========================================================\n    if (msg.payload.hasOwnProperty(\"init_config\")) {\n        if (msg.payload.init_config == true){\n            node.send([null, null, {payload: 1}])\n        }\n    }\n    \n    //===================== deteccion de comando para gestionar semaforo ===========================================================\n    if (msg.payload.hasOwnProperty(\"semaforoabierto\")) {\n        if (msg.payload.semaforoabierto == true) {\n            flow.set(\"semaforo\", \"green\")\n            flow.set(\"semaforoabierto\", true)   \n        }\n        else{\n            flow.set(\"semaforo\", \"red\")\n            flow.set(\"semaforoabierto\", false) \n        }\n    }\n    \n    //===================== deteccion de comando para abrir barrera ===========================================================\n    if (msg.payload.hasOwnProperty(\"abrirbarrera\")) {\n        if (msg.payload.abrirbarrera == true) {\n            node.send([ null, null, null, { payload: 0 }])\n            setTimeout(function () {\n                node.send([null, null, null, { payload: 1 }])\n            }, flow.get(\"relay2_pulse\"));\n        }\n        \n    }\n    //=====================================================================================================================================\n    \n    \n    \n    \n    \nmsg = {payload: 1};\nreturn msg\n}\nelse{\n\n    return // en caso de no ser el mensaje para este dispositivo no enviamos mensaje al proximo nodo, de esa manera no habra ninguna respuesta de este dispositivo\n}\n\n",
        "outputs": 4,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 540,
        "y": 1200,
        "wires": [
            [
                "b890981ca603e6e1",
                "bb6e7b4ac0e9a9f0"
            ],
            [
                "07212ec8d039ced0"
            ],
            [
                "6c4fcf9dad031bf8"
            ],
            [
                "cf7b0c00631682b2"
            ]
        ]
    },
    {
        "id": "48eda297.9cdedc",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "Envio de mensajes",
        "func": "// Este nodo realiza todos los envios de mensajes a los diferentes destinos: websocket, pantalla, relay\n\nif ((msg.id_topic == \"rfid\" || msg.id_topic == \"QR\") && flow.get(\"totem_master\")) { // si el mensaje proviene del lector de ID\n\n    if (flow.get(\"RFID_ok_flag\") || flow.get(\"QR_ok_flag\")) {\n        if(flow.get(\"QR_verificado\") || flow.get(\"RFID_verificado)\")){\n            flow.set(\"window_id_m4\", flow.get(\"id_leido\"));\n            flow.set(\"window_men_m3\", \"\")\n        }\n        //flow.set(\"QR_ok_flag\", false)\n        //flow.set(\"RFID_ok_flag\", false)\n        if (flow.get(\"peso_bajo_flag\")) {\n            // para el caso cuando queremos enviar tarjetas para asignar usuario en la base de datos\n            //flow.set(\"window_id_m4\", flow.get(\"id_tarjeta\"));\n            msg = {};\n            setTimeout(function () {\n                //node.warn(\"erase window\")\n                //flow.set(\"QR_ok_flag\", false)\n                //flow.set(\"RFID_ok_flag\", false)\n                flow.set(\"window_id_m4\", \"\");\n                // ================================== configura mensaje para las ventanas de la pantalla ===========================\n                var msg_display = { \"id_m1\": \"\", \"id_m2\": \"\", \"id_m3\": \"\", \"id_m4\": \"\", \"men_m1\": \"\", \"men_m2\": \"\", \"men_m3\": \"\", \"peso\": \"\" };\n                msg_display.id_m1 = flow.get(\"id_titulo_matricula\");\n                msg_display.id_m2 = flow.get(\"matricula\");\n                msg_display.id_m3 = flow.get(\"id_titulo_id\");\n                msg_display.id_m4 = flow.get(\"window_id_m4\");\n\n                msg_display.men_m1 = flow.get(\"window_men_m1\");\n                msg_display.men_m2 = flow.get(\"window_men_m2\");\n                msg_display.men_m3 = flow.get(\"window_men_m3\");\n\n                msg_display.peso = flow.get(\"peso\");\n                //asignamos el valor real del color del semaforo, este valor se define en el archivo de configuración\n                if (flow.get(\"semaforo\") == \"green\") {\n                    msg_display.semaforo = flow.get(\"semaforo_green_color\")\n                }\n                else {\n                    msg_display.semaforo = flow.get(\"semaforo_red_color\")\n                }\n\n\n                // ==================================================================================================================\n                node.send([msg_display, null, null, null])\n            }, 3000)\n        }\n\n\n    }\n    else {\n        if (flow.get(\"vehiculo_presente_flag\")) {\n            flow.set(\"window_id_m4\", flow.get(\"id_no_leido\"));\n        }\n        else {\n\n        }\n\n\n    }\n}\n//==============================================  Chequea si Fin de Pesada y Saca Mensaje ===========================================\nif (flow.get(\"fin_pesada_flag\") && flow.get(\"totem_master\")) {\n    setTimeout(() => {\n        node.send([null, null, null, { payload: \"fin_pesada\" }, null])   // salida de audio de FIN DE PESADA   <----\n    }, 500);\n    setTimeout(() => { // elimina cualquier notificacion en pantalla 3,5 segundos despues de detectar fin de pesada\n        if (!flow.get(\"mostrar_ubicacion_ok\")){\n            node.send([null, null, null, null, { payload: \"\" }])\n        }   \n    }, 3500);\n\n    flow.set(\"fin_pesada_flag\", false) // evitamos que en esta pesada vuelva a entrar aqui nuevamente\n    flow.set(\"semaforo\", \"green\")\n    // sacar comando para barrera si esta se encuentra habilitada\n    //node.send([null, { payload: 1 }, null, null])\n    if (flow.get(\"barrera_habilitada\")) {\n        node.send([null, { payload: 0 }, null, null])\n        setTimeout(function () {\n            node.send([null, { payload: 1 }, null, null])\n        }, flow.get(\"relay2_pulse\"));\n    }\n    //flow.set(\"peso_estable_flag\", false)\n    flow.set(\"botonsalida\", false)\n    //coloca los mensajes para mostrar en pantalla \n    flow.set(\"window_men_m1\", flow.get(\"men_pesaje_listo\"));\n    flow.set(\"window_men_m2\", flow.get(\"men_salida\"));\n    flow.set(\"window_men_m3\", \"\");\n}\n//===================================================================================================================== \n\n\n// ================================== configura mensaje para las ventanas de la pantalla ===========================\nvar msg_display = { \"id_m1\": \"\", \"id_m2\": \"\", \"id_m3\": \"\", \"id_m4\": \"\", \"men_m1\": \"\", \"men_m2\": \"\", \"men_m3\": \"\", \"peso\": \"\" };\nif (!flow.get(\"peso_bajo_flag\")) {\n    msg_display.id_m2 = flow.get(\"matricula\");\n    msg_display.id_m4 = flow.get(\"window_id_m4\");\n}\nelse{\n    msg_display.id_m2 = \"\"\n    msg_display.id_m4 = \"\"\n}\nmsg_display.id_m1 = flow.get(\"id_titulo_matricula\");\nmsg_display.id_m3 = flow.get(\"id_titulo_id\");\n\nmsg_display.men_m1 = flow.get(\"window_men_m1\");\nmsg_display.men_m2 = flow.get(\"window_men_m2\");\nmsg_display.men_m3 = flow.get(\"window_men_m3\");\n\nmsg_display.peso = flow.get(\"peso\");\n//asignamos el valor real del color del semaforo, este valor se define en el archivo de configuración\nmsg_display.semaforo = flow.get(\"semaforo\");\nif (flow.get(\"semaforo\") == \"green\") {\n    msg_display.semaforo = flow.get(\"semaforo_green_color\")\n}\nelse {\n    msg_display.semaforo = flow.get(\"semaforo_red_color\")\n}\nmsg_display.titulo_matricula_color = flow.get(\"titulo_matricula_color\") // color del texto del titulo de Matricula y del ID. Definido en el archivo de configuracion\nmsg_display.valor_matricula_color = flow.get(\"valor_matricula_color\") // color del texto del valor de la Matricula y del ID. Definido en el archivo de configuracion\nmsg_display.men_m1_color = \"#cc0000\" // color del mensaje de \"peso estable\" en la ventana W2\nmsg_display.men_m2_color = \"#6B3C01\" // color del mensaje de \"puede mover el vehiculo\" en la ventana W2\nmsg_display.men_m3_color = \"#386B01\" // color del mensaje de \"acerque tarjeta de ID\" en la ventana W2\n\n//============================Procesamiento botones en pantalla ======================================================\nif (flow.get(\"totem_master\")) {\n    if (flow.get(\"startmenu_flag\")) { // check si tenemos que poner boton de solicitud de primer menu\n        if (flow.get(\"button\") != undefined) {\n            msg_display.button = flow.get(\"button\")\n        }\n        else {\n            msg_display.button = \"button1\"\n        }\n        if (flow.get(\"button1\") != undefined) {\n            msg_display.button1 = flow.get(\"button1\")\n        }\n        else {\n            msg_display.button1 = \"COMPLETAR INFO.\"\n        }\n        if (flow.get(\"button2\") != undefined) {\n            msg_display.button2 = flow.get(\"button2\")\n        }\n        else {\n            msg_display.button2 = \"OK.\"\n        }\n        if (flow.get(\"men_linea1\") != undefined) {\n            msg_display.men_m1 = flow.get(\"men_linea1\")\n        }\n        else {\n            msg_display.men_m1 = \"Haga CLICK ABAJO\"\n        }\n        if (flow.get(\"men_linea2\") != undefined) {\n            msg_display.men_m2 = flow.get(\"men_linea2\")\n        }\n        else {\n            msg_display.men_m2 = \"\"\n        }\n\n        msg_display.men_m1_color = flow.get(\"men_linea1_color\")\n        msg_display.men_m2_color = flow.get(\"men_linea2_color\")\n        //flow.set(\"startmenu_flag\", false) // boton de solicitud de primer menu ejecutado\n        // Este flag \"startmenu_flag\" debe ser puesto a false luego de haber sacado el menu o pasado la tarjeta\n        // ya que si se pone a false antes de tocar el boton y viene otro valor de peso el boton desaparece de la pantalla\n    }\n    else {\n        msg_display.button = \"message\" // indica que no pone boton sino que escribe mensajes \n    }\n    // ==================================================================================================================\n\n    //------------------------------ BOTON de VALIDACION ----------------------------------------------------------------\n    if (flow.get(\"botonsalida\")) { // check si tenemos que poner boton de validacion a la salida. Si movimiento final siempre lo ponemos\n\n        msg_display.button = \"button3\" // hace display de un boton de validacion\n\n        // ------------ chequea si tenemos color del background del boton de validacion ---------------\n        if (flow.get(\"botonsalida_color\") != undefined) {\n            msg_display.botonvalidacion_background = flow.get(\"botonsalida_color\")\n        }\n        else {\n            msg_display.botonvalidacion_background = flow.get(\"botonsalida_color_def\")\n        }\n\n        // ------------ chequea si tenemos color del texto del boton de validacion -----------------\n        if (flow.get(\"botonsalida_txt_color\") != undefined) {\n            msg_display.botonvalidacion_foreground = flow.get(\"botonsalida_txt_color\")\n        }\n        else {\n            msg_display.botonvalidacion_foreground = flow.get(\"botonsalida_txt_color_def\")\n        }\n\n        // ------------ chequea si tenemos texto del boton de validacion -----------------\n        if (flow.get(\"botonsalida_txt\") != undefined) {\n            msg_display.button3 = flow.get(\"botonsalida_txt\")\n        }\n        else {\n            msg_display.button3 = flow.get(\"botonsalida_txt_def\")\n        }\n\n        // ------------ chequea si tenemos texto del mensaje antes del boton de validacion -----------------  \n        if (flow.get(\"botonsalida_mensaje1\") != undefined) {\n            msg_display.men_m1 = flow.get(\"botonsalida_mensaje1\")\n        }\n        else {\n            msg_display.men_m1 = flow.get(\"botonsalida_mensaje1_def\")\n        }\n\n\n    }\n\n}\n\n// ==================================================================================================================\nnode.send([msg_display, null, null, null])\n\n//  ****************gestion del relay del semaforo****************\nif (flow.get(\"semaforo\") == \"green\") {\n    node.send([null, null, { \"payload\": 1 }, null]);\n}\nelse {\n    node.send([null, null, { \"payload\": 0 }, null]);\n}\n//*****************************************************************\n\nreturn;",
        "outputs": 5,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1890,
        "y": 1160,
        "wires": [
            [
                "338d3caabc377186",
                "641f679340b3e774",
                "2f88b8abeda0f377",
                "d4f51c3e79849570",
                "06832f8daa7c54f9",
                "f2736d54833e6a3c"
            ],
            [
                "a862be7fbfd8827a"
            ],
            [
                "422897281c1c5ef6",
                "6af84585bb71f167"
            ],
            [
                "6530f7fdb887dbf1",
                "b2149025f64c76c7"
            ],
            [
                "7cb744ca5e4180bf"
            ]
        ]
    },
    {
        "id": "e0b4dd6107f95f28",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "entrada_analisis",
        "links": [
            "0c96e404f0aeec9c",
            "20fe9f7863d4d4ec",
            "38dc1312c5833a43",
            "91a3eb404d0e121c",
            "dba345c891c1fb51",
            "e8d66667ae2598db",
            "22fabd2601b01d02",
            "55be72af5b5206e5",
            "5d188f7a3a6eaa62",
            "185e340354acf892",
            "1c98099a9069bb98",
            "077ea8c7ce8cec99"
        ],
        "x": 625,
        "y": 1120,
        "wires": [
            [
                "b890981ca603e6e1",
                "bb6e7b4ac0e9a9f0"
            ]
        ]
    },
    {
        "id": "422897281c1c5ef6",
        "type": "rpi-gpio out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "semaforo",
        "pin": "17",
        "set": true,
        "level": "1",
        "freq": "",
        "out": "out",
        "bcm": true,
        "x": 2200,
        "y": 1240,
        "wires": []
    },
    {
        "id": "610afee2981890f5",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "7",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 150,
        "y": 1100,
        "wires": [
            [
                "bf6a26e8fa632175"
            ]
        ]
    },
    {
        "id": "bf6a26e8fa632175",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "init screen",
        "func": "if (flow.get(\"init_screen\")){\n    msg = {payload:{}}\n    msg.payload.totem_id = \"*\"\n    msg.payload.numero_vial = flow.get(\"numero_vial\")\n    msg.payload.peso = 0\n    msg.payload.peso_estable = 0\n    msg.payload.peso_estable_flag = false\n    flow.set(\"movimiento_registrado_flag\", false)\n    flow.set(\"init_screen\", false)\n    node.send(msg)\n}\nreturn;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 310,
        "y": 1100,
        "wires": [
            [
                "197dfc68.458e54"
            ]
        ]
    },
    {
        "id": "6c4fcf9dad031bf8",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "init_config_command",
        "mode": "link",
        "links": [
            "da672c95637f91bd"
        ],
        "x": 735,
        "y": 1340,
        "wires": []
    },
    {
        "id": "b7b5cf32f7eea205",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "semaforo",
        "links": [
            "cd806a312b8e7e6e"
        ],
        "x": 2085,
        "y": 1260,
        "wires": [
            [
                "422897281c1c5ef6",
                "6af84585bb71f167"
            ]
        ]
    },
    {
        "id": "780c6c61.a0cb04",
        "type": "exec",
        "z": "953e8740401d175c",
        "g": "f4b1c07e498c11be",
        "command": " ",
        "addpay": "payload",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "oldrc": false,
        "name": "Screen ON/OFF",
        "x": 1040,
        "y": 1580,
        "wires": [
            [],
            [],
            []
        ]
    },
    {
        "id": "49500a91.3971e4",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "f4b1c07e498c11be",
        "name": "screen off",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "echo 1 | sudo tee /sys/class/backlight/0-0045/bl_power",
        "payloadType": "str",
        "x": 820,
        "y": 1560,
        "wires": [
            [
                "780c6c61.a0cb04"
            ]
        ]
    },
    {
        "id": "97ec61bc.7e2aa",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "f4b1c07e498c11be",
        "name": "screen on",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "echo 0 | sudo tee /sys/class/backlight/0-0045/bl_power",
        "payloadType": "str",
        "x": 820,
        "y": 1520,
        "wires": [
            [
                "780c6c61.a0cb04"
            ]
        ]
    },
    {
        "id": "afb26e9306481251",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "f4b1c07e498c11be",
        "name": "reboot",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "sudo shutdown -r now",
        "payloadType": "str",
        "x": 810,
        "y": 1600,
        "wires": [
            [
                "780c6c61.a0cb04"
            ]
        ]
    },
    {
        "id": "2c73d02c99e64b37",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "f4b1c07e498c11be",
        "name": "poweroff",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "sudo poweroff",
        "payloadType": "str",
        "x": 820,
        "y": 1640,
        "wires": [
            [
                "780c6c61.a0cb04"
            ]
        ]
    },
    {
        "id": "cc25836d7a7314ad",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "f4b1c07e498c11be",
        "name": "reboot",
        "links": [
            "07212ec8d039ced0",
            "7ae4043cc5314f0b"
        ],
        "x": 915,
        "y": 1500,
        "wires": [
            [
                "780c6c61.a0cb04"
            ]
        ]
    },
    {
        "id": "b890981ca603e6e1",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "entrada de datos",
        "mode": "link",
        "links": [
            "2b68b52db6cc1683",
            "9667a055f9c967a7"
        ],
        "x": 725,
        "y": 1060,
        "wires": []
    },
    {
        "id": "6530f7fdb887dbf1",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "fin pesada",
        "mode": "link",
        "links": [
            "f47c019e5baa0206"
        ],
        "x": 2155,
        "y": 1420,
        "wires": []
    },
    {
        "id": "07212ec8d039ced0",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "exec",
        "mode": "link",
        "links": [
            "cc25836d7a7314ad"
        ],
        "x": 735,
        "y": 1300,
        "wires": []
    },
    {
        "id": "3f7b861dd5cd6b93",
        "type": "comment",
        "z": "953e8740401d175c",
        "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": 580,
        "y": 1440,
        "wires": []
    },
    {
        "id": "bb6e7b4ac0e9a9f0",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "Peso",
        "func": "let peso = flow.get(\"peso\") || 0;\nconst peso_minimo = flow.get(\"peso_minimo\");\nlet peso_estable = flow.get(\"peso_estable\");\nlet semaforo = flow.get(\"semaforo\");\n\nif (peso < peso_minimo) {\n    // evento de bascula OFF para cuando el totem no dispone de sensor remoto\n    /* eliminamos este trigger para el evento de la bascula\n    if (peso < (peso_minimo * 0.8) && flow.get(\"peso_trigger_ON\")) {// consideramos una histeresis\n        flow.set(\"peso_trigger_ON\", false) // marcamos que el trigger ON esta en FALSE para proxima activacion de la bascula\n        node.send([null, null, null, { payload: 1, topic: \"peso_OFF\" }, null, null, null, null])\n    }\n    */\n    //===========================================================================\n    if(!flow.get(\"peso_bajo_flag\")){ // Se cumple solo una vez al vaciar la bascula\n        \n        flow.set(\"matricula_image\", \"\") // inicializamos imagen del vehiculo\n        flow.set(\"plate_sent\", false)\n        flow.set(\"anpr1_flag\", false)\n        flow.set(\"anpr0_flag\", true)\n        \n        // Esto corresponde al nodo \"espera\" puesto ahi para el caso en que la imagen llegue antes que el evento de bascula en el caso de dos totems\n        flow.set(\"interval_matricula\", null);\n        flow.set(\"interval_running\", false);\n        flow.set(\"order_sent\", false);\n        flow.set(\"peso_bajo_flag_forzado\", false) // indica que se ha forzado el estado del peso de la bascula para poder resetear las variables\n        //----------------------------------------------------------------------------------------------------------------------------\n        flow.set(\"matricula_revisada\", false)\n        flow.set(\"matricula_autorizada\", false)\n        flow.set(\"last_firma\", \"\") // inicializamos imagen de la firma\n        flow.set(\"identificacion_pendiente\", false) // estas cuatro lineas son relativas a la identificacion especial para cuando no se puede leer la matricula\n        flow.set(\"identificacion_esp_aplicada\", false) // indica que la matricula no fue bien leida y se aplico el parametro de identificacion especial\n        flow.set(\"QR_revisado\", false)\n        flow.set(\"RFID_revisado\", false)\n        flow.set(\"QR_verificado\", false)\n        flow.set(\"RFID_verificado\", false)\n        flow.set(\"ID_obligatorio_tmp\", false) // usada en caso que ID_obligatorio = false pero usuario presenta un QR. Esta variable permite asegurar que se autoriza entrada solo si QR es verificado en BD. Ver nodo check json en la parte de RESPUESTA DE REVISION DEL CODIGO QR EN BD\n        flow.set(\"ID_presentado\", false) // usado cuando el ID no es obligatorio pero el usuario presenta un ID\n        flow.set(\"peso_bajo_flag\", true)\n        flow.set(\"matricula_leida_flag\", false)\n        flow.set(\"matricula_formato_flag\", false)\n        flow.set(\"snapshot_rq_flag\", false)\n        flow.set(\"lista_array\", [])\n        flow.set(\"menu0\", [])\n        flow.set(\"menu1\", [])\n        flow.set(\"menu2\", [])\n        flow.set(\"menu_seleccionado_flag\", false)// indicador de que el usuario termino la seleccion en el menu y se envio\n        flow.set(\"id_seleccionado\", \"no recibido\")\n        flow.set(\"mostrar_ubicacion_ok\", false)\n        flow.set(\"ubicacion_recibida\", false)\n        flow.set(\"ubicacion_interna\", false)\n        flow.set(\"startmenu_flag\", false) // reset del flag que permite mostrar el boton (\"completar informacion\") de acceso al menu de materiales\n        flow.set(\"editor_matricula_ok\", false) // se activa cuando el usuario hace clik en OK para ir a  editar la matricula\n        flow.set(\"badplate_llamado\", false) // se activa cuando se saca por primera vez la notificacion para EDITRA MATRICULA en la siguiente pantalla\n        flow.set(\"nota_modifique_matricula_done\", false) // nota al usuario cuando hay que modificar matricula no registrada\n        flow.set(\"nota_vuelva_intentar_done\", false) // nota al usuario despues de corregir la matricula en caso de error\n        flow.set(\"nota_avise_basculista_done\", false) // nota al usuario de aviso al basculista\n        flow.set(\"nota_resumen_mostrada\", false) // indica que la nota de resumen luego de presentar identificacion ya se mostro (nodo QR / RFID)\n        flow.set(\"totem_master\", false) // flag para NO declarar peso estable si lo encontramos en false  (ver nodo \"detector rx\") y para no enviar movimientos a Base de datos ni a servidor\n        flow.set(\"matricula\", \"Sin Matricula\")\n        flow.set(\"nombre_imagen\", \"sin-nombre-de-imagen\")\n        flow.set(\"nombre_contexto\", \"\") //inicializamos nombre de la imagen de la camara de contexto en vacio\n        flow.set(\"last_camara_contexto\", \"\") // Inicializamos contenido de la imagen de contexto\n        flow.set(\"badplate\", false)\n        //flow.set(\"distance_flag_SET\", false) // Si dejamos esta inicializacion puede bloquear el detector de vehiculos y dejarlo en estado Ocupado\n        //Poner este flag en false mientras el detector esta contando los pulsos de salida hace que no pueda entrar en la zona de puesta en cero (OFF)\n        flow.set(\"semaforoabierto\", true) // flag para gestion del semaforo desde websocket\n        //Atributos del vehiculo, leidos de la BD al momento de revisar si la matricula existe en BD\n        flow.set(\"pma\", 0)\n        flow.set(\"tara\", 0)\n        flow.set(\"tipo_movimiento\", \"nodefinido\")\n        flow.set(\"register\", {})\n        flow.set(\"movimiento_enviado\", false)\n        flow.set(\"matriculacheck_solicitada\", false) // flag utilizado para no repetir el envio de la revision de matricula en el nodo Matricula\n        flow.set(\"display_detector\", false) // flag que permite ver el valor del detector de objetos en la pantalla de configuracion. Se pone en true al entrar en el menus de Probar Sensor\n        \n        if (flow.get(\"switch_2_Totem_page\")) { //En caso que este flag haya sido habilitado en configuracion \n            //eliminamos cualquier posible ventana de notificacion que haya quedado abierta y cambiamos a la ventana principal de Totem en caso de haber quedado otra ventana abierta\n    \n            node.send([null, null, null, null, null, { payload: \"\" }, { \"payload\": { \"tab\": \"Totem\" } }, null]);\n        }\n        else {\n            //eliminamos cualquier posible ventana de notificacion que haya quedado abierta\n            node.send([null, null, null, null, null, { payload: \"\" }, null, null]);\n        }\n    }\n    //==========================================================================================================================\n    flow.set(\"peso_estable_flag\", false)\n    flow.set(\"peso_estable_audio_flag\", true)\n    flow.set(\"peso_estable\", 0)\n    flow.set(\"fin_pesada_flag\", false)\n    //flow.set(\"totem_master\", false) // flag para NO declarar peso estable si lo encontramos en false  (ver nodo \"detector rx\") y para no enviar movimientos a Base de datos ni a servidor\n    if (flow.get(\"totem_qty\") == 1) { flow.set(\"totem_master\", true) } // en el caso de un solo totem. Este dispositivo siempre sera el master\n    //flow.set(\"firma_activa\", false)\n    flow.set(\"QR_ok_flag\", false);\n    flow.set(\"RFID_ok_flag\", false);\n    flow.set(\"last_QR\", \"-\")\n    flow.set(\"last_RFID\", \"-\")\n    flow.set(\"botonsalida\", false)\n    flow.set(\"vehiculo_en_posicion\", false)\n    flow.set(\"salida_valida_flag\", false)\n    \n    flow.set(\"aviso_pma_hecho\", false) // reset del flag indicador de PMA\n    //flow.set(\"matricula_leida_flag\", false)\n    //flow.set(\"matricula_formato_flag\", false)\n    flow.set(\"matricula_ok_flag\", false)\n    flow.set(\"matricula_editada_flag\", false)\n    //flow.set(\"matricula\", \"Sin Matricula\")\n    //flow.set(\"snapshot_rq_flag\", false)\n    //flow.set(\"nombre_imagen\", \"sin-nombre-de-imagen\")\n    //flow.set(\"badplate\", false)\n    flow.set(\"firma_ok_flag\", false) // firma recibida\n    flow.set(\"firma_flag\", false) // solicitud de firma\n    flow.set(\"movimiento_registrado_flag\", false)\n    if (flow.get((\"semaforoabierto\"))){ // si el semaforo ha sido manipulado desde websocket entonces no lo tocamos\n        flow.set(\"semaforo\", \"green\")\n    }\n    flow.set(\"window_men_m1\", \"\");   // borra posibles mensajes en pantalla \n    flow.set(\"window_men_m2\", \"\");                      \n    flow.set(\"window_men_m3\", \"\");\n    flow.set(\"window_id_m4\", \"\");\n    flow.set(\"window_id_m2\", \"\");\n    \n    \n}\nelse {// ************************ peso > pmin *****************************************\n    if (flow.get(\"peso_bajo_flag\")) {\n        flow.set(\"peso_min_time\", new Date().getTime()) // marca momento en que se detecta peso > peso min\n        //node.warn(\"peso_min_time = \" + flow.get(\"peso_min_time\"))\n        flow.set(\"horaentrada\", new Date(flow.get(\"peso_min_time\")).toLocaleString()) // Convierte a formato legible\n        // evento de bascula ON para cuando hay un solo Totem en la bascula (sin sensor remoto)\n        /*\n        if (flow.get(\"peso_trigger_ON\") == false) {\n            flow.set(\"peso_trigger_ON\", true) // \n            node.send([null, null, null, { payload: 1, topic: \"peso_ON\" }, null, null, null, null])\n        }\n        */\n        node.send([null, null, null, { payload: 1, topic: \"peso_ON\" }, null, null, null, null]) // sacamos evento de bascula ON cuando no hay sensor remoto\n        //flow.set(\"matricula_leida_flag\", false)\n        //flow.set(\"matricula_formato_flag\", false)\n        flow.set(\"matricula\", \"Sin Matricula\")\n        flow.set(\"matricula_ok_flag\", false)\n        flow.set(\"badplate\", false) // inicializamos flag en caso que haya sido enviado cuando peso < pmin \n        flow.set(\"vehiculo_presente_flag\", true);\n        flow.set(\"ID_enviado\", false) // activamos flag para envio de un unico RFID / QR con peso > pmin\n        flow.set(\"esperando_id\", true)\n        flow.set(\"peso_bajo_flag\", false);\n\n        flow.set(\"semaforo\", \"red\")\n        node.send([{ payload: 1 }, null, null, null, null, null, null, null]) // generamos un nuevo UUID\n        setTimeout(function () {\n            let new_register = {\n            uuid: flow.get(\"new_uuid\"),\n            horaentrada: flow.get(\"horaentrada\"),\n            vial: flow.get(\"numero_vial\"),\n            totem: flow.get(\"totem_id\"),\n            sincro: false,\n            modo: flow.get(\"modo_totem\"),\n            firmaobligatoria: flow.get(\"firma_obligatoria\"),\n            id_seleccionado: \"no recibido\",\n            nombre_firma: \"No Habilitada\",\n            qr: \"-\",\n            rfid: \"-\",\n            matricula: \"Sin Matricula\",\n            nombre_imagen: \"Sin Matricula\",\n            sentido: \"No conocido\"\n            }\n            flow.set(\"register\", new_register)\n        }, 1000)\n\n        if (flow.get(\"switch_2_Totem_page\")) { // si ha sido habilitado cambiamos a la ventana principal de Totem en caso de haber quedado otra ventana abierta\n            node.send([null, null, null, null, null, null, { \"payload\": { \"tab\": \"Totem\" } }, null]);\n        }\n        \n        if ((flow.get(\"modo_totem\") == \"C\" || flow.get(\"modo_totem\") == \"D\" ) && !flow.get(\"supervisor\")){\n            if(flow.get(\"botonsalida_activo\")){\n                flow.set(\"botonsalida\", true)\n            }\n        }\n    }\n    if (flow.get(\"peso_estable_flag\")) {\n        flow.set(\"peso\", flow.get(\"peso_estable\"));\n    }   \n\n    \n    //-------------------------------procesamiento de la lista de materiales-----------------------------------------------------\n    //if (flow.get(\"lista_flag\") && flow.get(\"supervisor\")) {\n    if (flow.get(\"lista_flag\")) {\n        flow.set(\"lista_flag\", false) // evita otra entrada a esta secuencia para esta misma lista\n        var lista = flow.get(\"lista_array\") // lista con todos los materiales y niveles juntos\n        const lista_size = flow.get(\"lista_array\").length\n        let j = 0; let k = 0; let i = 0; let maxlevel = 0;\n        for (i = 0; i < lista_size; i++) { // calculamos el nivel maximo que tiene esta lista de menu\n            if (parseInt(lista[i].nivel) > maxlevel) {\n                maxlevel = parseInt(lista[i].nivel)\n            }\n        }\n        flow.set(\"maxlevel\", maxlevel + 1) // guardamos el maximo nivel de menu identificado en la lista\n        flow.set(\"selections\", \"\") //inicializamos con ninguna seleccion\n        if (flow.get(\"menu_inicio\")) {\n            flow.set(\"startmenu_flag\", true)\n        }\n\n    }\n\n        //----------------------------------- FIN Lista de Materiales -----------------------------------------------------------------------\n}\n\nreturn ([null, null, null, null, msg, null, null, null]);\n",
        "outputs": 7,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 790,
        "y": 1180,
        "wires": [
            [
                "3e708dc4202384a1"
            ],
            [],
            [
                "4a10854d5d2302af"
            ],
            [
                "2f57175a6c859157"
            ],
            [
                "658bbf0ae0c6d8de",
                "0ebfa0099db1ed19",
                "76471be6630c1c3c",
                "41b9eef871e59dd2"
            ],
            [
                "944c9f4b068e0946",
                "4ab4a211f9ce33d9"
            ],
            [
                "83a963e6bc339f0e"
            ]
        ]
    },
    {
        "id": "db3e9975c8f562b6",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "Analisis del modo \\n de Operacion",
        "func": "//***************************** Selecciona Modo de Operacion **************************************************\nif (!flow.get(\"supervisor\")) {\n    if (flow.get(\"peso_estable_flag\")) { //El peso estable se obtiene cuando el vehiculo esta frente al Totem (QR / RFID o sino Detector Local ON o bien pulsar un boton que aparezca en pantalla)\n        if (flow.get(\"tipo_movimiento\") == \"final\") {\n            flow.set(\"firma_ok_flag\", true) // indicamos que la firma esta hecha, esta no hace falta para la salida\n        }\n        let ID_verificado = flow.get(\"QR_verificado\") || flow.get(\"RFID_verificado\")\n        if (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)))) { //chequea si tenemos que solicitar firma\n            /*\n            El if anterior se cumple si:\n                 - firma_obligatoria es true y firma_ok_flag es false y firma_flag es false y si supervisor es false y ademas si menu_activo es false o bien menu_activo es true y menu_seleccionado_flag es true (osea se ha seleccionado el menu)\n                Con esto aseguramos que la firma la sacamos una vez que el usuario ha hecho la seleccion del menu (en caso de existir) y no antes.\n            */\n            // hemos cambiado \"firma_habilitada\" por \"firma_obligatoria\" porque finalmente con un solo parametro es suficiente\n            // finalmente hacemos el llamado a la firma solamente durante el movimiento inicial\n            if (flow.get(\"tipo_movimiento\") == \"inicial\") {\n                flow.set(\"firma_flag\", true) // solicitud de firma\n                setTimeout(() => {\n                    //node.warn(\"solicitando firma\")\n                    node.send([{ \"payload\": { \"tab\": \"Firma\" } }, { payload: \"firma\" }, null, null, {payload:\"\"}])\n                }, 2000);\n            }\n            /*\n            else if (flow.get(\"tipo_movimiento\") == \"final\") {\n                flow.set(\"firma_ok_flag\", true) // indicamos que la firma esta hecha, esta no hace falta para la salida\n            }\n            */\n        }\n\n        //=============================================================================================================================================\n        //====================================================== MODO DE OPERACION ====================================================================\n        //=============================================================================================================================================\n        let modo = flow.get(\"modo_totem\")\n        // -----------------------------------------------------COMUN PARA TODOS LOS MODOS ------------------------------------------------------------\n        let detector_flag = (flow.get(\"distance_flag_SET\") && (flow.get(\"sentido\") == \"entrada\") && flow.get(\"sensor_distancia_local\")) || false;\n        let boton_flag = flow.get(\"salida_valida_flag\") || false;\n        let matricula_flag = flow.get(\"matricula_ok_flag\") || false;\n        let register_flag = flow.get(\"movimiento_registrado_flag\"); //si true, indica que el movimiento ha sido enviado\n        let firma_flag = flow.get(\"firma_ok_flag\")\n        let menu_activo = flow.get(\"menu_activo\") // el menu activo se configura en el archivo de configuracion\n        let menu_selected = flow.get(\"menu_seleccionado_flag\") // este flag se activa cuando el conductor selecciona el residuo del menu y lo envia (en caso de menu_activo = true)\n        //let ID_verificado = flow.get(\"QR_verificado\") || flow.get(\"RFID_verificado\")\n        \n        // Reglas de obligatoriedad configurables en archivo de configuracion\n        let ID_presentado = flow.get(\"ID_presentado\")\n        let ID_obligatorio = flow.get(\"ID_obligatorio\") || (flow.get(\"ID_obligatorio_tmp\") || false)\n        let detector_obligatorio = flow.get(\"detector_obligatorio\") || false; // este es el detector de presencia de vehiculo frente al totem\n        let boton_obligatorio = flow.get(\"boton_obligatorio\") || false; // Este corresponde al boton de la pantalla de Validacion en caso de estar habilitado\n        let firma_obligatoria = flow.get(\"firma_obligatoria\") || false;\n        //let matricula_obligatoria = flow.get(\"matricula_obligatoria\") || false\n        let matricula_obligatoria = flow.get(\"matricula_obligatoria\") || true // siempre es obligatoria. Es el punto central para gestionar elos movimientos en BD\n        let imagen_contexto_ok = flow.get(\"camara_contexto_rx\") || !flow.get(\"camara_contexto\") || flow.get(\"camara_contexto_estado\") === \"ok\" || flow.get(\"camara_contexto_estado\") === \"fail\"\n        //Se evalua flow.get(\"camara_contexto_estado\")-> si es \"ok\" significa que se recibio la imagen. Si es \"fail\" significa que se agotaron los dos tiempos de espera de los dos intentos\n        //los valores posibles de \"camara_contexto_estado\" son \"idle\", waiting1\", \"waiting2\", \"ok\", \"fail\".\n        //idle = no se ha iniciado ninguna solicitud de imagen de contexto. \"waiting1\" = Se inicio la primera solicitud de imagen de contexto. \"waiting2\" = Se inicio la segunda solicitud\n        //\"ok\" = imagen de contexto recibida. \"fail\" = se enviaron dos intentos y se esperaron los dos tiempos previstos pero no se recibio imagen de contexto.\n        \n        // Evaluación de cumplimiento de Flags actuales y Flags obligatorios\n        //let ID_ok = !ID_obligatorio || ID_verificado\n        let ID_ok = (!ID_obligatorio && !ID_presentado) || ID_verificado;\n        let detector_ok = !detector_obligatorio || detector_flag;\n        if (flow.get(\"tipo_movimiento\") == \"inicial\") { boton_flag = true } // en movimiento inicial no tiene sentido el boton\n        let boton_ok = !boton_obligatorio || boton_flag;\n        let firma_ok = !firma_obligatoria || firma_flag;\n        //**************************************************** Excepcion para Trajeta RFID **************************************************\n        //*********************************** No es necesario tener la matricula del Vehiculo ni poner boton de salida *******************************\n        if (flow.get(\"RFID_verificado\")) { \n            matricula_flag = true \n            boton_ok = true\n        }\n        \n        let matricula_ok = !matricula_obligatoria || matricula_flag\n        let seleccion_ok\n        if(flow.get(\"tipo_movimiento\") == \"inicial\"){\n            seleccion_ok = !menu_activo || menu_selected || ID_verificado // Si el menu esta activo, el ID_verificado permite saltar la seleccion del residuo\n\n            if (ID_verificado && menu_activo) {\n                // ==================== eliminamos mensaje y boton del menu debido a que tenemos ID del residuo (Tarjeta o QR) ================================\n                flow.set(\"startmenu_flag\", false) // para poder eliminar el boton y borrar el mensaje em \"window_men_m1\"\n                flow.set(\"window_men_m1\", \"\") // colocamos mensaje vacio en el espacio del mensaje 1 donde decia Haga click Abajo\n            }\n        }\n        else if(flow.get(\"tipo_movimiento\") == \"final\"){\n            //seleccion_ok = !menu_activo || menu_selected || ID_verificado || boton_flag\n            seleccion_ok =  true\n            ID_ok = !flow.get(\"salida_ID_obligatorio\") || ID_verificado\n        }\n        \n        //************************ SOLICITUD DE UBICACION INTERNA Y NOTIFICACION AL CONDUCTOR ***************************************/\n        // ****** OJO AQUI ***** Mientras \"ubicacion_recibida\" no cambie a true, es decir hasta que la BD no conteste el request, esto seguira enviando el request nuevamente\n        // Es decir que si se pierde comunicacion esto enviara siempre que pueda entrar en este codigo.\n        let requerimientos_ok = ID_ok && boton_ok && firma_ok && matricula_ok && seleccion_ok && flow.get(\"mostrar_ubicacion\") && !flow.get(\"mostrar_ubicacion_ok\") && (flow.get(\"tipo_movimiento\") === \"inicial\")\n        //node.warn(\"requerimientos_ok = \" + requerimientos_ok)\n        //node.warn(\"seleccion = \" + seleccion_ok + \" ; matricula = \" + matricula_ok + \" ; boton = \" + boton_ok + \" ; firma = \" + firma_ok + \" ; mostrar_ubicacion = \" + flow.get(\"mostrar_ubicacion\") + \" ; movimiento = \" + (flow.get(\"tipo_movimiento\") === \"inicial\"))\n                \n        if (requerimientos_ok) {\n            if (!flow.get(\"ubicacion_recibida\")) { // Si aun no hemos recibido el aviso de la ubicacion, la solicitamos\n                let id_residuo\n                if (flow.get(\"last_QR\") != \"-\") { id_residuo = flow.get(\"last_QR\") }\n                else if (flow.get(\"last_RFID\") != \"-\") { id_residuo = flow.get(\"last_RFID\") }\n                else if (flow.get(\"id_seleccionado\") != \"no recibido\") { id_residuo = flow.get(\"id_seleccionado\") }\n                msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), device: \"supervisor\", accion: \"check_ubicacion\", id_residuo: id_residuo }\n                node.send([null, null, msg, null])\n                //node.warn(\"enviado solicitud de ubicacion\")\n            }\n\n            //if (flow.get(\"ubicacion_recibida\")) { // Con flow.get(\"mostrar_ubicacion_ok\") aseguramos que solo sacamos la notificacion una vez\n            else {\n                //Aqui sacamos notificacion al conductor\n\n                flow.set(\"mostrar_ubicacion_ok\", true) //marcamos que ya hemos recibido respuesta del ubicacion\n                if (!flow.get(\"ubicacion_interna\")) { // si no se ha recibido el flag de ubicacion interna en true es que no existe ubicacion especifica para ese residuo\n                    flow.set(\"dirigirse\", \"NO HAY UBICACION DEFINIDA\")\n                }\n                //node.warn(\"DIRIGIRSE A : \" + flow.get(\"dirigirse\"))// aqui enviamos la notificacion al conductor\n                // esperamos un tiempo para que este mensaje quede de ultimo y no sea sustituido por la notificacion del contenido y procedencia\n                setTimeout(() => { \n                    node.send([null, null, null, null, {\n                        payload:\n                            \"<div style='padding:0; margin:0; line-height:1.1;'>\" +\n                            \"<p style='margin:0; padding:0; font-size:42px; color:#ff0000; font-weight:bold;'>ATENCION</p>\" +\n                            \"<p style='margin:0; padding:20px; font-size:32px; color:#000000;'>\" + flow.get(\"dirigirse\") + \"</p>\" +\n                            \"</div>\"\n                    }])\n                }, 2000);\n                \n\n            }\n        }\n        if (flow.get(\"tipo_movimiento\") === \"final\") { flow.set(\"mostrar_ubicacion_ok\", true) }\n        let ubicacion_ok = !flow.get(\"mostrar_ubicacion\") || flow.get(\"mostrar_ubicacion_ok\")\n        //******************************************************************************************************************************************************** */\n        \n        \n        let todos_obligatorios_ok;\n\n        switch (modo) {\n\n            case \"A\": //************************************** MODO A [ Matricula - QR / RFID - sensor local - boton de salida ]***************************************************** */\n                // aqui revisamos si se recibio matricula y un evento que indique que el conductor esta frente al totem\n                // puede ser alguno de los siguientes eslementos:\n                // - un QR o una tarjeta RFID\n                // - el boton de activacion de salida que ponemos en pantalla en caso de estar habilitado para ello en el archivo de configuración\n                // - el sensor local de presencia de vehiculo\n                // - la seleccion del menu\n                // El flag movimiento_registrado_flag se revisa para asegurar que se registra una sola vez al cumplir las condiciones\n                // El registro queda en memoria, este se pasa a BD solamente al momento que se active el semaforo en verde\n                // Adidionalmente se revisa si existe algun parametro OBLIGATORIO (presente en la configuracion ) que se deba recibir antes de dar por finalizada la pesada. \n                // Un evento obligatorio podria ser La tarjeta RFID o el QR o pulsar el boton de Validacion\n\n                //******************************** AQUI PONDREMOS EL ANALISIS DE LA RX DE LA IMAGEN DE CONTEXTO ********************************************** */\n                \n\n                todos_obligatorios_ok = ID_ok && detector_ok && boton_ok && seleccion_ok && !register_flag && firma_ok && matricula_ok && ubicacion_ok && imagen_contexto_ok;\n                //OJO \"register_flag\" debe ser false lo cual indica que aun no hemos enviado el movimiento.\n                node.warn(\"todos obligatorios = \" + todos_obligatorios_ok)\n                node.warn(\"ID_ok = \" + ID_ok + \" ; detector_ok = \" + detector_ok + \" ; register_flag = \" + register_flag + \" ; seleccion = \" + seleccion_ok + \n                    \" ; matricula = \" + matricula_ok + \" ; boton = \" + boton_ok + \" ; ubicacion = \" + ubicacion_ok + \" ; imagen_contexto = \" + imagen_contexto_ok)\n                \n                if (todos_obligatorios_ok) { // Cerramos el registro con los parametros hasta ahora recogidos\n                    //Agregamos al registro los nuevos datos recogidos\n                    //node.warn(\"matricula ok\")\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.nombre_contexto = flow.get(\"nombre_contexto\") // el nombre se asigna al recibir la imagen en el nodo \"convert to base64\"\n                    /*\n                    if (flow.get(\"tipo_movimiento\") == \"inicial\"){\n                        new_register.qr = flow.get(\"last_QR\")\n                    }\n                    else if (flow.get(\"tipo_movimiento\") == \"final\"){\n                        new_register.qrsalida = flow.get(\"last_QR\")\n                    }\n                    */\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                    if (flow.get(\"firma_obligatoria\") && flow.get(\"firma_ok_flag\")) {\n                        new_register.nombre_firma = flow.get(\"nombre_firma\")\n                        new_register.firmaobligatoria = true\n                    }\n                    else {\n                        new_register.nombre_firma = \"No Habilitada\"\n                        new_register.firmaobligatoria = false\n                    }\n                    if (flow.get(\"camara_contexto\")) {\n                        new_register.imagen_contexto = flow.get(\"last_camara_contexto\")\n                    }\n                    if (flow.get(\"menu_activo\")){\n                        new_register.id_seleccionado = flow.get(\"id_seleccionado\")\n                    }\n                    if (!flow.get(\"movimiento_enviado\")){\n                        flow.set(\"register\", new_register)\n                        flow.set(\"movimiento_registrado_flag\", true)\n                        flow.set(\"fin_pesada_flag\", true)\n                    }\n                }\n                //------------------------------------------------------------------------------------------------------------------------------------------\n                //node.warn(\"revisando QR / RFID flags\")\n                \n                if (flow.get(\"QR_verificado\") || flow.get(\"RFID_verificado\") || !flow.get(\"ID_obligatorio\")) {\n                    flow.set(\"window_men_m3\", \"\");\n                }\n                else {\n                    //node.warn(\"sacamos mensaje de identificacion\")\n                    flow.set(\"window_men_m3\", flow.get(\"men_identificacion\")); // solicitamos por pantalla una identificación de tipo QR o RFID\n                }//-----------------------------------------------------------------------------------------------------------------------------------------\n                break;\n\n            case \"B\": //************************************** MODO B [ Matricula - sensor local - boton de salida ] *****************************************************\n                // aqui revisamos si se recibio matricula y un evento que indique que el conductor esta frente al totem\n                // puede ser alguno de los siguientes elementos:\n                // - el boton de activacion de salida que ponemos en pantalla en caso de estar habilitado para ello en el archivo de configuración\n                // - el sensor local de presencia de vehiculo\n                // El flag movimiento_registrado_flag se revisa para asegurar que se registra una sola vez al cumplir las condiciones\n                // El registro queda en memoria, este se pasa a BD solamente al momento se actibve el semaforo en verde.\n                // Adidionalmente se revisa si existe algun parametro OBLIGATORIO que se deba recibir antes de dar por finalizada la pesada. \n                // Un evento obligatorio podria ser La tarjeta RFID o el QR o pulsar el boton de Validacion\n\n                todos_obligatorios_ok = detector_ok && boton_ok && seleccion_ok && !register_flag && firma_ok && matricula_ok && ubicacion_ok;\n                if (todos_obligatorios_ok) { // Cerramos el registro con los parametros hasta ahora recogidos\n                    //Agregamos al registro los nuevos datos recogidos\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 = \"Sin Lector de QR\"\n                    new_register.rfid = \"Sin Lector de RFID\"\n                    new_register.sentido = flow.get(\"sentido\")\n                    new_register.botonvalidacion = flow.get(\"salida_valida_flag\")\n                    if (flow.get(\"firma_obligatoria\") && flow.get(\"firma_ok_flag\")) {\n                        new_register.nombre_firma = \"firma-\" + flow.get(\"nombre_imagen\")\n                        new_register.firmaobligatoria = true\n                    }\n                    else {\n                        new_register.nombre_firma = \"No Habilitada\"\n                        new_register.firmaobligatoria = false\n                    }\n                    if (flow.get(\"menu_activo\")) {\n                        new_register.id_seleccionado = flow.get(\"id_seleccionado\")\n                    }\n                    flow.set(\"register\", new_register)\n                    flow.set(\"movimiento_registrado_flag\", true)\n                    flow.set(\"fin_pesada_flag\", true)\n                }\n                break;\n            case \"C\":  //************************************** MODO C [ QR / RFID - sensor local - boton de salida ]*****************************************************\n                // aqui revisamos si se recibio un evento que indique que el conductor esta frente al totem\n                // puede ser alguno de los siguientes eslementos:\n                // - un QR o una tarjeta RFID\n                // - el boton de activacion de salida que ponemos en pantalla en caso de estar habilitado para ello en el archivo de configuración\n                // - el sensor local de presencia de vehiculo\n                // El flag movimiento_registrado_flag se revisa para asegurar que se registra una sola vez al cumplir las condiciones\n                // El registro queda en memoria, este se pasa a BD solamente al momento que se active el semaforo en verde.\n                // Adidionalmente se revisa si existe algun parametro OBLIGATORIO que se deba recibir antes de dar por finalizada la pesada. \n                // Un evento obligatorio podria ser La tarjeta RFID o el QR o pulsar el boton de Validacion\n\n                todos_obligatorios_ok = ID_ok && detector_ok && boton_ok && seleccion_ok && !register_flag && firma_ok && ubicacion_ok;\n                if (todos_obligatorios_ok) { // Cerramos el registro con los parametros hasta ahora recogidos\n\n                    //Agregamos al registro los nuevos datos recogidos\n                    let new_register = flow.get(\"register\")\n                    new_register.matricula = \"Sin Camara\"\n                    new_register.matriculaeditada = false\n                    new_register.nombre_imagen = \"Sin Camara\"\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                    if (flow.get(\"firma_obligatoria\") && flow.get(\"firma_ok_flag\")) {\n                        new_register.nombre_firma = \"firma-\" + flow.get(\"nombre_imagen\")\n                        new_register.firmaobligatoria = true\n                    }\n                    else {\n                        new_register.nombre_firma = \"No Habilitada\"\n                        new_register.firmaobligatoria = false\n                    }\n                    if (flow.get(\"menu_activo\")) {\n                        new_register.id_seleccionado = flow.get(\"id_seleccionado\")\n                    }\n                    flow.set(\"register\", new_register)\n                    flow.set(\"movimiento_registrado_flag\", true)\n                    flow.set(\"fin_pesada_flag\", true)\n                }\n                //------------------------------------------------------------------------------------------------------------------------------------------\n                if (flow.get(\"QR_verificado\") || flow.get(\"RFID_verificado\") || !flow.get(\"ID_obligatorio\")) {\n                    flow.set(\"window_men_m3\", \"\");\n                }\n                else {\n                    flow.set(\"window_men_m3\", flow.get(\"men_identificacion\"));\n                }//-----------------------------------------------------------------------------------------------------------------------------------------\n\n                break;\n            case \"D\":  //************************************** MODO D [boton de salida ]*****************************************************\n                // aqui revisamos si se recibio el boton de validacion que indica que el conductor esta frente al totem\n                // El flag movimiento_registrado_flag se revisa para asegurar que se registra una sola vez al cumplir las condiciones\n                // El registro queda en memoria, este se pasa a BD solamente al momento que el semaforo se active en verde.\n                boton_flag = flow.get(\"salida_valida_flag\") || false;\n\n                boton_obligatorio = seleccion_ok && (flow.get(\"boton_obligatorio\") || false); // Este corresponde al boton de la pantalla de Validacion en caso de estar habilitado\n                register_flag = flow.get(\"movimiento_registrado_flag\")\n\n                // Evaluación de cumplimiento de Flags actuales y Flags obligatorios\n\n                boton_ok = !boton_obligatorio || boton_flag;\n\n                todos_obligatorios_ok = boton_ok && !register_flag && firma_ok && ubicacion_ok;\n                if (todos_obligatorios_ok) { // Cerramos el registro con los parametros hasta ahora recogidos\n\n                    //Agregamos al registro los nuevos datos recogidos\n                    let new_register = flow.get(\"register\")\n                    new_register.matricula = \"Sin Camara\"\n                    new_register.matriculaeditada = false\n                    new_register.nombre_imagen = \"Sin Camara\"\n                    new_register.qr = \"Sin Lector de QR\"\n                    new_register.rfid = \"Sin Lector de RFID\"\n                    new_register.sentido = flow.get(\"sentido\")\n                    new_register.botonvalidacion = flow.get(\"salida_valida_flag\")\n                    if (flow.get(\"firma_obligatoria\") && flow.get(\"firma_ok_flag\")) {\n                        new_register.nombre_firma = \"firma-\" + flow.get(\"nombre_imagen\")\n                        new_register.firmaobligatoria = true\n                    }\n                    else {\n                        new_register.nombre_firma = \"No Habilitada\"\n                        new_register.firmaobligatoria = false\n                    }\n                    if (flow.get(\"menu_activo\")) {\n                        new_register.id_seleccionado = flow.get(\"id_seleccionado\")\n                    }\n                    flow.set(\"register\", new_register)\n                    flow.set(\"movimiento_registrado_flag\", true)\n                    flow.set(\"fin_pesada_flag\", true)\n                }\n\n                break;\n\n        }// termina el codigo del SWITCH CASE\n        //*************************************************** PREPARACION Y ENVIO DEL MOVIMIENTO *******************************************************\n        if (flow.get(\"movimiento_registrado_flag\") && !flow.get(\"supervisor\") && flow.get(\"totem_master\") && (flow.get(\"tipo_movimiento\") != \"nodefinido\")&& !flow.get(\"movimiento_enviado\")) {\n            let date = new Date();\n            let timestamp = parseInt(date.getTime() / 1000);\n            flow.set(\"horasalida\", new Date(date.getTime()).toLocaleString()) // Convierte a formato legible\n\n            let new_register = flow.get(\"register\")\n            new_register.horasalida = flow.get(\"horasalida\")\n            new_register.pesoestable = flow.get(\"peso_estable\")\n            new_register.timestamp = timestamp\n            //hay que revisar imagen, QR y RFID ya que no en todos los casos estan presentes\n\n            let obj = new_register;\n            delete obj.sincro\n            new_register = obj\n            new_register.imagen = flow.get(\"matricula_image\")\n            if (flow.get(\"firma_obligatoria\")) { // cambiamos \"firma_habilitada\" por \"firma_obligatoria\"\n                new_register.firma = flow.get(\"last_firma\")\n            }\n            if(flow.get(\"camara_contexto\")){\n                new_register.imagen_contexto = flow.get(\"last_camara_contexto\")\n            }\n\n            flow.set(\"movimiento_registrado_flag\", false) // evitamos que podamos entrar en este espacio hasta nuevo registro\n\n            // *********************** preparamos json con movimiento para enviar al supervisor local de la bascula *****************\n            let msg_mov = { payload: {} }\n            new_register.tara = flow.get(\"tara\")\n            new_register.destareautomatico = flow.get(\"destareautomatico\")\n            msg_mov.payload.device = \"supervisor\"\n            msg_mov.payload.totem_id = flow.get(\"totem_id\")\n            msg_mov.payload.accion = \"movimiento\"\n            //msg_mov.payload.tipo = flow.get(\"modelo\") // entrada / salida\n            msg_mov.payload.tipomovimiento = flow.get(\"tipo_movimiento\")\n            //msg_mov.payload.urlapi = flow.get(\"url_API\")\n            msg_mov.payload.movimiento = new_register // enviamos el registro del movimiento con las imagenes de matricula y de firma\n            flow.set(\"movimiento_enviado\", true)\n            flow.set(\"new_register\", {}) // inicializamos el registro para evitar cualquier posible duplicado en bd\n            node.send([null, null, msg_mov, null])\n        }\n\n    }\n}\nreturn ([null, null, null, msg, null])",
        "outputs": 5,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1650,
        "y": 1140,
        "wires": [
            [
                "5263b792c0037321"
            ],
            [
                "fab29e0f6601e531"
            ],
            [
                "b6a2b0d90858876e"
            ],
            [
                "48eda297.9cdedc"
            ],
            [
                "7cb744ca5e4180bf"
            ]
        ]
    },
    {
        "id": "5263b792c0037321",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "salida firma",
        "mode": "link",
        "links": [
            "8356acc61776cae7"
        ],
        "x": 1845,
        "y": 1020,
        "wires": []
    },
    {
        "id": "fab29e0f6601e531",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "salida audio",
        "mode": "link",
        "links": [
            "f47c019e5baa0206"
        ],
        "x": 1845,
        "y": 1060,
        "wires": []
    },
    {
        "id": "a862be7fbfd8827a",
        "type": "rpi-gpio out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "barrera",
        "pin": "27",
        "set": true,
        "level": "0",
        "freq": "",
        "out": "out",
        "bcm": true,
        "x": 2220,
        "y": 1180,
        "wires": []
    },
    {
        "id": "77d23eabe6e7d642",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "badplate",
        "mode": "link",
        "links": [
            "0df085dfc5184645"
        ],
        "x": 1435,
        "y": 1240,
        "wires": []
    },
    {
        "id": "3e708dc4202384a1",
        "type": "createrandom",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "generator": "UUID",
        "type_generator": "generator",
        "label": "UUID",
        "param_1": "v4",
        "type_param_1": "Version",
        "param_2": "",
        "type_param_2": "Name",
        "param_3": "",
        "type_param_3": "Namespace",
        "f_use_debug": false,
        "x": 990,
        "y": 960,
        "wires": [
            [
                "95f3d3aa2f768a9e"
            ]
        ]
    },
    {
        "id": "95f3d3aa2f768a9e",
        "type": "change",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "set UUID",
        "rules": [
            {
                "t": "set",
                "p": "new_uuid",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1120,
        "y": 960,
        "wires": [
            []
        ]
    },
    {
        "id": "2f57175a6c859157",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "evento bascula",
        "mode": "link",
        "links": [
            "d3223fe2481ad802"
        ],
        "x": 965,
        "y": 1000,
        "wires": []
    },
    {
        "id": "5420d37f8224c191",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "audio out",
        "mode": "link",
        "links": [
            "f47c019e5baa0206"
        ],
        "x": 1135,
        "y": 1120,
        "wires": []
    },
    {
        "id": "944c9f4b068e0946",
        "type": "ui_toast",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": true,
        "outputs": 1,
        "ok": "OK",
        "cancel": "",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "edit plate",
        "x": 1320,
        "y": 1240,
        "wires": [
            [
                "77d23eabe6e7d642"
            ]
        ]
    },
    {
        "id": "83a963e6bc339f0e",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "control_ui",
        "mode": "link",
        "links": [
            "8356acc61776cae7"
        ],
        "x": 965,
        "y": 1380,
        "wires": []
    },
    {
        "id": "4a10854d5d2302af",
        "type": "file",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "save firma",
        "filename": "filename",
        "filenameType": "msg",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1150,
        "y": 1040,
        "wires": [
            []
        ]
    },
    {
        "id": "76471be6630c1c3c",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "estable",
        "func": "//****************************************Chequeo de Peso Estable ********************************************\n// El peso estable es el peso leido en cualquier de los siguientes momentos:\n// 1. Al Recibir un QR o RFID\n// 2. Cuando el Detector de presencia en el Totem es ON (distance_flag_SET)\n// 3. Cuando se activa el Boton de Validacion (salida_valida_flag true)\n// 4. Cuando el usuario envia la seleccion del menu en caso de que este habilitado (vehiculo_en_posicion)\n// 5. Cuando el usuario acepta el editor de matricula (editor_matricula_ok)\n\n\nconst en_posicion = ((flow.get(\"distance_flag_SET\") || flow.get(\"QR_ok_flag\") || flow.get(\"RFID_ok_flag\") || flow.get(\"salida_valida_flag\") || flow.get(\"vehiculo_en_posicion\")) || flow.get(\"editor_matricula_ok\"))\n//if (((flow.get(\"distance_flag_SET\") || flow.get(\"QR_ok_flag\") || flow.get(\"RFID_ok_flag\") || flow.get(\"salida_valida_flag\") || flow.get(\"vehiculo_en_posicion\")) || flow.get(\"editor_matricula_ok\")) && !flow.get(\"peso_bajo_flag\") && !flow.get(\"peso_estable_flag\") && flow.get(\"totem_master\") && !flow.get(\"supervisor\")) {//aseguramos se hace una sola vez durante la pesada\n\nif (en_posicion && !flow.get(\"peso_bajo_flag\") && !flow.get(\"peso_estable_flag\") && flow.get(\"totem_master\") && !flow.get(\"supervisor\")) {//aseguramos se hace una sola vez durante la pesada\n    flow.set(\"peso_estable\", flow.get(\"peso\"))\n    //node.warn(\"peso = \" + flow.get(\"peso_estable\") + \" pma = \" + flow.get(\"pma\") + \"pma - peso = \" + (flow.get(\"pma\") - flow.get(\"peso_estable\")))\n    // chequeamos si el peso del vehiculo es inferior al Peso Maximo Autorizado (PMA)\n    if ((flow.get(\"pma\") > 0 && (flow.get(\"pma\") - flow.get(\"peso_estable\")) < 0)) {\n\n        if (!flow.get(\"aviso_pma_hecho\")) {\n            node.send([null, null, { payload: \"<p style='font-size:50px;color:#ff0000;'>PESO EXCEDIDO </p><p style='font-size:30px;color:#000000;'> SALGA DE LA BASCULA </p>\" }])\n            flow.set(\"aviso_pma_hecho\", true) // hacemos el aviso una sola vez en la pesada\n        }\n    }\n    else {\n        if (!flow.get(\"peso_estable_flag\")) { // solo emitimos audio y mensaje una sola vez durante la misma pesada\n            flow.set(\"peso_estable_flag\", true)\n            node.send([{ payload: \"peso_estable\" }, null]) // activa audio de peso estable\n            flow.set(\"window_men_m1\", flow.get(\"men_peso_estable\")); //se envia mensaje de Peso estable en pantalla\n        }\n\n    }\n\n    /*********************************************************************************************************************************************************************\n     *                                                      PROCESAMIENTO DE LA CAMARA DE CONTEXTO\n     * ******************************************************************************************************************************************************************* */\n    // Evitar relanzar si ya hay un proceso en curso aunque aqui solamente se accede una vez durante la pesada (cuando esl peso pasa a estable)\n    const estadoActual = flow.get(\"camara_contexto_estado\") || \"idle\";\n    if (estadoActual === \"waiting_1\" || estadoActual === \"waiting_2\") {\n        return null;\n    }\n    // Limpiar timers viejos por seguridad\n    const oldT1 = flow.get(\"camara_contexto_timer1\");\n    if (oldT1) clearTimeout(oldT1);\n    const oldT2 = flow.get(\"camara_contexto_timer2\");\n    if (oldT2) clearTimeout(oldT2);\n\n    // Nuevo ciclo\n    // La variable reqId permanece dentro de los callbacks (SetTimeout1 y SetTimeout2)\n    const reqId = Date.now().toString() + \"_\" + Math.floor(Math.random() * 100000);\n    // Inicialización de estado\n    flow.set(\"camara_contexto_reqid\", reqId);\n    flow.set(\"camara_contexto_estado\", \"waiting_1\");\n    flow.set(\"camara_contexto_intento\", 1);\n    flow.set(\"camara_contexto_rx\", false);\n    flow.set(\"camara_contexto_timer1\", null);\n    flow.set(\"camara_contexto_timer2\", null);\n    // --------- MENSAJE PARA REQUEST 1 ---------\n    let msgHttp1 = RED.util.cloneMessage(msg);\n    msgHttp1.contexto_reqid = reqId;\n    msgHttp1.contexto_intento = 1;\n    msgHttp1.camara_contexto_rq = 1;\n    msgHttp1.url = `http://${flow.get(\"camara_contexto_IP\")}/cgi-bin/snapshot.cgi?channel=1`;\n\n\n    // ------------------------------------------- TIMEOUT 1 DE LA CAMARA DE CONTEXTO -------------------------------------------------------\n    const t1 = setTimeout(() => {\n        const reqIdActual = flow.get(\"camara_contexto_reqid\");\n        const estado = flow.get(\"camara_contexto_estado\");\n        const rx = flow.get(\"camara_contexto_rx\");\n\n        // Si este timer ya no corresponde al ciclo actual, ignorar\n        if (reqIdActual !== reqId) return;\n\n        // Si ya llegó imagen, no hacer nada\n        if (estado === \"ok\" || rx === true) return;\n\n        // Lanzamos intento 2 en caso que no haya llegado la imagen anterior\n        flow.set(\"camara_contexto_estado\", \"waiting_2\");\n        flow.set(\"camara_contexto_intento\", 2);\n        flow.set(\"camara_contexto_rx\", false);\n\n        let retryMsg = {\n            _source: \"timeout1_retry\",\n            contexto_reqid: reqId,\n            contexto_intento: 2,\n            camara_contexto_rq: 2,\n            url: `http://${flow.get(\"camara_contexto_IP\")}/cgi-bin/snapshot.cgi?channel=1`\n        };\n\n        // Enviamos solicitud 2 desde el propio timer\n        node.send([null, null, null, retryMsg, null]);\n        //============================================================= FIN DEL TIMEOUT 1 ==============================================================\n\n        // ------------------------------------------- TIMEOUT 2 DE LA CAMARA DE CONTEXTO --------------------------------------------------------------\n        const t2 = setTimeout(() => {\n            const reqIdActual2 = flow.get(\"camara_contexto_reqid\");\n            const estado2 = flow.get(\"camara_contexto_estado\");\n            const rx2 = flow.get(\"camara_contexto_rx\");\n\n            // Si este timer ya no corresponde al ciclo actual, ignorar\n            if (reqIdActual2 !== reqId) return;\n\n            // Si ya llegó imagen, no hacer nada\n            if (estado2 === \"ok\" || rx2 === true) return;\n\n            // Fallo definitivo\n            flow.set(\"camara_contexto_estado\", \"fail\");\n            flow.set(\"camara_contexto_intento\", 2);\n            flow.set(\"camara_contexto_rx\", false);\n            flow.set(\"camara_contexto_timer2\", null);\n\n            // Dejamos una traza\n            node.warn(\"No se recibió imagen de contexto ni en intento 1 ni en intento 2\");\n\n            //emitir un mensaje de continuación sin imagen\n            node.send([null, { payload: 1 }, null, null, {\n                _source: \"contexto_fail_final\",\n                contexto_reqid: reqId,\n                camara_contexto_estado: \"fail\"\n            }]);\n\n        }, flow.get(\"camara_contexto_delay1\"));\n\n        flow.set(\"camara_contexto_timer2\", t2);\n\n    }, flow.get(\"camara_contexto_delay1\"));\n\n    flow.set(\"camara_contexto_timer1\", t1);\n\n    // salida 4 -> hacia http request para solocitar imagen de contexto\n\n    return [null, null, null, msgHttp1, null];\n\n\n    //Solicitamos Imagen de la camara de contexto\n    /*\n        if (flow.get(\"camara_contexto\")){\n            let msg_camara_contexto = {}\n            msg_camara_contexto.url = `http://${flow.get(\"camara_contexto_IP\")}/cgi-bin/snapshot.cgi?channel=1`\n            flow.set(\"camara_contexto_rq\", 1) // marcamos primera solicitud de camara de contexto\n            flow.set(\"camara_contexto_rx\", false) // inicializamos flag de imagen no recibida de la camara de contexto\n            node.send ([null, null, null, msg_camara_contexto])\n            setTimeout(function () { // timer para revisar si recibimos respuesta de la camara de contexto\n                if (!flow.get(\"camara_contexto_rx\")) { // Si la imagen de contexto aun no ha sido recibida, solicitamos otra\n                    msg_camara_contexto.url = `http://${flow.get(\"camara_contexto_IP\")}/cgi-bin/snapshot.cgi?channel=1`\n                    flow.set(\"camara_contexto_rq\", 2) \n                    node.send([null, null, null, msg_camara_contexto])\n                }\n            }, flow.get(\"camara_contexto_delay\"))\n    \n        }\n    */\n}\nelse if (flow.get(\"supervisor\") && flow.get(\"peso_estable_flag\") && flow.get(\"peso_estable_audio_flag\")) {\n    flow.set(\"window_men_m1\", flow.get(\"men_peso_estable\")); //se envia mensaje de Peso estable en pantalla\n    flow.set(\"peso_estable_audio_flag\", false)\n    node.send([{ payload: \"peso_estable\" }, null]) // activa audio de peso estable\n}\n//*************************************************************************************************************\n\nreturn ([null, msg, null, null, null]);",
        "outputs": 5,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1000,
        "y": 1140,
        "wires": [
            [
                "5420d37f8224c191"
            ],
            [
                "db3e9975c8f562b6"
            ],
            [
                "f5343bf87806ece4"
            ],
            [
                "c79815b5a79ef60a"
            ],
            []
        ]
    },
    {
        "id": "0ebfa0099db1ed19",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "firma",
        "func": "//Este codigo es para guardar la imagen de la firma una vez realizada. Por eso se evalua \"firma_ok_flag\"\n//La firma se inicia desde el nodo Analisis del Modo de Operacion, una vez hayamos detectado que el peso es estable.\n//es decir que el vehiculo se encuentra frente al totem.\nif (flow.get(\"firma_obligatoria\") && flow.get(\"firma_ok_flag\") && !flow.get(\"supervisor\")) {\n    // hemos cambiado \"firma_habilitada\" por \"firma_obligatoria\"\n    let msg_firma = {}\n    msg_firma.payload = flow.get(\"last_firma\")\n    if ((flow.get(\"identificacion_esp\") && !flow.get(\"matricula_ok_flag\")) || flow.get(\"modo_totem\") == \"C\" || flow.get(\"modo_totem\") == \"D\") { // en estos casos  ( C y D ) no tenemos matricula, no podemos usar el nombre de la foto\n        // como parte del nombre de la firma. En el caso de identificación especial puede haber matricula o puede no haberla por eso evaluamos \"matricula_ok_flag\"\n        let now = new Date().getTime()\n        msg_firma.filename = flow.get(\"path_firmas\") + \"firma-Sin-Matricula-\" + now\n        flow.set(\"nombre_firma\", \"firma-Sin-Matricula-\" + now)\n    }\n    else {\n        msg_firma.filename = flow.get(\"path_firmas\") + \"firma-\" + flow.get(\"nombre_imagen\") //usamos el nombre de la foto porque tiene la matricula asociada en el.\n        flow.set(\"nombre_firma\", \"firma-\" + flow.get(\"nombre_imagen\"))\n    }\n    node.send(msg_firma)\n}\nreturn;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 990,
        "y": 1080,
        "wires": [
            [
                "4a10854d5d2302af"
            ]
        ]
    },
    {
        "id": "658bbf0ae0c6d8de",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "QR / RFID",
        "func": "\n//if (flow.get(\"identificacion_esp\") && flow.get(\"identificacion_pendiente\") && !flow.get(\"supervisor\")) {\nif (!flow.get(\"nota_resumen_mostrada\") && !flow.get(\"supervisor\")) {   \n    // hay que chequear identificacion contra algun tipo de autenticacion \n    if (flow.get(\"QR_ok_flag\")) {\n        flow.set(\"ID_presentado\", true)\n        if (!flow.get(\"QR_revisado\")) {\n            // Solicitamos revision del codigo QR en BD\n            msg.payload.numero_vial = flow.get(\"numero_vial\")\n            msg.payload.totem_id = flow.get(\"totem_id\")\n            msg.payload.device = \"supervisor\"\n            msg.payload.accion = \"qr/rfid\"\n            msg.payload.tipo = \"qr\"\n            msg.payload.id = flow.get(\"last_QR\")\n            node.send([{ payload: \"\" }, null, msg]);\n            \n        }\n        else {\n            if (flow.get(\"QR_verificado\")) {\n                flow.set(\"nota_resumen_mostrada\", true) // evita repeticion de la notificacion con el resumen\n                flow.set(\"identificacion_pendiente\", false)\n                let cliente = flow.get(\"cliente\") || \"Centro Desconocido\";\n                let residuo = flow.get(\"residuo\") || \"Servicio Desconocido\";\n                // Construir el mensaje en formato HTML\n                //msg.payload = `<p style='font-size:40px;color:#orange;'>EL CODIGO QR PRESENTADO</p><br>VIENE DE: <b>${nombreCentro}</b><br>TRAE: <b>${servicio}</b>`;\n                msg.payload = `<div style=\"margin:0;padding:0;line-height:1;\">\n                                <div style=\"font-size:80px;color:green;margin:0;line-height:1.2;\">CODIGO QR OK</div>\n                                <div style=\"font-size:30px;text-align:left;font-weight:bold;color:FireBrick;margin:5;line-height:1.5;\">VIENE DE: <b style=\"color:black;\">${cliente}</b></div>\n                                <div style=\"font-size:30px;text-align:left;font-weight:bold;color:FireBrick;margin:5;line-height:1.2;\">TRAE: <b style=\"color:black;\">${residuo}</b></div>\n                                </div>\n                                `;\n                                //<div style=\"font-size:30px;text-align:left;font-weight:bold;color:FireBrick;margin:5;line-height:1.2;\">Matricula: <b style=\"color:black;\">${matricula}</b></div>\n                \n                msg.topic = \"QR_verificado\";\n                //flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n                flow.set(\"startmenu_flag\", false) // Permite que el boton de solicitud de primer menu, en caso de estar en pantalla, se pueda eliminar\n                \n                if (flow.get(\"matricula_ok_flag\") && flow.get(\"tipo_movimiento\") == \"final\") {\n                    flow.set(\"botonsalida\", false) // eliminamos boton de salida y validamos el flag para simular que fue pulsado \n                    flow.set(\"salida_valida_flag\", true)\n                    return [null, null, null, null, {payload:1}]\n                }\n                \n                if (flow.get(\"matricula_ok_flag\")){\n                    node.send([msg, null, null])\n                }\n                else{\n                    const matricula_final = new Array(8) // Tamaño maximo de la matricula\n                    node.send([{payload:\"<p style='font-size:50px;color:#ff0000;'>SIN MATRICULA </p><p style='font-size:30px;color:#000000;'> MODIFIQUE EN LA SIGUIENTE PANTALLA </p>\", matricula:matricula_final, size:matricula_final.length}, null, null])\n                }\n                \n                /*\n                setTimeout(function () {\n                    node.send([{ payload: \"\" }, null, null]);\n                }, 3000)\n                */\n                //Eliminamos la pantalla del editor y ponemos la pantalla principal del Totem\n                if (flow.get(\"editor_matricula_ok\")) {\n                    node.send([null, null, null, { \"payload\": { \"tab\": \"Totem\" } }]);\n\n                }\n            }\n            else{\n                //aqui va una notificacion de QR no registrado y permitimos que se lea nuevamente la tarjeta\n                flow.set(\"QR_revisado\", false)\n                flow.set(\"ID_enviado\", false) // permitimos envio de otro ID. Este flag se utiliza en el nodo de \"envio QR / RFID\"\n                flow.set(\"QR_ok_flag\", false)\n                node.send([{ payload: \"<p style='font-size:50px;color:#ff0000;'>EL QR NO ESTA REGISTRADO </p><p style='font-size:30px;color:#000000;'> UTILICE OTRO QR </p>\" }, null, null])\n            }\n        }\n    }\n    else if (flow.get(\"RFID_ok_flag\")) {\n        flow.set(\"ID_presentado\", true)\n        if (!flow.get(\"RFID_revisado\")) {\n            // Solicitamos revision del codigo de la tarjeta en BD\n            msg.payload.numero_vial = flow.get(\"numero_vial\")\n            msg.payload.totem_id = flow.get(\"totem_id\")\n            msg.payload.device = \"supervisor\"\n            msg.payload.accion = \"qr/rfid\"\n            msg.payload.tipo = \"rfid\"\n            msg.payload.id = flow.get(\"last_RFID\")\n            node.send([{ payload: \"\" }, null, msg]);\n            \n        }\n        else{\n            if (flow.get(\"RFID_verificado\") && !flow.get(\"nota_resumen_mostrada\") && flow.get(\"matricula_revisada\")) {\n                flow.set(\"nota_resumen_mostrada\", true) //evita repeticion de la notificacion con el resumen\n                flow.set(\"identificacion_pendiente\", false)\n                let cliente = flow.get(\"cliente\") || \"Centro Desconocido\";\n                let residuo = flow.get(\"residuo\") || \"Servicio Desconocido\";\n\n                if (flow.get(\"matricula_ok_flag\") && flow.get(\"tipo_movimiento\") == \"final\") {\n                    flow.set(\"botonsalida\", false) // eliminamos boton de salida y validamos el flag para simular que fue pulsado \n                    flow.set(\"salida_valida_flag\", true)\n                    return [{payload:\"\"}, null, null, null, { payload: 1 }]\n                }\n                \n              // Construir la notificacion en formato HTML\n                //msg.payload = `<p style='font-size:60px;color:green;'>TARJETA  OK</p><p style='font-size:24px;color:black;'>VIENE DE: <b>${nombreCentro}</b></p><p style='font-size:24px;color:black;'>TRAE: <b>${servicio}</b></p>`;\n                msg.payload =   `<div style=\"margin:0;padding:0;line-height:1;\">\n                                <div style=\"font-size:80px;color:green;margin:0;line-height:1.2;\">Tarjeta OK</div>\n                                <div style=\"font-size:30px;text-align:left;font-weight:bold;color:FireBrick;margin:5;line-height:1.5;\">VIENE DE: <b style=\"color:black;\">${cliente}</b></div>\n                                <div style=\"font-size:30px;text-align:left;font-weight:bold;color:FireBrick;margin:5;line-height:1.2;\">TRAE: <b style=\"color:black;\">${residuo}</b></div>\n                                </div>\n                                `;\n                                //<div style=\"font-size:30px;text-align:left;font-weight:bold;color:FireBrick;margin:5;line-height:1.2;\">Matricula: <b style=\"color:black;\">${matricula}</b></div>\n                msg.topic = \"RFID_verificado\";\n                //flow.set(\"matricula_leida_flag\", false) // marcamos como si la matricula se hubiera leido ya que aplicamos identificacion especial\n                flow.set(\"startmenu_flag\", false) // Permite que el boton de solicitud de primer menu, en caso de estar en pantalla, se pueda eliminar\n                node.send([msg, null, null])\n                //node.warn(\"notificacion\")\n                /*\n                setTimeout(function () {\n                    node.send([{ payload: \"\" }, null, null]);\n                }, 3000)\n                */\n                //Eliminamos la pantalla del editor y ponemos la pantalla principal del Totem\n                if (flow.get(\"editor_matricula_ok\")) {\n                    node.send([null, null, null, { \"payload\": { \"tab\": \"Totem\" } }]);\n\n                }\n            }\n            else if (!flow.get(\"RFID_verificado\")) {\n                //aqui va una notificacion de tarjeta no registrada y permitimos que se lea nuevamente la tarjeta\n                flow.set(\"RFID_revisado\", false)\n                flow.set(\"RFID_ok_flag\", false)\n                flow.set(\"ID_enviado\", false) // permitimos envio de otro ID\n                node.send([{ payload: \"<p style='font-size:50px;color:#ff0000;'>LA TARJETA NO ESTA REGISTRADA </p><p style='font-size:30px;color:#000000;'> UTILICE OTRA TARJETA </p>\" }, null, null])\n            }\n        }\n    }\n    \n}\n/*\nif (flow.get(\"identificacion_esp\") && flow.get(\"identificacion_pendiente\") && !flow.get(\"supervisor\")){\n    // hay que chequear identificacion contra algun tipo de autenticacion \n    if(flow.get(\"QR_ok_flag\") && flow.get(\"last_QR\") == \"myQR is 12345678\"){\n        flow.set(\"QR_verificado\", true)\n        node.send({ payload: \"\" });\n    }\n    else if( flow.get(\"RFID_ok_flag\") && flow.get(\"last_RFID\") == 12345678){\n        flow.set(\"RFID_verificado\", true)\n        node.send({ payload: \"\" });\n    }\n}\n*/\nelse if(flow.get(\"supervisor\")){\n    msg = {}\n    if(msg.id_topic == \"QR\" && flow.get(\"QR_ok_flag\")){\n        msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), RFID: flow.get(\"last_QR\")}\n        node.send([null, msg, null])\n    }\n    else if(msg.id_topic == \"rfid\" && flow.get(\"RFID_ok_flag\")){\n        msg.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), RFID: flow.get(\"last_RFID\")}\n        node.send([null, msg, null])\n    }\n}\nreturn",
        "outputs": 5,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1010,
        "y": 1320,
        "wires": [
            [
                "944c9f4b068e0946"
            ],
            [
                "16994333f8deedd9"
            ],
            [
                "f75c248d56207759"
            ],
            [
                "efa47b4b532c3c95"
            ],
            [
                "077ea8c7ce8cec99"
            ]
        ]
    },
    {
        "id": "16994333f8deedd9",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "envio_QR",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 1185,
        "y": 1300,
        "wires": []
    },
    {
        "id": "5a4bb45aef7a1433",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "buscar en BD",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 1185,
        "y": 1260,
        "wires": []
    },
    {
        "id": "f75c248d56207759",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "check qr/rfid",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 1185,
        "y": 1340,
        "wires": []
    },
    {
        "id": "06832f8daa7c54f9",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "salida uibuilder",
        "mode": "link",
        "links": [
            "656a6ccd2e2d8874"
        ],
        "x": 2025,
        "y": 1040,
        "wires": []
    },
    {
        "id": "423023a153680e64",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "entrada json",
        "links": [
            "41b4309de63a6bba"
        ],
        "x": 335,
        "y": 1260,
        "wires": [
            [
                "197dfc68.458e54"
            ]
        ]
    },
    {
        "id": "6f9cc9fc71e8ca71",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "barrera",
        "links": [
            "6fca6204952f0b67",
            "cf7b0c00631682b2"
        ],
        "x": 2065,
        "y": 1200,
        "wires": [
            [
                "a862be7fbfd8827a"
            ]
        ]
    },
    {
        "id": "2f1a8bd6dd9ea0d5",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "invalid plate",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"Picture\":{\"CutoutPic\":{\"Content\":\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAgAGADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8/I7ixmYJHdQOzEABZVJP61Zu9OWxuprW4dYriHIliYEGIgkEN6Hj9a+0tM+BXwr1vVYrG6+E8FlHPuDXC3U6hRg8gAj9DXxY99Na+GdTRLiUJbvPFHJJ9/arsFLepwBz7V6dGjzo5ZT5THbXtIQlTdgEccRsf6U5dX0l13Le...\",\"PicName\":\"0751LWL-20240508183537-plate.jpg\"},\"NormalPic\":{\"Content\":\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgHBgYHDQ8MCQcGCQsUHCwcHQsIBQgKDBogIzA9MRwkHxkMBxQSHi4cNDgyNCgnHRMaIDc0PC4fKSIsJzM0OTL//gf+ARAA7gAAAAAAAAAAAAAAAAEAAAAwNzUxTFdMAAAAAAAAAAAAAAAAAAIAAAACAAAAMjAyNC0wNS0wOCAxODozNTozNwBGAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFN1ciBhIE5vcnRlAAAAAAAAAAAAPeoHAHwFAACwAgAAWQAAABgAAABhbm9ueW1pdHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/iT/474gcAAggAAAAAAAAAAAAAMTAuMTQ4LjExLjcxAAAAAAAAAAAAAwAARgAAAEYAAAA5QjAzRkU3UEFKMDAwMTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAAA5ShRYWYCAAAAAv8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQIBAAAAAFkIFBQxAAAAAAAAAAAAAAAAANoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFU1AAvwAAAAAAAAAAAAAAAABVbmtub3duAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...\",\"PicName\":\"0751LWL-20240508183537.jpg\"},\"Plate\":{\"BoundingBox\":[1404,688,1493,712],\"Channel\":0,\"IsExist\":true,\"PlateColor\":\"White\",\"PlateNumber\":\"222FPG\",\"PlateType\":\"\",\"UploadNum\":129},\"SnapInfo\":{\"AccurateTime\":\"2024-05-08 18:35:37.614\",\"AllowUser\":false,\"AllowUserEndTime\":\"\",\"DSTTune\":0,\"DefenceCode\":\"kieYKmmsy4M6QyAm\",\"DeviceID\":\"CAMARA_SALIDA\",\"InCarPeopleNum\":0,\"LanNo\":1,\"OpenStrobe\":false,\"SnapTime\":\"2024-05-08 18:35:37\",\"TimeZone\":1},\"Vehicle\":{\"VehicleSeries\":\"Otro\",\"VehicleSign\":\"GMC\"}}}",
        "payloadType": "json",
        "x": 2690,
        "y": 1480,
        "wires": [
            [
                "9db92332012fa977",
                "1ee7d9733d5bdccd"
            ]
        ]
    },
    {
        "id": "0e4b3c888740bdc2",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "valid plate 0524MKK",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"Picture\":{\"NormalPic\":{\"Content\":\"[encoded data, 187728 characters]\",\"PicName\":\"0524MKK-20240914065034.jpg\"},\"Plate\":{\"BoundingBox\":[0,0,0,0],\"Channel\":0,\"IsExist\":false,\"PlateColor\":\"\",\"PlateNumber\":\"0524MKK\",\"PlateType\":\"\",\"UploadNum\":1477},\"SnapInfo\":{\"AccurateTime\":\"2024-09-14 06:50:34.954\",\"AllowUser\":false,\"AllowUserEndTime\":\"\",\"DSTTune\":0,\"DefenceCode\":\"Oc40queEIoSueWso\",\"DeviceID\":\"CAMARA_ENTRADA\",\"InCarPeopleNum\":0,\"LanNo\":1,\"OpenStrobe\":false,\"SnapTime\":\"2024-09-14 06:50:34\",\"TimeZone\":1},\"Vehicle\":{\"VehicleSeries\":\"\"}}}",
        "payloadType": "json",
        "x": 2710,
        "y": 1520,
        "wires": [
            [
                "1ee7d9733d5bdccd",
                "9db92332012fa977"
            ]
        ]
    },
    {
        "id": "f12149b9937d1f9c",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "valid plate 8550MCK",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"Picture\":{\"NormalPic\":{\"Content\":\"[encoded data, 511136 characters]\",\"PicName\":\"8550MCK-20250303113806.jpg\"},\"Plate\":{\"BoundingBox\":[1078,647,1267,700],\"Channel\":0,\"IsExist\":true,\"PlateColor\":\"White\",\"PlateNumber\":\"8550MCK\",\"PlateType\":\"\",\"UploadNum\":1545},\"SnapInfo\":{\"AccurateTime\":\"2024-09-26 11:38:06.272\",\"AllowUser\":false,\"AllowUserEndTime\":\"\",\"DSTTune\":0,\"DefenceCode\":\"6a6CwhllFt2G55Z5\",\"DeviceID\":\"CAMARA_SALIDA\",\"InCarPeopleNum\":0,\"LanNo\":1,\"OpenStrobe\":false,\"SnapTime\":\"2024-09-26 11:38:06\",\"TimeZone\":1},\"Vehicle\":{\"VehicleSeries\":\"Otro\"}},\"Date\":\"03/3/2025, 11:37:45\"}",
        "payloadType": "json",
        "x": 2690,
        "y": 1560,
        "wires": [
            [
                "1ee7d9733d5bdccd",
                "9db92332012fa977"
            ]
        ]
    },
    {
        "id": "864ac0651127081b",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "valid plate 6499-MSY",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"Picture\":{\"NormalPic\":{\"Content\":\"[encoded data, 821656 characters]\",\"PicName\":\"6499MSY-20241003172520.jpg\"},\"Plate\":{\"BoundingBox\":[1141,779,1330,826],\"Channel\":0,\"IsExist\":true,\"PlateColor\":\"White\",\"PlateNumber\":\"6499MSY\",\"PlateType\":\"\",\"UploadNum\":789},\"SnapInfo\":{\"AccurateTime\":\"2024-10-03 17:25:20.310\",\"AllowUser\":false,\"AllowUserEndTime\":\"\",\"DSTTune\":0,\"DefenceCode\":\"osqVxqygL6Kz3B2I\",\"DeviceID\":\"CAMARA_ENTRADA\",\"InCarPeopleNum\":0,\"LanNo\":1,\"OpenStrobe\":false,\"SnapTime\":\"2024-10-03 17:25:20\",\"TimeZone\":1},\"Vehicle\":{\"VehicleSeries\":\"Otro\"}}}",
        "payloadType": "json",
        "x": 2700,
        "y": 1600,
        "wires": [
            [
                "1ee7d9733d5bdccd",
                "9db92332012fa977"
            ]
        ]
    },
    {
        "id": "6e7d311b8384ddbb",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "valid plate 0724HPH",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"Picture\":{\"NormalPic\":{\"Content\":\"[encoded data, 720212 characters]\",\"PicName\":\"0724HPH-20241003172709.jpg\"},\"Plate\":{\"BoundingBox\":[790,829,1047,901],\"Channel\":0,\"IsExist\":true,\"PlateColor\":\"White\",\"PlateNumber\":\"0724HPH\",\"PlateType\":\"\",\"UploadNum\":791},\"SnapInfo\":{\"AccurateTime\":\"2024-10-03 17:27:09.755\",\"AllowUser\":false,\"AllowUserEndTime\":\"\",\"DSTTune\":0,\"DefenceCode\":\"Ym0w7A9mUlD8k4pi\",\"DeviceID\":\"CAMARA_ENTRADA\",\"InCarPeopleNum\":0,\"LanNo\":1,\"OpenStrobe\":false,\"SnapTime\":\"2024-10-03 17:27:09\",\"TimeZone\":1},\"Vehicle\":{\"VehicleSeries\":\"Otro\"}}}",
        "payloadType": "json",
        "x": 2710,
        "y": 1640,
        "wires": [
            [
                "1ee7d9733d5bdccd",
                "9db92332012fa977"
            ]
        ]
    },
    {
        "id": "654f0bef9ed49b91",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "QR TEST",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "QR",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "84216BF7-E8F5-495F-87A7-1B7373074FB4",
        "payloadType": "str",
        "x": 2700,
        "y": 2040,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "25d47d91059e756a",
        "type": "change",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "distance_flag_SET",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "sentido",
                "pt": "flow",
                "to": "topic",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 2920,
        "y": 1980,
        "wires": [
            []
        ]
    },
    {
        "id": "9bd5c49ea39343ab",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Distance_set true",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "entrada",
        "payload": "true",
        "payloadType": "bool",
        "x": 2720,
        "y": 1940,
        "wires": [
            [
                "25d47d91059e756a",
                "0c96e404f0aeec9c"
            ]
        ]
    },
    {
        "id": "6b8e8d62fb919f6c",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Distance_set false",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "entrada",
        "payload": "false",
        "payloadType": "bool",
        "x": 2710,
        "y": 1980,
        "wires": [
            [
                "25d47d91059e756a"
            ]
        ]
    },
    {
        "id": "1ee7d9733d5bdccd",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "link out 5",
        "mode": "link",
        "links": [
            "7058b9e632aa584b"
        ],
        "x": 2995,
        "y": 1640,
        "wires": []
    },
    {
        "id": "9db92332012fa977",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "link out 6",
        "mode": "link",
        "links": [
            "34b97be83a87f482"
        ],
        "x": 2995,
        "y": 1540,
        "wires": []
    },
    {
        "id": "120a33aec5cdad35",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "link out 7",
        "mode": "link",
        "links": [
            "03abdad8b6e3e504"
        ],
        "x": 3045,
        "y": 2160,
        "wires": []
    },
    {
        "id": "bc10f90fd35fca2a",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Status",
        "func": "let flag3 = flow.get('QR_ok_flag') || flow.get(\"RFID_ok_flag\") || false;\nlet flag1 = flow.get('matricula_ok_flag') || false;\nlet flag5 = flow.get('peso_estable_flag') || false;\nlet flag2 = flow.get('distance_flag_SET') || false;\nlet flag6 = flow.get(\"movimiento_registrado_flag\") || false;\nlet flag4 = flow.get(\"salida_valida_flag\") || false;\nlet flag7 = flow.get(\"totem_master\") || false;\n\n// Creamos el texto con un dot verde o rojo por flag\nlet statusText = `MAT:${flag1 ? '🟢' : '🔴'} DIST:${flag2 ? '🟢' : '🔴'} QR:${flag3 ? '🟢' : '🔴'} VAL:${flag4 ? '🟢' : '🔴'} PE:${flag5 ? '🟢' : '🔴'} REG:${flag6 ? '🟢' : '🔴'} MAES:${flag7 ? '🟢' : '🔴'}`;\n\nnode.status({\n    fill: \"grey\",    \n    shape: \"dot\",    // Mantén el shape como \"dot\" para no romper estilo\n    text: statusText\n});\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2770,
        "y": 2380,
        "wires": [
            []
        ]
    },
    {
        "id": "2b68b52db6cc1683",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "status",
        "links": [
            "b890981ca603e6e1"
        ],
        "x": 2645,
        "y": 2380,
        "wires": [
            [
                "bc10f90fd35fca2a"
            ]
        ]
    },
    {
        "id": "0c96e404f0aeec9c",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "link out 9",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 2875,
        "y": 1940,
        "wires": []
    },
    {
        "id": "715237a531d3d520",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "RFID TEST",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "rfid",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "763cdffa",
        "payloadType": "str",
        "x": 2710,
        "y": 2340,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "46476ff7f3b16d50",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "valid plate 0397FHJ",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"Picture\":{\"NormalPic\":{\"Content\":\"[encoded data, 720212 characters]\",\"PicName\":\"0397FHJ-20241003172709.jpg\"},\"Plate\":{\"BoundingBox\":[790,829,1047,901],\"Channel\":0,\"IsExist\":true,\"PlateColor\":\"White\",\"PlateNumber\":\"0397FHJ\",\"PlateType\":\"\",\"UploadNum\":791},\"SnapInfo\":{\"AccurateTime\":\"2024-10-03 17:27:09.755\",\"AllowUser\":false,\"AllowUserEndTime\":\"\",\"DSTTune\":0,\"DefenceCode\":\"Ym0w7A9mUlD8k4pi\",\"DeviceID\":\"CAMARA_ENTRADA\",\"InCarPeopleNum\":0,\"LanNo\":1,\"OpenStrobe\":false,\"SnapTime\":\"2024-10-03 17:27:09\",\"TimeZone\":1},\"Vehicle\":{\"VehicleSeries\":\"Otro\"}}}",
        "payloadType": "json",
        "x": 2710,
        "y": 1680,
        "wires": [
            [
                "9db92332012fa977",
                "1ee7d9733d5bdccd"
            ]
        ]
    },
    {
        "id": "088db8eaaaf5641a",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Modo A = (QR or RFID or Distance or Boton)  and  Matricula",
        "info": "",
        "x": 2770,
        "y": 2480,
        "wires": []
    },
    {
        "id": "bcb4dca5a8f9ea87",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Modo B = (Distance or Boton)  and  Matricula",
        "info": "",
        "x": 2730,
        "y": 2520,
        "wires": []
    },
    {
        "id": "d28debfbf333767f",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Modo C = QR or RFID or Boton",
        "info": "",
        "x": 2690,
        "y": 2560,
        "wires": []
    },
    {
        "id": "e9ad5c9298c1658d",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Modo D = Boton",
        "info": "",
        "x": 2640,
        "y": 2600,
        "wires": []
    },
    {
        "id": "86102659d7bd05ca",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "QR ESP 1",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "QR",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "546DA2A4-C456-482D-B98F-B4EB0FFABB0E",
        "payloadType": "str",
        "x": 2700,
        "y": 2080,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "fefe878035fbe1d8",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "RFID ESP KO",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "RFID",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "12345678",
        "payloadType": "str",
        "x": 2710,
        "y": 2220,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "afba789e9a047452",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "Detector remoto",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "entrada",
        "payload": "{\"totem_id\":\"totem_2\",\"sentido\":\"Entrada en bascula\",\"topic\":\"detector_ON\",\"input_flag\":true}",
        "payloadType": "json",
        "x": 2700,
        "y": 1880,
        "wires": [
            [
                "de4c24109d3a3cec"
            ]
        ]
    },
    {
        "id": "de4c24109d3a3cec",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "salida detector",
        "mode": "link",
        "links": [
            "85fe3844a73d42e6"
        ],
        "x": 2895,
        "y": 1880,
        "wires": []
    },
    {
        "id": "e82fc95572128625",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "RFID ESP OK",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "RFID",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "763ccd5a",
        "payloadType": "str",
        "x": 2710,
        "y": 2260,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "5eada7a4d497bdf2",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "RFID ESP OK",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "RFID",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "b267461a",
        "payloadType": "str",
        "x": 2710,
        "y": 2300,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "2f66b472df7dae12",
        "type": "websocket out",
        "z": "953e8740401d175c",
        "g": "2245c4484f098470",
        "name": "servidor",
        "server": "",
        "client": "be0e1690e4e13e0d",
        "x": 1900,
        "y": 780,
        "wires": []
    },
    {
        "id": "fac0534420b62514",
        "type": "websocket out",
        "z": "953e8740401d175c",
        "g": "2245c4484f098470",
        "name": "ws/test para monitoreo",
        "server": "eb652223.5293e",
        "client": "",
        "x": 1940,
        "y": 840,
        "wires": []
    },
    {
        "id": "0df0c819dae1658d",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "2245c4484f098470",
        "name": "websocket_out",
        "links": [
            "7881be0e538c924f",
            "6b37f85b1ad9472f",
            "41cb4ca9c6c75818",
            "16994333f8deedd9",
            "da0422341e874eb1",
            "a1f46ee9a5bf3bd0",
            "b010f913d811a8e4",
            "5a4bb45aef7a1433",
            "f75c248d56207759",
            "b6a2b0d90858876e",
            "42499939644932e1",
            "a0f7956de8f33a0b"
        ],
        "x": 1725,
        "y": 820,
        "wires": [
            [
                "2f66b472df7dae12",
                "fac0534420b62514",
                "8db77186d4b23dbc"
            ]
        ]
    },
    {
        "id": "2ec53aa1784087c1",
        "type": "uibuilder",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "",
        "topic": "",
        "url": "totem",
        "okToGo": true,
        "fwdInMessages": false,
        "allowScripts": false,
        "allowStyles": false,
        "copyIndex": true,
        "templateFolder": "blank",
        "extTemplate": "",
        "showfolder": false,
        "reload": true,
        "sourceFolder": "src",
        "deployedVersion": "7.3.0",
        "showMsgUib": false,
        "title": "",
        "descr": "",
        "editurl": "vscode://vscode-remote/ssh-remote+192.168.0.29/home/trypton/.node-red/projects/Bascula_Autonoma_1/uibuilder/totem/?windowId=_blank",
        "x": 3860,
        "y": 2200,
        "wires": [
            [
                "be843dd4381d45d5",
                "ecaff4d18050a7c5"
            ],
            []
        ]
    },
    {
        "id": "b8fdc502b1bf35b7",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"payload\":{\"pantalla\":\"menu-principal\"}}",
        "payloadType": "json",
        "x": 3690,
        "y": 2080,
        "wires": [
            [
                "2ec53aa1784087c1"
            ]
        ]
    },
    {
        "id": "be843dd4381d45d5",
        "type": "function",
        "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[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,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 4100,
        "y": 2180,
        "wires": [
            [
                "88b9b4d1c5aa7359",
                "207488e79f57a0eb"
            ],
            [
                "6d39924d1d2081dd"
            ],
            [
                "cd806a312b8e7e6e"
            ],
            [
                "0dee226932d66a29"
            ],
            [
                "7ae4043cc5314f0b"
            ],
            [
                "72222dcbbfea7534"
            ],
            [
                "772b17b6e7954d5f"
            ],
            [
                "6fca6204952f0b67"
            ],
            [
                "ee22e8c1327a7b65"
            ]
        ]
    },
    {
        "id": "ecaff4d18050a7c5",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "debug 13",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4080,
        "y": 2080,
        "wires": []
    },
    {
        "id": "cd806a312b8e7e6e",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "set semaforo",
        "mode": "link",
        "links": [
            "b7b5cf32f7eea205"
        ],
        "x": 4315,
        "y": 2200,
        "wires": []
    },
    {
        "id": "88b9b4d1c5aa7359",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "feedback",
        "mode": "link",
        "links": [
            "7058b9e632aa584b"
        ],
        "x": 4265,
        "y": 2120,
        "wires": []
    },
    {
        "id": "7058b9e632aa584b",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "entrada uibuilder-1",
        "links": [
            "1ee7d9733d5bdccd",
            "88b9b4d1c5aa7359",
            "ade2db3a9cf2dab1",
            "b90652f4011dad39"
        ],
        "x": 3535,
        "y": 2180,
        "wires": [
            [
                "882ee24d1489b06c"
            ]
        ]
    },
    {
        "id": "207488e79f57a0eb",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "debug 14",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4300,
        "y": 2040,
        "wires": []
    },
    {
        "id": "0dee226932d66a29",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "aplicar_config",
        "mode": "link",
        "links": [
            "da672c95637f91bd"
        ],
        "x": 4315,
        "y": 2240,
        "wires": []
    },
    {
        "id": "7ae4043cc5314f0b",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "reiniciar",
        "mode": "link",
        "links": [
            "cc25836d7a7314ad"
        ],
        "x": 4315,
        "y": 2280,
        "wires": []
    },
    {
        "id": "882ee24d1489b06c",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "datos de\\n pesada y \\n snapshot",
        "func": "if(msg.payload.hasOwnProperty(\"tiposDatos\")){\n    return(msg)\n\n}\nelse { //Aqui analizamos las imagenes de las camaras y lo relacionado con los botones de configuracion para la visualizacion de camaras\n    let msgmix = {}\n    if (flow.get(\"snapshot_cam\") && msg.payload.hasOwnProperty(\"Picture\")) { // si recibimos snapshot extraemos informacion necesaria para mostrar en la pagina de snapshot\n        msgmix.plate = msg.payload.Picture.Plate.PlateNumber // guardamos el numero de matricula\n        msgmix.picture = \"data:image/png;base64,\" + msg.payload.Picture.NormalPic.Content\n        msgmix.plate_time = msg.payload.Picture.SnapInfo.SnapTime\n        msgmix.camera_id = msg.payload.Picture.SnapInfo.DeviceID\n    }\n    else if (flow.get(\"context_cam\") && msg.topic === \"context_rqtd\") { // si recibimos snapshot de camara de contexto\n        if (Buffer.isBuffer(msg.payload)) {\n            msg.payload = msg.payload.toString('base64');\n        }\n        msgmix.picture_contexto = \"data:image/png;base64,\" + msg.payload\n        let date = new Date();\n        msgmix.fecha_contexto = new Date(date.getTime()).toLocaleString()\n        \n        msg.payload = {}\n        \n    }\n\n\n    else if (flow.get(\"escuchar_cam\")) { // Si estamos esperando snapshots de la camara o acciones del usuario en botones left o right\n        let snapshot_array = flow.get('snapshot_array') || [];\n        let snapshot_readindex = flow.get('snapshot_readindex') || 0;\n        let snapshot_saveindex = flow.get('snapshot_saveindex') || 0;\n        let snapshot_indexmax = flow.get('snapshot_indexmax') || 0;\n\n\n        if (flow.get(\"getfromarray\") == \"left\") {\n            msgmix.buttons = true // indica al index.js (del uibuilder) que no tiene que inicializar la pantalla de escuchar camara\n            if (snapshot_readindex > 0) {\n                snapshot_readindex = snapshot_readindex - 1\n                flow.set(\"snapshot_readindex\", snapshot_readindex)\n                msgmix = snapshot_array[snapshot_readindex]\n                msgmix.snapshot_readindex = snapshot_readindex\n                msgmix.snapshot_indexmax = snapshot_indexmax\n            }\n        }\n        else if (flow.get(\"getfromarray\") == \"right\") {\n            msgmix.buttons = true // indica al index.js (del uibuilder) que no tiene que inicializar la pantalla de escuchar camara\n            if (snapshot_readindex < flow.get(\"snapshot_indexmax\") - 1) {\n                snapshot_readindex = snapshot_readindex + 1\n                flow.set(\"snapshot_readindex\", snapshot_readindex)\n                msgmix = snapshot_array[snapshot_readindex]\n                msgmix.snapshot_readindex = snapshot_readindex\n                msgmix.snapshot_indexmax = snapshot_indexmax\n\n            }\n        }\n        else {\n            if (msg.payload.hasOwnProperty(\"Picture\")) { // si recibimos snapshot extraemos informacion necesaria para mostrar en la pagina de escuchar camara\n                msgmix.plate = msg.payload.Picture.Plate.PlateNumber // guardamos el numero de matricula\n                msgmix.picture = \"data:image/png;base64,\" + msg.payload.Picture.NormalPic.Content\n                msgmix.plate_time = msg.payload.Picture.SnapInfo.SnapTime\n                msgmix.camera_id = msg.payload.Picture.SnapInfo.DeviceID\n                msgmix.buttons = true\n\n\n                snapshot_array[snapshot_saveindex] = RED.util.cloneMessage(msgmix);  // Clonamos para evitar referencias\n                flow.set(\"snapshot_array\", snapshot_array)\n                flow.set(\"snapshot_saveindex\", snapshot_saveindex + 1)\n                flow.set(\"snapshot_indexmax\", snapshot_indexmax + 1)\n\n                if (snapshot_readindex == snapshot_saveindex - 1) {\n                    flow.set(\"snapshot_readindex\", snapshot_readindex + 1) // en caso que el index de lectura este al final, se incrementa al llegar una nueva imagen\n                }\n                msgmix.snapshot_indexmax = flow.get(\"snapshot_indexmax\")\n                msgmix.snapshot_readindex = flow.get(\"snapshot_readindex\")\n            }\n        }\n    }\n\n    else {\n\n        let msgmix = {}\n\n    }\n    flow.set(\"getfromarray\", \"\")\n\n    //*****************************************Agrega datos de la pesada para PAGINA PRINCIPAL************************************************************ */\n    if (!msg.payload.hasOwnProperty(\"Picture\")) {\n        msgmix = Object.assign({}, msg, msgmix);  // msg sobrescribe valores si hay conflictos\n    }\n    msgmix.peso = flow.get(\"peso\")\n    msgmix.semaforo_color = flow.get(\"semaforo\")\n    msgmix.matricula = flow.get(\"matricula\")\n    msgmix.peso_estable_flag = flow.get(\"peso_estable_flag\")\n    msgmix.vial = flow.get(\"numero_vial\")\n    msgmix.totem_id = flow.get(\"totem_id\")\n    msgmix.iplan = flow.get(\"myip\")\n    msgmix.ipsupervisor = flow.get(\"websocket_url\")\n    msgmix.snapshot_cam = flow.get(\"snapshot_cam\")\n    msgmix.context_cam = flow.get(\"context_cam\")\n    msgmix.escuchar_cam = flow.get(\"escuchar_cam\")\n    msgmix.distance = flow.get(\"distance\")       // Añadido por david\n    msgmix.distance_min = flow.get(\"distance_min\") // Añadido por david\n    return msgmix;\n}",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3660,
        "y": 2200,
        "wires": [
            [
                "2ec53aa1784087c1",
                "0c8c862285db249e"
            ]
        ]
    },
    {
        "id": "9667a055f9c967a7",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "link in 2",
        "links": [
            "b890981ca603e6e1"
        ],
        "x": 3535,
        "y": 2240,
        "wires": [
            [
                "882ee24d1489b06c"
            ]
        ]
    },
    {
        "id": "0c8c862285db249e",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "debug 17",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 3880,
        "y": 2100,
        "wires": []
    },
    {
        "id": "72222dcbbfea7534",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "snapshot_rqst",
        "mode": "link",
        "links": [
            "108fa8de0b9851c3"
        ],
        "x": 4315,
        "y": 2320,
        "wires": []
    },
    {
        "id": "772b17b6e7954d5f",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "debug 18",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4360,
        "y": 2360,
        "wires": []
    },
    {
        "id": "7a4c757e6f6ddfcd",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "CONFIGURACION DE PARAMETROS Y PRUEBAS ",
        "info": "Este grupo de nodos permiten servir una pagina web para que el usuario desde un movil pueda realizar la gestion de configuracion\nde los principales parametros del Totem asi como realizar algunas pruebas en la camara y en el semaforo.\nEl punto central de este grupo es el paquete UIBUILDER para nodered el cual convive sin inconvenientes con el DASHBOARD de nodered.\nEl uibuilder posee un nodo para cada path de acceso que deseemos habilitar. Cada nodo es independiente del otro en el sentido que \ncada uno dispone de un index.html, un index.js y un index.css ue permiten configurar la pagina que sirve el nodo. \nRecordemos que estos archivos se ejecutan en el cliente, por lo tanto para que el index.js tenga acceso a los datos de nodered y nodered \na los datos generados por el cliente, el nodo uibuilder establece una comunicacion websocket transparente para el usuario de manera\na poder intercambiar informacion entre nodered y el browser. Los elementos fundamentales en index.js para llevar a cabo esta comunicacion\nson las funciones \"uibuilder.onChange()\" para recibir los mensajes de nodered y \"uibuilder.send()\" para enviar a nodered los datos generados \npor el cliente.\nEl nodo \"buttons decoder\" fundamentalmente se dedica a interpretar los comandos del cliente (botones que ha pulsado el usuario) de manera a recopilar\nla informacion adecuada para enviar al uibuilder nuevamente de manera que cambie de pagina o muestre ciertos datos, o tambien a ejecutar\nlos comandos solicitados como reboot, apagar dispositivo, encender / apagar semaforo, ...\nEl nodo de entrada \"datos de pesada y snapshot\" recibe por un lado los snapshots enviados por la camara y extrae la informacion necesaria \npara enviar al cliente, y por otro lado siempre que haya alguna recepcion de datos del Totem se preparan los datos correspondientes para \ncolocarlos en la pantalla principal del uibuilder o pantalla de monitoreo.",
        "x": 3730,
        "y": 2340,
        "wires": []
    },
    {
        "id": "6fca6204952f0b67",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "activa-barrera",
        "mode": "link",
        "links": [
            "6f9cc9fc71e8ca71"
        ],
        "x": 4315,
        "y": 2400,
        "wires": []
    },
    {
        "id": "1826a83d58a7539b",
        "type": "uibuilder",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "",
        "topic": "",
        "url": "display",
        "okToGo": true,
        "fwdInMessages": false,
        "allowScripts": false,
        "allowStyles": false,
        "copyIndex": true,
        "templateFolder": "blank",
        "extTemplate": "",
        "showfolder": false,
        "oldUrl": "bascula",
        "reload": true,
        "sourceFolder": "src",
        "deployedVersion": "7.3.0",
        "showMsgUib": false,
        "title": "",
        "descr": "",
        "editurl": "vscode://vscode-remote/ssh-remote+192.168.0.29/home/trypton/.node-red/projects/Bascula_Autonoma_1/uibuilder/bascula/?windowId=_blank",
        "x": 3780,
        "y": 2640,
        "wires": [
            [
                "51e2882d2082cd6c",
                "9334d6e746bacf1d"
            ],
            []
        ]
    },
    {
        "id": "51e2882d2082cd6c",
        "type": "debug",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "debug 8",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 3980,
        "y": 2580,
        "wires": []
    },
    {
        "id": "153ff925612f0a72",
        "type": "inject",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "",
        "props": [
            {
                "p": "peso",
                "v": "15300",
                "vt": "num"
            },
            {
                "p": "id_m2",
                "v": "1234ABC",
                "vt": "str"
            },
            {
                "p": "id_m4",
                "v": "71774563",
                "vt": "str"
            },
            {
                "p": "semaforo",
                "v": "green",
                "vt": "str"
            },
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"mensaje1\":\"Por favor confirme\",\"mensaje1_color\":\"blue\",\"mostrarOk\":true,\"boton_texto\":\"OK\",\"boton_color\":\"#026d73\"}",
        "payloadType": "json",
        "x": 3490,
        "y": 2600,
        "wires": [
            [
                "1826a83d58a7539b"
            ]
        ]
    },
    {
        "id": "4be318029a87b632",
        "type": "inject",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "",
        "props": [
            {
                "p": "peso",
                "v": "15300",
                "vt": "num"
            },
            {
                "p": "matricula",
                "v": "1234ABC",
                "vt": "str"
            },
            {
                "p": "tarjetaid",
                "v": "ID Leido",
                "vt": "str"
            },
            {
                "p": "semaforo_color",
                "v": "green",
                "vt": "str"
            },
            {
                "p": "button",
                "v": "button3",
                "vt": "str"
            },
            {
                "p": "men_m1",
                "v": "Esto es una prueba",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 3470,
        "y": 2660,
        "wires": [
            [
                "1826a83d58a7539b"
            ]
        ]
    },
    {
        "id": "42a05ba042772cf0",
        "type": "inject",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "",
        "props": [
            {
                "p": "peso",
                "v": "25300",
                "vt": "num"
            },
            {
                "p": "matricula",
                "v": "6421 FRD",
                "vt": "str"
            },
            {
                "p": "tarjetaid",
                "v": "ID Leido",
                "vt": "str"
            },
            {
                "p": "semaforo_color",
                "v": "red",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 3470,
        "y": 2720,
        "wires": [
            [
                "1826a83d58a7539b"
            ]
        ]
    },
    {
        "id": "3a6c2ab4113cbfc9",
        "type": "inject",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "",
        "props": [
            {
                "p": "peso",
                "v": "25300",
                "vt": "num"
            },
            {
                "p": "matricula",
                "v": "6421 FRD",
                "vt": "str"
            },
            {
                "p": "tarjetaid",
                "v": "ID Leido",
                "vt": "str"
            },
            {
                "p": "semaforo_color",
                "v": "red",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 3470,
        "y": 2760,
        "wires": [
            [
                "1826a83d58a7539b"
            ]
        ]
    },
    {
        "id": "656a6ccd2e2d8874",
        "type": "link in",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "uibuilder-in",
        "links": [
            "06832f8daa7c54f9"
        ],
        "x": 3575,
        "y": 2640,
        "wires": [
            [
                "1826a83d58a7539b"
            ]
        ]
    },
    {
        "id": "9334d6e746bacf1d",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "response",
        "func": "if ((msg.payload.boton == \"ok\") && flow.get(\"supervisor\")){\n    let msg_out = {}\n    msg_out.payload = { numero_vial: flow.get(\"numero_vial\"), totem_id: flow.get(\"totem_id\"), validacion: \"ok\" }\n    node.send([msg_out, null])\n}\nreturn;",
        "outputs": 2,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3960,
        "y": 2640,
        "wires": [
            [
                "a1f46ee9a5bf3bd0"
            ],
            []
        ]
    },
    {
        "id": "a1f46ee9a5bf3bd0",
        "type": "link out",
        "z": "953e8740401d175c",
        "d": true,
        "g": "427191a7a5984434",
        "name": "link out 8",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 4095,
        "y": 2620,
        "wires": []
    },
    {
        "id": "1032df4341902ad3",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "QR ESP 2",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "id_topic",
                "v": "QR",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "946DA2A4-B07B-482D-A98F-C6EB0FFABCAE",
        "payloadType": "str",
        "x": 2700,
        "y": 2120,
        "wires": [
            [
                "120a33aec5cdad35"
            ]
        ]
    },
    {
        "id": "f4c61087c128e20f",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "debug 26",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4420,
        "y": 2240,
        "wires": []
    },
    {
        "id": "6d39924d1d2081dd",
        "type": "file",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "guardar config",
        "filename": "/home/trypton/.node-red/projects/config_bascula_autonoma.txt",
        "filenameType": "str",
        "appendNewline": true,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 4400,
        "y": 2160,
        "wires": [
            []
        ]
    },
    {
        "id": "8360f8ecbe5bfff4",
        "type": "function",
        "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.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,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1570,
        "y": 500,
        "wires": [
            [
                "cbcfabf68c8a649c",
                "3d273f36272c6de9"
            ],
            [
                "7c5a1da447b15ffd"
            ]
        ]
    },
    {
        "id": "cbcfabf68c8a649c",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "595fb0f89af85c42",
        "name": "debug 28",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1720,
        "y": 420,
        "wires": []
    },
    {
        "id": "f0e588f6149dad6d",
        "type": "debug",
        "z": "953e8740401d175c",
        "name": "debug 29",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 820,
        "wires": []
    },
    {
        "id": "efa47b4b532c3c95",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "link out 12",
        "mode": "link",
        "links": [
            "8356acc61776cae7"
        ],
        "x": 1185,
        "y": 1380,
        "wires": []
    },
    {
        "id": "4e1d349cc2c30e66",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "debug 30",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4600,
        "y": 1580,
        "wires": []
    },
    {
        "id": "cf7b0c00631682b2",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "abrir barrera",
        "mode": "link",
        "links": [
            "6f9cc9fc71e8ca71"
        ],
        "x": 735,
        "y": 1380,
        "wires": []
    },
    {
        "id": "b2149025f64c76c7",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "debug 31",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 2200,
        "y": 1380,
        "wires": []
    },
    {
        "id": "53335688d117ec96",
        "type": "comment",
        "z": "953e8740401d175c",
        "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": []
    },
    {
        "id": "f5343bf87806ece4",
        "type": "ui_toast",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 1,
        "ok": "OK",
        "cancel": "",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "aviso PMA",
        "x": 1270,
        "y": 1160,
        "wires": [
            [
                "9d8e071a6d3642c0"
            ]
        ]
    },
    {
        "id": "4ab4a211f9ce33d9",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "debug 32",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1160,
        "y": 1080,
        "wires": []
    },
    {
        "id": "b6a2b0d90858876e",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "send2api",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 1845,
        "y": 1100,
        "wires": []
    },
    {
        "id": "5d188f7a3a6eaa62",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "boton salida",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 2905,
        "y": 1100,
        "wires": []
    },
    {
        "id": "b5e1afed9e1c8376",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "debug 3",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 3040,
        "y": 1060,
        "wires": []
    },
    {
        "id": "750c950486ec6cb1",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "debug 4",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 3200,
        "y": 1280,
        "wires": []
    },
    {
        "id": "4e23a3a80fc384b7",
        "type": "inject",
        "z": "953e8740401d175c",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "vial",
                "v": "numero_vial",
                "vt": "flow"
            },
            {
                "p": "totem_id",
                "v": "totem_id",
                "vt": "flow"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"matricula\":\"1234ABC\",\"peso\":12345}",
        "payloadType": "json",
        "x": 1310,
        "y": 680,
        "wires": [
            [
                "2f66b472df7dae12"
            ]
        ]
    },
    {
        "id": "42499939644932e1",
        "type": "link out",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "link out 1",
        "mode": "link",
        "links": [
            "0df0c819dae1658d"
        ],
        "x": 745,
        "y": 2120,
        "wires": []
    },
    {
        "id": "e7cb71b1e09afda6",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "function 2",
        "func": "if ((msg.payload.totem_id == \"totem_1\" || msg.payload.totem_id == \"*\") && msg.payload.numero_vial == 3) { // verifica que los datos son para este totem\n    node.send([msg, null])\n\n}\nelse if ((msg.payload.totem_id == \"totem_2\" || msg.payload.totem_id == \"*\") && msg.payload.numero_vial == 4) { // verifica que los datos son para este totem\n    node.send([null, msg])\n}\nelse if ((msg.payload.totem_id == \"totem_1\" || msg.payload.totem_id == \"*\") && msg.payload.numero_vial == 2) { // verifica que los datos son para este totem\n    node.send([null, null, msg])\n}\nelse if ((msg.payload.totem_id == \"totem_2\" || msg.payload.totem_id == \"*\") && msg.payload.numero_vial == 5) { // verifica que los datos son para este totem\n    node.send([null, null, null, msg])\n}\nreturn",
        "outputs": 4,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 590,
        "y": 1900,
        "wires": [
            [
                "d117899cc8faa6d0"
            ],
            [
                "27ff2fd5838987df"
            ],
            [
                "c5c336e381de6e5d"
            ],
            [
                "10b95a9046bf57f7"
            ]
        ]
    },
    {
        "id": "d117899cc8faa6d0",
        "type": "debug",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "debug 5",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 790,
        "y": 1860,
        "wires": []
    },
    {
        "id": "27ff2fd5838987df",
        "type": "debug",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "debug 6",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 790,
        "y": 1900,
        "wires": []
    },
    {
        "id": "5bf3c0851a93230d",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": " vial 3 totem 1",
        "func": "msg.payload  = { \"numero_vial\": 3, \"totem_id\": \"totem_1\", \"device\": \"supervisor\", \"matricula\": \"0236MWK\", \"accion\": \"check\" }\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 600,
        "y": 2080,
        "wires": [
            [
                "42499939644932e1"
            ]
        ]
    },
    {
        "id": "cb449de1338a8b6b",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "vial 4 totem 2",
        "func": "msg.payload = { \"numero_vial\": 4, \"totem_id\": \"totem_2\", \"device\": \"supervisor\", \"matricula\": \"0312MGB\", \"accion\": \"check\" }\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 600,
        "y": 2120,
        "wires": [
            [
                "42499939644932e1"
            ]
        ]
    },
    {
        "id": "545d688854bdc270",
        "type": "delay",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "",
        "pauseType": "delay",
        "timeout": "100",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 410,
        "y": 2120,
        "wires": [
            [
                "cb449de1338a8b6b",
                "c405dbca2e94854c",
                "35c3f5c0d362ff4d"
            ]
        ]
    },
    {
        "id": "6bdb05cfc53c7c6f",
        "type": "inject",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 260,
        "y": 2080,
        "wires": [
            [
                "545d688854bdc270",
                "5bf3c0851a93230d"
            ]
        ]
    },
    {
        "id": "c405dbca2e94854c",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "vial 2 totem 1",
        "func": "msg.payload = { \"numero_vial\": 2, \"totem_id\": \"totem_1\", \"device\": \"supervisor\", \"matricula\": \"0420MMR\", \"accion\": \"check\" }\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 600,
        "y": 2160,
        "wires": [
            [
                "42499939644932e1",
                "1b2a16010cfceb38"
            ]
        ]
    },
    {
        "id": "c5c336e381de6e5d",
        "type": "debug",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "debug 7",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 790,
        "y": 1940,
        "wires": []
    },
    {
        "id": "35c3f5c0d362ff4d",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "vial 5 totem 2",
        "func": "msg.payload = { \"numero_vial\": 5, \"totem_id\": \"totem_2\", \"device\": \"supervisor\", \"matricula\": \"0397FHJ\", \"accion\": \"check\" }\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 600,
        "y": 2220,
        "wires": [
            [
                "42499939644932e1"
            ]
        ]
    },
    {
        "id": "10b95a9046bf57f7",
        "type": "debug",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "debug 9",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 790,
        "y": 1980,
        "wires": []
    },
    {
        "id": "41b9eef871e59dd2",
        "type": "function",
        "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(\"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: `<p style='font-size:46px;color:#ff0000;'>MATRICULA ${flow.get(\"matricula\")} TIENE UN TRANSITO PENDIENTE</p><p style='font-size:30px;color:#000000;'> AVISE AL BASCULISTA </p>` }, null])\n                    node.send([null, {\n                        payload: `<p style='font-size:46px;color:#ff0000;'>MATRICULA <span style='color:#0000AA;font-weight:bold;'>${flow.get(\"matricula\")}</span> TIENE UN TRANSITO PENDIENTE</p><p style='font-size:30px;color:#000000;'>AVISE AL BASCULISTA</p>`}, 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: `<p style='font-size:46px;color:#ff0000;'>MATRICULA <span style='color:#0000AA;font-weight:bold;'>${flow.get(\"matricula\")}</span> NO TIENE TRANSITO PENDIENTE</p><p style='font-size:30px;color:#000000;'> AVISE AL BASCULISTA </p>` }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA NO REGISTRADA  </p><p style='font-size:30px;color:#000000;'> AVISE AL BASCULISTA </p>\" }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA NO REGISTRADA </p><p style='font-size:30px;color:#000000;'> VUELVA A INTENTARLO </p>\" }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA NO REGISTRADA </p><p style='font-size:30px;color:#000000;'> MODIFIQUE MATRICULA </p>\" }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA ERRONEA </p><p style='font-size:30px;color:#000000;'> DEBE PRESENTAR UN QR O TARJETA VALIDA </p>\" }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA OK </p><p style='font-size:30px;color:#000000;'> DEBE PRESENTAR UN QR O TARJETA VALIDA </p>\" }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA ERRONEA </p><p style='font-size:30px;color:#000000;'> VUELVA A ENTRAR EN LA BASCULA O PRESENTE UNA IDENTIFICACION</p>\" }, null])\n            let matricula = flow.get(\"matricula\")\n            node.send([\n                null,\n                {\n                    payload:\n                        \"<div style='padding:0; margin:0; line-height:1.1;'>\" +\n                        \"<p style='margin:0; padding:0; font-size:42px; color:#ff0000; font-weight:bold;'>MATRICULA ERRONEA</p>\" +\n                        \"<p style='margin:0; padding:8px; font-size:35px; color:#0000ff; font-weight:bold;'>Matrícula: \" + matricula + \"</p>\" +\n                        \"<p style='margin:0; padding:20px; font-size:32px; color:#000000;'>VUELVA A ENTRAR EN LA BASCULA O PRESENTE UNA IDENTIFICACION</p>\" +\n                        \"</div>\"\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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA ERRONEA </p><p style='font-size:30px;color:#000000;'> VUELVA A ENTRAR EN LA BASCULA </p>\" }, 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: \"<p style='font-size:50px;color:#ff0000;'>MATRICULA ERRONEA </p><p style='font-size:30px;color:#000000;'> DEBE PRESENTAR UN QR O TARJETA VALIDA </p>\" }, 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:\"<p style='font-size:50px;color:#ff0000;'>MATRICULA ERRONEA </p><p style='font-size:30px;color:#000000;'> MODIFIQUE EN LA SIGUIENTE PANTALLA </p>\", matricula:matricula_final, size:matricula_final.length}])\n    }\n    else\n        setTimeout(() => {\n            node.send([null, { payload: \"<p style='font-size:50px;color:#ff0000;'>MODIFIQUE MATRICULA </p><p style='font-size:50px;color:#000000;'> EN LA SIGUIENTE PANTALLA </p>\", matricula: matricula_final, size: matricula_final.length }])\n        }, 3000);     \n}\n//-------------------------------------------------------------------------------------------------------------------------\n    //=============================================================================================================\nreturn;",
        "outputs": 3,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1010,
        "y": 1240,
        "wires": [
            [
                "185e340354acf892"
            ],
            [
                "944c9f4b068e0946"
            ],
            [
                "5a4bb45aef7a1433",
                "b31db528b0be605a"
            ]
        ]
    },
    {
        "id": "1b2a16010cfceb38",
        "type": "debug",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "debug 15",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 2180,
        "wires": []
    },
    {
        "id": "84f8ce2853a75080",
        "type": "inject",
        "z": "953e8740401d175c",
        "d": true,
        "g": "c0824ad89a3b525c",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 250,
        "y": 2180,
        "wires": [
            [
                "c405dbca2e94854c"
            ]
        ]
    },
    {
        "id": "ae0cbe34a4030d33",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "debug 16",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 3920,
        "y": 1520,
        "wires": []
    },
    {
        "id": "b31db528b0be605a",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "debug 20",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1280,
        "y": 1300,
        "wires": []
    },
    {
        "id": "185e340354acf892",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "recheck matricula",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 1185,
        "y": 1220,
        "wires": []
    },
    {
        "id": "2d5dd90ce0b556cf",
        "type": "comment",
        "z": "953e8740401d175c",
        "name": "CAMBIO DEL ROOTFOLDER EN UIBUILDER",
        "info": "Para cambiar la dirección de la carpeta donde se situan los archivos de UIBUILDER\nlo podemos hacer en el archivo de settings.js de nodered, modificando el parametro \"uibRoot\" el cual tiene \nalgo asi: uibRoot: '/home/trypton/.node-red/projects/bascula_autonoma3/uibuilder/'. Si cambiamos \nde proyecto solo tenemos que cambiar el nombre de la carpeta del proyecto. En el caso mostrado\nla carpeta del proyecto es bascula_autonoma3. luego de guardar el archivo setting.js es necesario\nre-arrancar el node red (node-red-restart)",
        "x": 3870,
        "y": 1960,
        "wires": []
    },
    {
        "id": "9d8e071a6d3642c0",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "rewrite",
        "func": "if (msg.payload == \"OK\"){\n    node.send({ payload: \"<p style='font-size:50px;color:#ff0000;'>PESO EXCEDIDO </p><p style='font-size:30px;color:#000000;'> SALGA DE LA BASCULA </p>\" })\n}\nreturn ;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1410,
        "y": 1160,
        "wires": [
            [
                "f5343bf87806ece4"
            ]
        ]
    },
    {
        "id": "7cb744ca5e4180bf",
        "type": "ui_toast",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": true,
        "outputs": 1,
        "ok": "OK",
        "cancel": "",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "Ubicacion",
        "x": 1860,
        "y": 1240,
        "wires": [
            []
        ]
    },
    {
        "id": "38a39c85f67ce6f1",
        "type": "comment",
        "z": "953e8740401d175c",
        "g": "61f2c799bf9b937c",
        "name": "CASO QUE PODRIA BLOQUEAR EL SISTEMA",
        "info": "Caso de dos Totems en bascula bidireccional\nSi recibimos una señal del detector remoto en ON esto solicita un snapshot al cabo de 1 segundo.\nSi la imagen llega antes que la bascula haya superado el pmin. El sistema se va a bloquear \nporque en el nodo \"envia matricula\" se hace un chequeo de peso_bajo_flag y si esta en true es decir no ha superado el pmin\nentonces no envia la matricula y por ende el sistema no se mueve mas porque a este bloque no llegan mas \neventos que lo puedan hacer reaccionar.\n\nEste es un caso que en la realidad no deberia suceder, salvo que el vehiculo active el detector y se detenga inmediatamente\nen esa posicion unos segundos sin haber tocado la bascula, de forma que la imagen de la matricula llega al totem \npero la senal de la bascula (peso > pmin) llega un tiempo despues que llego la imagen al nodo \"envia matricula\".\n\nPara resolver esta posible situacion lo que vamos a hacer es incluir un nodo de timer rearmable (nodo \"espera\") que solamente va a \nfuncionar cuando totem_qty = 2. Este nodo intercalado entre el que recibe el mensaje con la matricula procesada (\"envia matricula\") \ny el que recibe la matricula de la camara (analisis de camara\"), se asegura que antes de pedir al nodo \"enviar matricula\" que la envie, haya llegado \nel evento de la bascula observando el flag \"peso_bajo_flag\" hasta que este tenga el estado false.\nEn ese momento envia el mensaje original ( {payload: 1}) al nodo \"enviar_matricula\" para que continue el proceso. De lo contrario se queda observando \ncada 400 ms a ver si el flag del peso ha cambiado de estado.\nEn caso de que \"nunca\" llegue el peso, porque el camion se salio en retroceso o por otra circustancia, el timer se autoextingue al cabo de X segundos\n",
        "x": 4280,
        "y": 1380,
        "wires": []
    },
    {
        "id": "1c98099a9069bb98",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "salida detector rx",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "x": 3835,
        "y": 1500,
        "wires": []
    },
    {
        "id": "0dbd268561a07c9e",
        "type": "catch",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "",
        "scope": [
            "e49672ad6c84b341"
        ],
        "uncaught": false,
        "x": 4320,
        "y": 1740,
        "wires": [
            [
                "b9cf7510f73e95c3"
            ]
        ]
    },
    {
        "id": "b9cf7510f73e95c3",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "debug 24",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4500,
        "y": 1740,
        "wires": []
    },
    {
        "id": "67d5b1beafb80c7d",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "espera ",
        "func": " /* OJO Aqui utilizamos un timer setInterval que guardamos en el contexto local. Importante este punto ya que el timer es un objeto no serializable\n y por lo tanto no deberiamos guardar en contexto flow o global ya que esos contextos estan previstos para objetos serializables (estados, valores, arrays, json...)\n Si lo ponemos en flow el timer puede romperse, quedar corrupto, o incoherente al hacer deploy o cualquier operacion de inicio.\n lo 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\n en la pestaña \"Al final\" que es lo mismo. Close se refiere a cuando el node se reinicia, se hace redeploy etc..\n*/\n\nif (!flow.get(\"peso_bajo_flag\") && !flow.get(\"order_sent\") ) { // si hay peso en bascula y el mensaje no ha sido enviado a proximo nodo lo enviamos ahora \n    node.send({ payload: 1 });// Este es el mensaje que enviamos al proximo nodo para que realice el procesamiento de la matricula\n    flow.set(\"order_sent\", true)\n    node.status({ fill: \"green\", shape: \"dot\", text: \"sin delay...\" });\n}\nelse{// \n    if (!flow.get(\"interval_running\") && !flow.get(\"order_sent\")) { // si el timer no ha sido arrancado y no hemos enviado mensaje\n        node.status({ fill: \"yellow\", shape: \"dot\", text: \"esperando 'peso_bajo_flag'=false \" });\n        flow.set(\"interval_running\", true) // marcamos timer corriendo\n        let t = context.get(\"interval_matricula\"); // En la primera vez es undefined pero funciona correctamente al hacer t = setInterval mas adelante\n        if (t) clearInterval(t);\n\n        context.set(\"counter_espera_peso\", 0);\n        let c = context.get(\"counter_espera_peso\") || 0;\n\n        t = setInterval(() => {\n            c = (context.get(\"counter_espera_peso\") || 0) + 1;\n            context.set(\"counter_espera_peso\", c);\n\n            //node.warn(\"contador = \" + c);\n\n            if (c >= 40) {\n                clearInterval(t);\n                context.set(\"interval_matricula\", null);\n                node.status({ fill: \"red\", shape: \"dot\", text: \"terminado ciclo de espera\" });\n                flow.set(\"interval_running\", false);\n                return;\n            }\n\n            if (!flow.get(\"peso_bajo_flag\") && !flow.get(\"peso_bajo_flag_forzado\")) {\n                clearInterval(t);\n                context.set(\"interval_matricula\", null);\n                node.status({ fill: \"green\", shape: \"dot\", text: \"liberado (peso_bajo_flag=false)\" });\n                flow.set(\"interval_running\", false);\n                flow.set(\"order_sent\", true);\n                node.send([{ payload: 1 }, null]);\n                return;\n            }\n        }, 400);\n\n        context.set(\"interval_matricula\", t);\n    }\n}\n\nreturn;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "// El código añadido aquí se ejecutará cuando el nodo\n// se detenga o se vuelva a instanciar.\nconst t = context.get(\"interval_matricula\");\nif (t) {\n    clearInterval(t);\n    context.set(\"interval_matricula\", null);\n}",
        "libs": [],
        "x": 4110,
        "y": 1560,
        "wires": [
            [
                "92309a9f0b0f2c29",
                "4120796963ac5cf7"
            ]
        ]
    },
    {
        "id": "4120796963ac5cf7",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "debug 21",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4280,
        "y": 1520,
        "wires": []
    },
    {
        "id": "cbea4a6fab6a2384",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "analisis detector 2A",
        "func": "/* Cuando se acerca un vehiculo a la bascula, la camara por lo general envia snapshots con matricula detectada. Estos snapshots los llamamos ANPR0\n   y se guarda el ultimo en memoria. Esto sucede antes que el vehiculo active el detector o la bascula.\n    - Al momento de activar el detector se solicita un snapshot. Este snapshot recibido sera procesado por el nodo de analisis de la camara\n    - En caso de no llegar el snaphot en el tiempo previsto (\"max_wait_time_to_send_plate\") o de recibir el sanpshot fuera de formato entonces se revisa si tenemos\n      anpr0 valido y lo enviamos, en caso contrario enviamos una matricula \"NOT FOUND\"\n      \n    * ANPR0 valido significa que la matricula tiene el formato esperado\n    * ANPR0 No valido significa que tiene matricula pero el formato no es correcto (mala lectura)\n    \n    Definicion de variables importantes:\n    anpr0_time = timesatmp al momento de recibir el ANPR0\n    anpr0_flag = Si true, hemos recibido al menos un ANPR0\n    anpr0_valid_flag = Si true, hemos recibido un ANPR0 con matricula valida\n    time_max_2_bascula = tiempo transcurrido desde recepcion del ANPR0 hasta la activacion de la bascula\n    wait_time_to_send_plate = tiempo de espera para enviar la matricula, por si llega un nuevo snapshot antes de 3s\n    max_wait_time_to_send_plate = tiempo de espera para enviar la matricula, por si llega un nuevo snapshot antes de 10s\n    max_time_bascula_sensor = tiempo maximo permitido entre activacion de bascula y activacion del detector de vehiculos\n    snapshot_rq_flag = Si true, indica que hemos solicitado un snapshot\n    distance_flag = Si true, indica que un vehiculo ha activado el detector\n    distance_flag_ONtime = timestamp del momento en que se activo el detector\n    last_time_on_off_distance = Tiempo transcurrido entre el ON y el OFF del detector de vehiculos, de la pesada anterior.\n    peso_min_time = timestamp del momento en que se activa la bascula. Este se pone a cero cuando detectamos peso < pmin\n    \n    \n*/\n// Si entra aqui es que se recibio un mensaje del nodo detector remoto\nvar timestamp_now = new Date().getTime()\nvar thisdate = new Date()\nvar msg_rq = {}\nflow.set(\"timestamp_now\", timestamp_now)\n\n//-------------------------------------------------- Caso activacion por el detector de vehiculos ---------------------------------------------\n//Este detector lo usamos aqui en caso de tener 2 Totems, sino el sensor se usa cuando se active la bascula como un estado, en ese caso no pasaria por aqui. \n//En caso de usar la bascula como evento, el resultado del detector no se usaria como evento sino como estado y seria el detector local no el detector remoto\n//En este apartado se atiende el caso de 2T 1SL 1SR asi como 2T 0SL 1SR\n\nif (msg.topic == \"detector_ON\" && flow.get(\"totem_qty\") == 2){ //\"detector_ON\" es lo que envia en el topic del mensaje el detector remoto, cuando detecta vehiculo.\n    // hemos recibido un trigger del sensor detector de objetos\n    if(flow.get(\"input_flag\")){ //Este flag nos indica si el vehiculo esta entrando en la bascual respecto a la posicion del sensor remoto.\n        \n        // *******solicitud de un snapshot con un delay********\n        setTimeout(function(){\n            var msg_rq = {}\n            flow.set(\"timestamp_rq\", new Date().getTime()) // marcamos tiempo de solicitud\n            msg_rq.url = \"http://\" + flow.get(\"camara_IP\") + \"/cgi-bin/trafficSnap.cgi?action=manSnap&channel=1\"\n            flow.set(\"snapshot_rq_flag\", true) // marcamos que hemos solicitado un snapshot\n            node.send([null, msg_rq])\n        }, flow.get(\"time_to_snapshot\"));// esperamos un tiempo variable por configuracion entre que recibimos el detector y cuando pedimos el snaphot\n      \n        \n              // ************************************ TIME OUT *********************************************\n        \n        // --- (Re)ARRANQUE del timer --- si ya había uno, lo cancelamos para no duplicar\n        let t = context.get(\"timer_snapshot\");\n        if (t) clearTimeout(t);\n        context.set(\"timer_snapshot\", null);\n\n        t = setTimeout(() => { // hacemos una espera hasta que este dentro de la bascula\n                //node.warn(\"ts3 = \" + new Date().getTime() + \"time_sensor_to_this = \" + (new Date().getTime() -flow.get(\"distance_flag_ONtime\")))\n            const thisdate = new Date();\n            const anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // tiempo entre anpr0 y activacion de bascula\n            if(flow.get(\"anpr0_flag\") && ( anpr0_2_bascula < flow.get(\"time_max_2_bascula\") + flow.get(\"max_wait_time_to_send_plate\")) && anpr0_2_bascula >= 0){ // chequea si hay anpr0\n                flow.set(\"last_plate\", flow.get(\"anpr0\")) // asignamos anpr0 al objeto que se va a enviar\n                     \n                }\n                else{// Si no hay vehiculo identificado enviamos indicacion de que paso un vehiculo sin poder ser identificado\n                    var plate = {payload:{Picture:{Plate:{}, SnapInfo:{}, NormalPic:{}}}} // construimos parametros para poder procesar estructura de snapshot al enviar la matricula\n                    plate.payload.Picture.Plate.PlateNumber = \"NOT FOUND\"\n                    plate.payload.Picture.SnapInfo.SnapTime = thisdate.toLocaleString('es-ES')\n                    plate.payload.Picture.SnapInfo.DeviceID = flow.get(\"camaraID\")\n                    plate.payload.Picture.NormalPic.Content = \"\"\n                    plate.payload.Picture.NormalPic.PicName = \"No Picture\"\n                    flow.set(\"last_plate\", plate)\n                    //esto deja constancia de que paso un vehiculo sin poder ver la matricula\n                }\n                if (!flow.get(\"plate_sent\") && flow.get(\"peso_min_time\") > 0) {//asegurar que hay vehiculo en la bascula\n                        \n                    flow.set(\"called_from\", \"2T 1SR snapshot\")\n                    node.send([{payload:1}, null])\n                }\n            // IMPORTANTÍSIMO: limpiar handle al terminar\n            context.set(\"timer_snapshot\", null);\n            \n        }, flow.get(\"max_wait_time_to_send_plate\"));\n    }\n}\n// --- CANCELACIÓN del TIMER desde otro nodo ---\nelse if (msg.topic === \"cancel_snapshot_timer\") {\n    const t = context.get(\"timer_snapshot\");\n    if (t) clearTimeout(t);\n        context.set(\"timer_snapshot\", null);\n        return null; // no enviar nada\n    }\nreturn ;",
        "outputs": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "// El código añadido aquí se ejecutará cuando el nodo\n// se detenga o se vuelva a instanciar.\nconst t = context.get(\"timer_snapshot\");\nif (t) clearTimeout(t);\ncontext.set(\"timer_snapshot\", null);",
        "libs": [],
        "x": 3890,
        "y": 1620,
        "wires": [
            [
                "92309a9f0b0f2c29"
            ],
            [
                "e49672ad6c84b341"
            ]
        ]
    },
    {
        "id": "0e5b5bc676912875",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "analisis peso 2A",
        "func": "\n//------------------------------------------------ Aqui entramos por evento de bascula ----------------------------------------------------\n//El evento de bascula viene identificado por msg.topic = \"peso_ON\"\nvar thisdate = new Date()\nif(msg.topic == \"peso_ON\" && flow.get(\"totem_qty\") == 1){\n\n    if(flow.get(\"sensor_distancia_local\")){\n    // caso de un solo Totem CON sensor de distancia local\n    /* Este es el caso de un solo Totem con sensor local de vehiculos\n       Cuando se activa la bascula, esta puede activarse porque el vehiculo esta entrando en la bascula por el lado del Totem\n       en cuyo caso el vehiculo va saliendo de la planta, ya que el Totem se coloca para que el chofer del vehiculo pueda acceder a este\n       cuando tiene todo el vehiculo en la bascula entrando en la planta, o bien puede ser activada porque el vehiculo esta entrando en la planta \n       en cuyo caso no activara el sensor local de este Totem hasta que llegue al final de la bascula. Utilizamos los tiempos de activacion \n       de eventos para saber en que sentido circula el vehiculo.\n    */\n    // 1T 1SL 0SR\n    flow.set(\"input_flag\", false)// inicializamos flag de direccion del vehiculo \n    setTimeout(function(){ // esperamos dos segundos para ver si se ha activado el sensor de vehiculos\n        if (!flow.get(\"distance_flag\")){\n            //el vehiculo viene entrando en la bascula por lado opuesto al Totem\n            flow.set(\"input_flag\", true)\n            flow.set(\"called_from\", \"bascula-1TC1SL\")// 1TCSL = 1 Totem1, 1 Camara, Sensor local\n            read_plate()\n        }\n        else{\n            // Vehiculo entrando en la bascula por el lado del Totem\n            // Este es el caso de  1 Totem saliendo de la planta, se puede enviar mensaje con la salida de un vehiculo\n        }\n    }, 1500)\n\n    \n    }\n    else{\n    // caso de un solo Totem SIN sensor de distancia local 1T 0SL 0SR\n        //Si la bascula fuera de doble sentido se podria utilizar el flag de anpr0 para saber \n        // si vehiculo viene en el sentido correcto.  \n        //En este caso podria haber un fallo cuando la camara no detecte vehiculo acercandose a la bascula (anpr0)\n        flow.set(\"called_from\", \"bascula-1TC0SL\")// 1TCNSL = 1 Totem, 1 Camara, No Sensor Local, 0 Sensor Remoto\n        read_plate() // vamos a solicitar un snapshot y colocar un timeout para recibir dicho snapshot\n    }\n}\n\nelse if(msg.topic == \"peso_ON\" && flow.get(\"totem_qty\") == 2){\n    \n    /* Caso con DOS Totems 2T 1SL 1SR */ \n    if(flow.get(\"sensor_distancia_remoto\")){\n    // caso de dos Totems CON sensor de distancia remoto\n        /* Este caso se atiende en el nodo del detector\n           es decir el evento de bascula en este caso no se toma en cuenta\n        */\n    }\n    else{\n    //  **Caso de dos Totems SIN sensor de distancia remoto 2T 1SL 0SR**\n        if(flow.get(\"sensor_distancia_local\")){\n        // CON sensor de distancia local\n            flow.set(\"input_flag\", false)// inicializamos flag de direccion del vehiculo \n            setTimeout(function(){ // esperamos dos segundos para ver si se ha activado el sensor de vehiculos\n                if (!flow.get(\"distance_flag\")){\n                //el vehiculo viene entrando en la bascula\n                flow.set(\"input_flag\", true)\n                flow.set(\"called_from\", \"bascula-2TC1SL0SR\")// 1TCS = 1 Totem1, 1 Camara, Sensor Local, No Sensor Remoto\n                read_plate()\n                }\n            }, 1500)\n        }\n        else{\n        // **NO sensor de distancia local NO sensor de distancia remoto++ 2T 0SL 0SR\n        //En este caso no conocemos por donde entra el camion asi que la unica forma de saber si le pedimos un snaphot\n        //a esta camara es mirando si hemos recibido algun anpr0 antes de entrar en la bascula, lo cual quiere decir que \n        //el vehiculo viene en esta dirección. Por lo tanto la condicion es que haya habido un anpr0.\n            if(flow.get(\"anpr0_flag\")){\n                var anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // calcula tiempo entre anpr0 y activacion de bascula\n                if(flow.get(\"valid_anpr0_flag\") && anpr0_2_bascula < flow.get(\"time_max_2_bascula\") && anpr0_2_bascula >= 0){ // chequea que el anpr0 se tomo antes de time_max_2_bascula segundos (10 segundos)\n                    flow.set(\"called_from\", \"bascula-2TC0SL0SR\") // 2TCNS = 2 Totems, 1 Camara, No SensorL, No SensorR\n                    read_plate() \n                }\n            }\n        }\n    }\n        \n}\n// --- CANCELACIÓN del TIMER desde otro nodo ---\nelse if (msg.topic === \"cancel_snapshot_timer\") {\n    const t = context.get(\"timer_snapshot\");\n    if (t) clearTimeout(t);\n        context.set(\"timer_snapshot\", null);\n    return null; // no enviar nada\n}\n\nreturn;\n\n/*\n ===========================================FUNCION PARA SOLICITAR Y ESPERAR LAS MATRICULAS========================================================\n\n*/\nfunction read_plate(){\n    //node.warn(\"hello\")\n    setTimeout(function(){\n        var msg_rq = {}\n        //node.warn (\"hola\")\n        flow.set(\"timestamp_rq\", new Date().getTime()) // marcamos tiempo de solicitud\n        msg_rq.url = \"http://\" + flow.get(\"camara_IP\") + \"/cgi-bin/trafficSnap.cgi?action=manSnap&channel=1\"\n        flow.set(\"snapshot_rq_flag\", true) // marcamos que hemos solicitado un snapshot\n        node.send([null, msg_rq])\n        \n    }, flow.get(\"time_to_snapshot\"));// esperamos un tiempo variable por configuracion entre que recibimos el detector y cuando pedimos el snaphot\n    \n    //node.warn(\"read_plate_bascula\")\n    \n     //******************************************* TIMEOUT DEL SNAPSHOT **********************************************************\n    \n    // --- (Re)ARRANQUE del timer --- si ya había uno, lo cancelamos para no duplicar\n    let t = context.get(\"timer_snapshot\");\n    if (t) clearTimeout(t);\n    context.set(\"timer_snapshot\", null);\n    \n    t = setTimeout(() => { // hacemos una espera hasta que este dentro de la bascula\n                // si no envio snapshot, se va a ejecutar esta parte\n                //node.warn(\"ts3 = \" + new Date().getTime() + \"time_sensor_to_this = \" + (new Date().getTime() -flow.get(\"distance_flag_ONtime\")))\n                const anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // tiempo entre anpr0 y activacion de bascula\n                if(flow.get(\"anpr0_flag\") && ( anpr0_2_bascula < flow.get(\"time_max_2_bascula\") + flow.get(\"max_wait_time_to_send_plate\")) && anpr0_2_bascula >= 0){ // chequea si hay anpr0\n                    flow.set(\"last_plate\", flow.get(\"anpr0\")) // asignamos anpr0 al objeto que se va a enviar\n                     \n                }\n                else{// Si no hay vehiculo identificado enviamos indicacion de que paso un vehiculo sin poder ser identificado\n                    var plate = {payload:{Picture:{Plate:{}, SnapInfo:{}, NormalPic:{}}}} // construimos parametros para poder procesar estructura de snapshot al enviar la matricula\n                    plate.payload.Picture.Plate.PlateNumber = \"NOT FOUND\"\n                    plate.payload.Picture.SnapInfo.SnapTime = thisdate.toLocaleString('es-ES')\n                    plate.payload.Picture.SnapInfo.DeviceID = flow.get(\"camaraID\")\n                    plate.payload.Picture.NormalPic.Content = \"\"\n                    plate.payload.Picture.NormalPic.PicName = \"No Picture\"\n                    flow.set(\"last_plate\", plate)\n                    //esto deja constancia de que paso un vehiculo sin poder ver la matricula\n                }\n                \n                if (!flow.get(\"plate_sent\") && flow.get(\"peso_min_time\") > 0) {//asegurar que hay vehiculo en la bascula\n                    flow.set(\"called_from\", flow.get(\"called_from\") + \" snapshot NRx\")\n                    node.send([{payload:1}, null])\n                }\n            // IMPORTANTÍSIMO: limpiar handle al terminar\n            context.set(\"timer_snapshot\", null);\n            }, flow.get(\"max_wait_time_to_send_plate\"));\n    //*******************************************************************************************************************************       \n    \n}\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\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": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "// El código añadido aquí se ejecutará cuando el nodo\n// se detenga o se vuelva a instanciar.\nconst t = context.get(\"timer_snapshot\");\nif (t) clearTimeout(t);\ncontext.set(\"timer_snapshot\", null);",
        "libs": [],
        "x": 3880,
        "y": 1680,
        "wires": [
            [
                "92309a9f0b0f2c29"
            ],
            [
                "e49672ad6c84b341",
                "46dbff31c3270ba1"
            ]
        ]
    },
    {
        "id": "ba06db16261a710a",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "name": "analisis detector 2",
        "func": "/* Cuando se acerca un vehiculo a la bascula, la camara por lo general envia snapshots con matricula detectada. Estos snapshots los llamamos ANPR0\n   y se guarda el ultimo en memoria. Esto sucede antes que el vehiculo active el detector o la bascula.\n    - Al momento de activar el detector se solicita un snapshot. Este snapshot recibido sera procesado por el nodo de analisis de la camara\n    - En caso de no llegar el snaphot en el tiempo previsto (\"max_wait_time_to_send_plate\") o de recibir el sanpshot fuera de formato entonces se revisa si tenemos\n      anpr0 valido y lo enviamos, en caso contrario enviamos una matricula \"NOT FOUND\"\n      \n    * ANPR0 valido significa que la matricula tiene el formato esperado\n    * ANPR0 No valido significa que tiene matricula pero el formato no es correcto (mala lectura)\n    \n    Definicion de variables importantes:\n    anpr0_time = timesatmp al momento de recibir el ANPR0\n    anpr0_flag = Si true, hemos recibido al menos un ANPR0\n    anpr0_valid_flag = Si true, hemos recibido un ANPR0 con matricula valida\n    time_max_2_bascula = tiempo transcurrido desde recepcion del ANPR0 hasta la activacion de la bascula\n    wait_time_to_send_plate = tiempo de espera para enviar la matricula, por si llega un nuevo snapshot antes de 3s\n    max_wait_time_to_send_plate = tiempo de espera para enviar la matricula, por si llega un nuevo snapshot antes de 10s\n    max_time_bascula_sensor = tiempo maximo permitido entre activacion de bascula y activacion del detector de vehiculos\n    snapshot_rq_flag = Si true, indica que hemos solicitado un snapshot\n    distance_flag = Si true, indica que un vehiculo ha activado el detector\n    distance_flag_ONtime = timestamp del momento en que se activo el detector\n    last_time_on_off_distance = Tiempo transcurrido entre el ON y el OFF del detector de vehiculos, de la pesada anterior.\n    peso_min_time = timestamp del momento en que se activa la bascula. Este se pone a cero cuando detectamos peso < pmin\n    \n    \n*/\n// Si entra aqui es que se recibio un mensaje del nodo detector remoto\nvar timestamp_now = new Date().getTime()\nvar thisdate = new Date()\nvar msg_rq = {}\nflow.set(\"timestamp_now\", timestamp_now)\n\n//-------------------------------------------------- Caso activacion por el detector de vehiculos ---------------------------------------------\n//Este detector lo usamos aqui en caso de tener 2 Totems, sino el sensor se usa cuando se active la bascula como un estado, en ese caso no pasaria por aqui. \n//En caso de usar la bascula como evento, el resultado del detector no se usaria como evento sino como estado y seria el detector local no el detector remoto\n//En este apartado se atiende el caso de 2T 1SL 1SR asi como 2T 0SL 1SR\n\nif (msg.topic == \"detector_ON\" && flow.get(\"totem_qty\") == 2){ //\"detector_ON\" es lo que envia en el topic del mensaje el detector remoto, cuando detecta vehiculo.\n    // hemos recibido un trigger del sensor detector de objetos\n    if(flow.get(\"input_flag\")){ //Este flag nos indica si el vehiculo esta entrando en la bascual respecto a la posicion del sensor remoto.\n        \n        // *******solicitud de un snapshot con un delay********\n        flow.set(\"disparo\", setTimeout(function(){\n            var msg_rq = {}\n            flow.set(\"timestamp_rq\", new Date().getTime()) // marcamos tiempo de solicitud\n            msg_rq.url = \"http://\" + flow.get(\"camara_IP\") + \"/cgi-bin/trafficSnap.cgi?action=manSnap&channel=1\"\n            flow.set(\"snapshot_rq_flag\", true) // marcamos que hemos solicitado un snapshot\n            node.send([null, msg_rq])\n        }, flow.get(\"time_to_snapshot\")));// esperamos un tiempo variable por configuracion entre que recibimos el detector y cuando pedimos el snaphot\n      \n        \n              // ************************************ TIME OUT *********************************************\n        flow.set(\"timer_snapshot\", setTimeout(function () { // hacemos una espera hasta que este dentro de la bascula\n                //node.warn(\"ts3 = \" + new Date().getTime() + \"time_sensor_to_this = \" + (new Date().getTime() -flow.get(\"distance_flag_ONtime\")))\n                var anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // tiempo entre anpr0 y activacion de bascula\n                if(flow.get(\"anpr0_flag\") && ( anpr0_2_bascula < flow.get(\"time_max_2_bascula\") + flow.get(\"max_wait_time_to_send_plate\")) && anpr0_2_bascula >= 0){ // chequea si hay anpr0\n                    flow.set(\"last_plate\", flow.get(\"anpr0\")) // asignamos anpr0 al objeto que se va a enviar\n                     \n                }\n                else{// Si no hay vehiculo identificado enviamos indicacion de que paso un vehiculo sin poder ser identificado\n                    var plate = {payload:{Picture:{Plate:{}, SnapInfo:{}, NormalPic:{}}}} // construimos parametros para poder procesar estructura de snapshot al enviar la matricula\n                    plate.payload.Picture.Plate.PlateNumber = \"NOT FOUND\"\n                    plate.payload.Picture.SnapInfo.SnapTime = thisdate.toLocaleString('es-ES')\n                    plate.payload.Picture.SnapInfo.DeviceID = flow.get(\"camaraID\")\n                    plate.payload.Picture.NormalPic.Content = \"\"\n                    plate.payload.Picture.NormalPic.PicName = \"No Picture\"\n                    flow.set(\"last_plate\", plate)\n                    //esto deja constancia de que paso un vehiculo sin poder ver la matricula\n                }\n                if (!flow.get(\"plate_sent\") && flow.get(\"peso_min_time\") > 0) {//asegurar que hay vehiculo en la bascula\n                        \n                    flow.set(\"called_from\", \"2T 1SR snapshot\")\n                    node.send([{payload:1}, null])\n                }\n               \n        }, flow.get(\"max_wait_time_to_send_plate\")));\n    }\n}\nreturn ;",
        "outputs": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3850,
        "y": 1860,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "9c52a578b99744fa",
        "type": "function",
        "z": "953e8740401d175c",
        "d": true,
        "name": "analisis peso 2",
        "func": "\n//------------------------------------------------ Aqui entramos por evento de bascula ----------------------------------------------------\n//El evento de bascula viene identificado por msg.topic = \"peso_ON\"\nvar thisdate = new Date()\nif(msg.topic == \"peso_ON\" && flow.get(\"totem_qty\") == 1){\n\n    if(flow.get(\"sensor_distancia_local\")){\n    // caso de un solo Totem CON sensor de distancia local\n    /* Este es el caso de un solo Totem con sensor local de vehiculos\n       Cuando se activa la bascula, esta puede activarse porque el vehiculo esta entrando en la bascula por el lado del Totem\n       en cuyo caso el vehiculo va saliendo de la planta, ya que el Totem se coloca para que el chofer del vehiculo pueda acceder a este\n       cuando tiene todo el vehiculo en la bascula entrando en la planta, o bien puede ser activada porque el vehiculo esta entrando en la planta \n       en cuyo caso no activara el sensor local de este Totem hasta que llegue al final de la bascula. Utilizamos los tiempos de activacion \n       de eventos para saber en que sentido circula el vehiculo.\n    */\n    // 1T 1SL 0SR\n    flow.set(\"input_flag\", false)// inicializamos flag de direccion del vehiculo \n    setTimeout(function(){ // esperamos dos segundos para ver si se ha activado el sensor de vehiculos\n        if (!flow.get(\"distance_flag\")){\n            //el vehiculo viene entrando en la bascula por lado opuesto al Totem\n            flow.set(\"input_flag\", true)\n            flow.set(\"called_from\", \"bascula-1TC1SL\")// 1TCSL = 1 Totem1, 1 Camara, Sensor local\n            read_plate()\n        }\n        else{\n            // Vehiculo entrando en la bascula por el lado del Totem\n            // Este es el caso de  1 Totem saliendo de la planta, se puede enviar mensaje con la salida de un vehiculo\n        }\n    }, 1500)\n\n    \n    }\n    else{\n    // caso de un solo Totem SIN sensor de distancia local 1T 0SL 0SR\n        //Si la bascula fuera de doble sentido se podria utilizar el flag de anpr0 para saber \n        // si vehiculo viene en el sentido correcto.  \n        //En este caso podria haber un fallo cuando la camara no detecte vehiculo acercandose a la bascula (anpr0)\n        flow.set(\"called_from\", \"bascula-1TC0SL\")// 1TCNSL = 1 Totem, 1 Camara, No Sensor Local, 0 Sensor Remoto\n        read_plate() // vamos a solicitar un snapshot y colocar un timeout para recibir dicho snapshot\n    }\n}\n\nelse if(msg.topic == \"peso_ON\" && flow.get(\"totem_qty\") == 2){\n    \n    /* Caso con DOS Totems 2T 1SL 1SR */ \n    if(flow.get(\"sensor_distancia_remoto\")){\n    // caso de dos Totems CON sensor de distancia remoto\n        /* Este caso se atiende en el nodo del detector\n           es decir el evento de bascula en este caso no se toma en cuenta\n        */\n    }\n    else{\n    //  **Caso de dos Totems SIN sensor de distancia remoto 2T 1SL 0SR**\n        if(flow.get(\"sensor_distancia_local\")){\n        // CON sensor de distancia local\n            flow.set(\"input_flag\", false)// inicializamos flag de direccion del vehiculo \n            setTimeout(function(){ // esperamos dos segundos para ver si se ha activado el sensor de vehiculos\n                if (!flow.get(\"distance_flag\")){\n                //el vehiculo viene entrando en la bascula\n                flow.set(\"input_flag\", true)\n                flow.set(\"called_from\", \"bascula-2TC1SL0SR\")// 1TCS = 1 Totem1, 1 Camara, Sensor Local, No Sensor Remoto\n                read_plate()\n                }\n            }, 1500)\n        }\n        else{\n        // **NO sensor de distancia local NO sensor de distancia remoto++ 2T 0SL 0SR\n        //En este caso no conocemos por donde entra el camion asi que la unica forma de saber si le pedimos un snaphot\n        //a esta camara es mirando si hemos recibido algun anpr0 antes de entrar en la bascula, lo cual quiere decir que \n        //el vehiculo viene en esta dirección. Por lo tanto la condicion es que haya habido un anpr0.\n            if(flow.get(\"anpr0_flag\")){\n                var anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // calcula tiempo entre anpr0 y activacion de bascula\n                if(flow.get(\"valid_anpr0_flag\") && anpr0_2_bascula < flow.get(\"time_max_2_bascula\") && anpr0_2_bascula >= 0){ // chequea que el anpr0 se tomo antes de time_max_2_bascula segundos (10 segundos)\n                    flow.set(\"called_from\", \"bascula-2TC0SL0SR\") // 2TCNS = 2 Totems, 1 Camara, No SensorL, No SensorR\n                    read_plate() \n                }\n            }\n        }\n    }\n        \n}\n\n\nreturn;\n\n/*\n ===========================================FUNCION PARA SOLICITAR Y ESPERAR LAS MATRICULAS========================================================\n\n*/\nfunction read_plate(){\n    //node.warn(\"hello\")\n    flow.set(\"disparo\", setTimeout(function(){\n        var msg_rq = {}\n        //node.warn (\"hola\")\n        flow.set(\"timestamp_rq\", new Date().getTime()) // marcamos tiempo de solicitud\n        msg_rq.url = \"http://\" + flow.get(\"camara_IP\") + \"/cgi-bin/trafficSnap.cgi?action=manSnap&channel=1\"\n        flow.set(\"snapshot_rq_flag\", true) // marcamos que hemos solicitado un snapshot\n        node.send([null, msg_rq])\n        \n    }, flow.get(\"time_to_snapshot\")));// esperamos un tiempo variable por configuracion entre que recibimos el detector y cuando pedimos el snaphot\n    \n    //node.warn(\"read_plate_bascula\")\n    \n     //******************************************* TIMEOUT DEL SNAPSHOT **********************************************************\n    flow.set(\"timer_snapshot\", setTimeout(function () { // hacemos una espera para asegurar que el vehiculo ya esta al final de la bascula\n                // si no envio snapshot, se va a ejecutar esta parte\n                //node.warn(\"ts3 = \" + new Date().getTime() + \"time_sensor_to_this = \" + (new Date().getTime() -flow.get(\"distance_flag_ONtime\")))\n                var anpr0_2_bascula = flow.get(\"peso_min_time\") - flow.get(\"anpr0_time\") // tiempo entre anpr0 y activacion de bascula\n                if(flow.get(\"anpr0_flag\") && ( anpr0_2_bascula < flow.get(\"time_max_2_bascula\") + flow.get(\"max_wait_time_to_send_plate\")) && anpr0_2_bascula >= 0){ // chequea si hay anpr0\n                    flow.set(\"last_plate\", flow.get(\"anpr0\")) // asignamos anpr0 al objeto que se va a enviar\n                     \n                }\n                else{// Si no hay vehiculo identificado enviamos indicacion de que paso un vehiculo sin poder ser identificado\n                    var plate = {payload:{Picture:{Plate:{}, SnapInfo:{}, NormalPic:{}}}} // construimos parametros para poder procesar estructura de snapshot al enviar la matricula\n                    plate.payload.Picture.Plate.PlateNumber = \"NOT FOUND\"\n                    plate.payload.Picture.SnapInfo.SnapTime = thisdate.toLocaleString('es-ES')\n                    plate.payload.Picture.SnapInfo.DeviceID = flow.get(\"camaraID\")\n                    plate.payload.Picture.NormalPic.Content = \"\"\n                    plate.payload.Picture.NormalPic.PicName = \"No Picture\"\n                    flow.set(\"last_plate\", plate)\n                    //esto deja constancia de que paso un vehiculo sin poder ver la matricula\n                }\n                \n                if (!flow.get(\"plate_sent\") && flow.get(\"peso_min_time\") > 0) {//asegurar que hay vehiculo en la bascula\n                    flow.set(\"called_from\", flow.get(\"called_from\") + \" snapshot NRx\")\n                    node.send([{payload:1}, null])\n                }\n               \n            }, flow.get(\"max_wait_time_to_send_plate\")));\n    //*******************************************************************************************************************************       \n    \n}\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\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": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3840,
        "y": 1900,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "c0c7eac1a54f2ea0",
        "type": "file in",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "firma",
        "filename": "/home/trypton/.node-red/projects/audio/firma_dig.mp3",
        "filenameType": "str",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1930,
        "y": 1500,
        "wires": [
            [
                "23706ca448e439ea"
            ]
        ]
    },
    {
        "id": "23706ca448e439ea",
        "type": "ui_audio",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "",
        "group": "ac875e596615614c",
        "voice": "",
        "always": true,
        "x": 2100,
        "y": 1560,
        "wires": []
    },
    {
        "id": "0c7a53155c4927ab",
        "type": "file in",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "qr_rfid",
        "filename": "/home/trypton/.node-red/projects/audio/identificacion_rx.mp3",
        "filenameType": "str",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1930,
        "y": 1540,
        "wires": [
            [
                "23706ca448e439ea"
            ]
        ]
    },
    {
        "id": "ce9b745d5013070e",
        "type": "file in",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "peso estable",
        "filename": "/home/trypton/.node-red/projects/audio/peso estable_dig.mp3",
        "filenameType": "str",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1910,
        "y": 1580,
        "wires": [
            [
                "23706ca448e439ea"
            ]
        ]
    },
    {
        "id": "7a5b8831173f85b2",
        "type": "file in",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "fin pesada",
        "filename": "/home/trypton/.node-red/projects/audio/final_dig.mp3",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1910,
        "y": 1620,
        "wires": [
            [
                "23706ca448e439ea"
            ]
        ]
    },
    {
        "id": "3683a851f79ee374",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "decoder",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "firma",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "qr_rfid",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "peso_estable",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "fin_pesada",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 4,
        "x": 1740,
        "y": 1540,
        "wires": [
            [
                "c0c7eac1a54f2ea0"
            ],
            [
                "0c7a53155c4927ab"
            ],
            [
                "ce9b745d5013070e"
            ],
            [
                "7a5b8831173f85b2"
            ]
        ]
    },
    {
        "id": "f47c019e5baa0206",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "audio in",
        "links": [
            "5420d37f8224c191",
            "6530f7fdb887dbf1",
            "fab29e0f6601e531"
        ],
        "x": 1285,
        "y": 1540,
        "wires": [
            [
                "20ba13f6d67ea6ac"
            ]
        ]
    },
    {
        "id": "20ba13f6d67ea6ac",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "check audio",
        "func": "if((msg.topic != \"retry\") && msg.topic != \"end\" ){\n    flow.set(\"next_audio\", msg.payload)\n    msg = { payload: \"\", topic: \"audio_ko\" }\n}\n\nif (msg.topic != \"end\"){\n    let isPlaying = flow.get(\"audioPlaying\") || false;\n\n    if (!isPlaying) {\n        msg.topic = \"audio_ok\"  \n    }\n    else{\n        msg.topic = \"audio_ko\"\n    }\n    \n}\nelse{\n    msg = null\n}\nreturn msg",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1410,
        "y": 1540,
        "wires": [
            [
                "ad5beee5d0151013"
            ]
        ]
    },
    {
        "id": "ad5beee5d0151013",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "envia audio",
        "func": "\nif (msg.topic == \"audio_ok\"){\n    msg.payload = flow.get(\"next_audio\")\n    msg.topic = \"end\"\n    \n}\nelse{\n    \n    // Espera 500ms y reintenta\n    setTimeout(() => {\n        msg ={}\n        msg.payload = 1\n        msg.topic = \"retry\"\n        node.send(msg);\n    }, 100);\n    msg = null\n    \n    \n}\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1570,
        "y": 1540,
        "wires": [
            [
                "3683a851f79ee374",
                "20ba13f6d67ea6ac"
            ]
        ]
    },
    {
        "id": "a395ed21d7049d5d",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "Guardar estado",
        "func": "let playing = msg.status.text === \"playing\";\nflow.set(\"audioPlaying\", playing);\nreturn null;",
        "outputs": 0,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1520,
        "y": 1640,
        "wires": []
    },
    {
        "id": "6ebdceb73c4cfbf0",
        "type": "status",
        "z": "953e8740401d175c",
        "g": "0fc258061e59d527",
        "name": "audio status",
        "scope": [
            "23706ca448e439ea"
        ],
        "x": 1350,
        "y": 1640,
        "wires": [
            [
                "a395ed21d7049d5d"
            ]
        ]
    },
    {
        "id": "8ccf22739ca72997",
        "type": "switch",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "detector",
        "property": "display_detector",
        "propertyType": "flow",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 3500,
        "y": 2120,
        "wires": [
            [
                "882ee24d1489b06c"
            ]
        ]
    },
    {
        "id": "7fd2f6e6fe3f37ea",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "link in 1",
        "links": [
            "f730c0726e257f7f"
        ],
        "x": 3405,
        "y": 2120,
        "wires": [
            [
                "8ccf22739ca72997"
            ]
        ]
    },
    {
        "id": "8bfc0e03b763b6b1",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "peso 1500",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"totem_id\":\"totem_1\",\"numero_vial\":2,\"peso\":1500}",
        "payloadType": "json",
        "x": 2860,
        "y": 1740,
        "wires": [
            [
                "41b4309de63a6bba"
            ]
        ]
    },
    {
        "id": "eb6a49472973163d",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "peso 0",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "{\"totem_id\":\"totem_1\",\"numero_vial\":2,\"peso\":0}",
        "payloadType": "json",
        "x": 2850,
        "y": 1800,
        "wires": [
            [
                "41b4309de63a6bba"
            ]
        ]
    },
    {
        "id": "41b4309de63a6bba",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "05cb4985c3c68ae8",
        "name": "link out 3",
        "mode": "link",
        "links": [
            "423023a153680e64"
        ],
        "x": 2975,
        "y": 1760,
        "wires": []
    },
    {
        "id": "c79815b5a79ef60a",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "camara_contexto_rq",
        "mode": "link",
        "links": [
            "9b0761cc102bc028"
        ],
        "x": 1115,
        "y": 1180,
        "wires": []
    },
    {
        "id": "72071eee550615d7",
        "type": "http request",
        "z": "953e8740401d175c",
        "g": "1b1ee13b380e32b6",
        "name": "camara contexto",
        "method": "GET",
        "ret": "bin",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": true,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "digest",
        "senderr": false,
        "headers": [],
        "x": 1680,
        "y": 1360,
        "wires": [
            [
                "f5b38e6e811de790",
                "730bbe3df0ab62db"
            ]
        ]
    },
    {
        "id": "f5b38e6e811de790",
        "type": "function",
        "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\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": 1890,
        "y": 1360,
        "wires": [
            [
                "8385582377c91fb6"
            ]
        ]
    },
    {
        "id": "9b0761cc102bc028",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "1b1ee13b380e32b6",
        "name": "camara_contexto_url",
        "links": [
            "c79815b5a79ef60a"
        ],
        "x": 1545,
        "y": 1360,
        "wires": [
            [
                "a0aa0f96fe081912",
                "72071eee550615d7"
            ]
        ]
    },
    {
        "id": "8385582377c91fb6",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "1b1ee13b380e32b6",
        "name": "contexto_ok",
        "mode": "link",
        "links": [
            "31e0432a8e9a2f00"
        ],
        "x": 2025,
        "y": 1360,
        "wires": []
    },
    {
        "id": "31e0432a8e9a2f00",
        "type": "link in",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "contexto_rx",
        "links": [
            "8385582377c91fb6"
        ],
        "x": 1525,
        "y": 1060,
        "wires": [
            [
                "db3e9975c8f562b6"
            ]
        ]
    },
    {
        "id": "f3676f905e4ade55",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "function 3",
        "func": "let msg_camara_contexto = {}\nmsg_camara_contexto.url = `http://${flow.get(\"camara_contexto_IP\")}/cgi-bin/snapshot.cgi?channel=1`\nflow.set(\"camara_contexto_rx\", false) // inicializamos flag de imagen no recibida de la camara de contexto\nnode.send( msg_camara_contexto)\nreturn;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1600,
        "y": 1280,
        "wires": [
            [
                "72071eee550615d7"
            ]
        ]
    },
    {
        "id": "730bbe3df0ab62db",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "1b1ee13b380e32b6",
        "name": "debug 1",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1860,
        "y": 1400,
        "wires": []
    },
    {
        "id": "ff3913749bd9e2f3",
        "type": "inject",
        "z": "953e8740401d175c",
        "g": "646bdfb9988a1708",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 1570,
        "y": 1220,
        "wires": [
            [
                "f3676f905e4ade55"
            ]
        ]
    },
    {
        "id": "a0aa0f96fe081912",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "1b1ee13b380e32b6",
        "name": "debug 2",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1660,
        "y": 1400,
        "wires": []
    },
    {
        "id": "ee22e8c1327a7b65",
        "type": "http request",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "",
        "method": "GET",
        "ret": "bin",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "digest",
        "senderr": false,
        "headers": [],
        "x": 4370,
        "y": 2440,
        "wires": [
            [
                "b90652f4011dad39",
                "f4c61087c128e20f"
            ]
        ]
    },
    {
        "id": "b90652f4011dad39",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "e3bc37824e09b774",
        "name": "cam context",
        "mode": "link",
        "links": [
            "7058b9e632aa584b"
        ],
        "x": 4485,
        "y": 2440,
        "wires": []
    },
    {
        "id": "46dbff31c3270ba1",
        "type": "debug",
        "z": "953e8740401d175c",
        "g": "0e2b148022a7af13",
        "name": "debug 10",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 4160,
        "y": 1740,
        "wires": []
    },
    {
        "id": "43ae11415631042a",
        "type": "ui_toast",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": true,
        "outputs": 1,
        "ok": "OK",
        "cancel": "CANCEL",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "Shutdown",
        "x": 2900,
        "y": 1220,
        "wires": [
            [
                "184d7d929902a0d4"
            ]
        ]
    },
    {
        "id": "184d7d929902a0d4",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "it's ok",
        "func": "if (msg.payload === \"OK\"){\n    node.send({\n        payload:\n            \"<div style='padding:0; margin:0; line-height:1.1;'>\" +\n            \"<p style='margin:0; padding:0; font-size:42px; color:#ff0000; font-weight:bold;'>ATENCION</p>\" +\n            \"<p style='margin:0; padding:20px; font-size:32px; color:#000000;'>\" + \"ESTA SEGURO ???\" + \"</p>\" +\n            \"</div>\"\n    })\n    \n}\nelse {\n    node.send({payload:\"\"})\n}\n\nreturn ;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3050,
        "y": 1220,
        "wires": [
            [
                "f33bb7424ebd286a"
            ]
        ]
    },
    {
        "id": "f33bb7424ebd286a",
        "type": "ui_toast",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": true,
        "outputs": 1,
        "ok": "SI",
        "cancel": "NO",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "Shutdown-2",
        "x": 3190,
        "y": 1220,
        "wires": [
            [
                "f825c6ab70a8cffc"
            ]
        ]
    },
    {
        "id": "f825c6ab70a8cffc",
        "type": "function",
        "z": "953e8740401d175c",
        "g": "bad04a9f657a59e6",
        "name": "apagar",
        "func": "if (msg.payload === \"SI\"){\n    node.send({payload: \"sudo poweroff\"})\n    \n}\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3060,
        "y": 1280,
        "wires": [
            [
                "750c950486ec6cb1"
            ]
        ]
    },
    {
        "id": "077ea8c7ce8cec99",
        "type": "link out",
        "z": "953e8740401d175c",
        "g": "9328043d4e9512a9",
        "name": "link out 4",
        "mode": "link",
        "links": [
            "e0b4dd6107f95f28"
        ],
        "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": []
    }
]