Timestamping between boolean messages?

So I have a windows machine with node-red connected to a Compactlogix controller and am sending Boolean data between the two.

I am wanting to start a timer when the data type goes true and stop the timer when the data type goes false and keep adding to that time when the data goes true again. Only need to reset when i want to reset when injected though.

I am wanting to gather machine downtime information for a particular alarm. I am still relatively new to JavaScript though i have done quite a few dashboards and flows before.

Any resolution or guidance to this would be great.

Here is a low code example.

[{"id":"6c53e20d.c7aff4","type":"inject","z":"bf9e1e33.030598","name":"set / reset","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"count","payload":"0","payloadType":"num","x":242.3333282470703,"y":582.3333740234375,"wires":[["4708cf70.8d9ab8"]]},{"id":"4708cf70.8d9ab8","type":"join","z":"bf9e1e33.030598","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":660,"wires":[["6b1ba010.249c78"]]},{"id":"6b1ba010.249c78","type":"change","z":"bf9e1e33.030598","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"$$.payload.finish - $$.payload.start","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"count","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"$$.payload.finish - $$.payload.start  + $$.payload.count","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":760,"wires":[["868ecb3e.48f64","4708cf70.8d9ab8"]]},{"id":"5f3b5d1f.fe6bcc","type":"change","z":"bf9e1e33.030598","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"finish","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"},{"t":"set","p":"complete","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":680,"wires":[["4708cf70.8d9ab8"]]},{"id":"8da1a14.869fc6","type":"change","z":"bf9e1e33.030598","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"start","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":640,"wires":[["4708cf70.8d9ab8"]]},{"id":"868ecb3e.48f64","type":"debug","z":"bf9e1e33.030598","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":760,"wires":[]},{"id":"6f55ee86.e40b6","type":"switch","z":"bf9e1e33.030598","name":"","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":400,"y":660,"wires":[["8da1a14.869fc6"],["5f3b5d1f.fe6bcc"]]},{"id":"9680830a.e82508","type":"rbe","z":"bf9e1e33.030598","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","x":270,"y":660,"wires":[["6f55ee86.e40b6"]]},{"id":"f7c54e8d.3484f","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":130,"y":700,"wires":[["9680830a.e82508"]]},{"id":"677236c3.592848","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":110,"y":640,"wires":[["9680830a.e82508"]]}]

[edit] just swap is false and is true in the switch node,if you need it to count false to true, rather than true to false.

This is close to what I'm looking for, I didn't even think about using switch nodes. Great idea.

The only issue is the timestamp starts over once "false" is triggered. I wanting the timestamp to keep pause on false and pick back up on true if that makes sense

Hello ..
most probably its will need some adjustments according to your needs
but here is a example using JS intervals

[{"id":"e4f6aecc19f55705","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":330,"y":1200,"wires":[["0537f5f91839d08c"]]},{"id":"6bfc9c66fe261ed4","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":330,"y":1260,"wires":[["0537f5f91839d08c"]]},{"id":"5c9bb3c6b75e18a1","type":"inject","z":"54efb553244c241f","name":"Reset","props":[{"p":"payload"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"anything","payloadType":"str","x":330,"y":1320,"wires":[["0537f5f91839d08c"]]},{"id":"0537f5f91839d08c","type":"function","z":"54efb553244c241f","name":"Counter","func":"let timer = flow.get(\"timer\") || undefined\nlet counter = flow.get(\"counter\") || 0\n\nif (msg.payload === true && !timer) {\n    // create a timer\n    timer = setInterval(() => {\n        counter++  // increment counter\n        node.send({ payload: counter }) // send counter\n        flow.set(\"counter\", counter) // update counter Context\n    }, 1000)\n    // save timer in Context\n    flow.set(\"timer\", timer)\n    node.status({ fill: \"green\", shape: \"dot\", text: \"Running\" });\n}\n\nif (msg.payload === false && timer) {\n    // clear timer but keep \"counter\"\n    clearInterval(timer)\n    flow.set(\"timer\", undefined)\n    node.status({ fill: \"yellow\", shape: \"dot\", text: \"Stopped\" });\n}\n\nif (msg.reset === true) {\n    // clear both the timer and the counter\n    clearInterval(timer)\n    flow.set(\"counter\", 0)\n    flow.set(\"timer\", undefined)\n    node.status({});\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":1260,"wires":[["04cbc19ef90ddd1d"]]},{"id":"04cbc19ef90ddd1d","type":"debug","z":"54efb553244c241f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":750,"y":1260,"wires":[]}]

[EDIT] added extra clearInterval on reset

Both ideas work out really well, ill paste the flow with both solutions below.

[{"id":"f7a3812fb08c4a68","type":"tab","label":"Machine Status Timestamp","disabled":false,"info":"","env":[]},{"id":"17b7ae8c412dc165","type":"eth-ip in","z":"f7a3812fb08c4a68","endpoint":"6039e43e4f3683b1","mode":"single","variable":"Test_OP_cnv_1","program":"","name":"Machine #1 Status 1","x":110,"y":160,"wires":[["9680830a.e82508"]]},{"id":"ca96d8ab8574489e","type":"eth-ip in","z":"f7a3812fb08c4a68","endpoint":"8834283c61b80e45","mode":"single","variable":"Test_OP_cnv2","program":"","name":"Machine #2 Status 2","x":110,"y":360,"wires":[["0537f5f91839d08c"]]},{"id":"cb31d3c.377153","type":"dsm","z":"f7a3812fb08c4a68","name":"operating time","sm_config":"{\n    \"currentState\": \"stopped\",\n    \"states\": {\n        \"stopped\": {\n            \"on\": \"started\"\n        },\n        \"started\": {\n            \"inc\": \"counting\",\n            \"off\": \"stopped\"\n        },\n        \"counting\": {\n            \"inc\": \"counting\",\n            \"off\": \"stopped\"\n        }\n    },\n    \"data\": {\n        \"time\": 0,\n        \"step\": 1,\n        \"prev_time\": null\n    },\n    \"methods\": {\n        \"on\": [\n            \"if (sm.currentState === 'started') {\",\n            \"   sm.data.prev_time = Date.now();\",\n            \"   node.send(msg);\",\n            \"   resume('inc', msg);\",\n            \"}\"\n        ],\n        \"inc\": [\n            \"timeout.interval = setTimeout(function() {\",\n            \"   var now = Date.now();\",\n            \"   sm.data.time += now - sm.data.prev_time;\",\n            \"   sm.data.prev_time = now;\",\n            \"   resume('inc', msg);\",\n            \"}, sm.data.step*1000);\",\n            \"output = false;\"\n        ],\n        \"off\": [\n            \"clearTimeout(timeout.interval);\"\n        ],\n        \"reset\": [\n            \"sm.data.time = 0;\"\n        ],\n        \"onTransition\": [\n            \"sm.text = 'time '+ Math.round(sm.data.time / 1000);\"\n        ],\n        \"status\": {\n            \"fill\": {\n                \"get\": \"sm.currentState === 'counting' ? 'green' : 'grey';\"\n            },\n            \"shape\": \"dot\",\n            \"text\": {\n                \"get\": \"sm.text;\"\n            }\n        }\n    }\n}","x":850,"y":160,"wires":[[]]},{"id":"6c53e20d.c7aff4","type":"inject","z":"f7a3812fb08c4a68","name":"set / reset","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"reset","x":640,"y":60,"wires":[["cb31d3c.377153"]]},{"id":"5f3b5d1f.fe6bcc","type":"change","z":"f7a3812fb08c4a68","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":180,"wires":[["cb31d3c.377153"]]},{"id":"6f55ee86.e40b6","type":"switch","z":"f7a3812fb08c4a68","name":"","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":430,"y":160,"wires":[["06545186ad607af4"],["5f3b5d1f.fe6bcc"]]},{"id":"9680830a.e82508","type":"rbe","z":"f7a3812fb08c4a68","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":290,"y":160,"wires":[["6f55ee86.e40b6"]]},{"id":"06545186ad607af4","type":"change","z":"f7a3812fb08c4a68","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"on","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":120,"wires":[["cb31d3c.377153"]]},{"id":"0537f5f91839d08c","type":"function","z":"f7a3812fb08c4a68","name":"Counter","func":"let timer = flow.get(\"timer\") || undefined\nlet counter = flow.get(\"counter\") || 0\n\nif (msg.payload === true && !timer) {\n    // create a timer\n    timer = setInterval(() => {\n        counter++  // increment counter\n        node.send({ payload: counter }) // send counter\n        flow.set(\"counter\", counter) // update counter Context\n    }, 1000)\n    // save timer in Context\n    flow.set(\"timer\", timer)\n    node.status({ fill: \"green\", shape: \"dot\", text: \"Running\" });\n}\n\nif (msg.payload === false && timer) {\n    // clear timer but keep \"counter\"\n    clearInterval(timer)\n    flow.set(\"timer\", undefined)\n    node.status({ fill: \"yellow\", shape: \"dot\", text: \"Stopped\" });\n}\n\nif (msg.reset === true) {\n    // clear both the timer and the counter\n    flow.set(\"counter\", 0)\n    flow.set(\"timer\", undefined)\n    node.status({});\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":360,"wires":[["04cbc19ef90ddd1d"]]},{"id":"04cbc19ef90ddd1d","type":"debug","z":"f7a3812fb08c4a68","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":360,"wires":[]},{"id":"5c9bb3c6b75e18a1","type":"inject","z":"f7a3812fb08c4a68","name":"Reset","props":[{"p":"payload"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"whatever","payloadType":"str","x":150,"y":300,"wires":[["0537f5f91839d08c"]]},{"id":"6039e43e4f3683b1","type":"eth-ip endpoint","address":"192.168.80.104","slot":"0","cycletime":"2001","name":"Machine Status 1","vartable":{"":{"Test_OP_cnv_1":{"type":"BOOL"},"":{"type":""}}}},{"id":"8834283c61b80e45","type":"eth-ip endpoint","address":"192.168.80.104","slot":"0","cycletime":"2001","name":"Machine Status 2","vartable":{"":{"Test_OP_cnv2":{"type":"BOOL"}}}}]

Thanks for all the help.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.