How can i build a random delayed trigger loop?

Nice, but I would suggest a reset input, just in case you don't really want it to run forever.

[{"id":"81fa9f84.c2ef3","type":"change","z":"f3a075a0.2b2ae","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":120,"wires":[["3fac4536.38e1d2"]]},{"id":"3fac4536.38e1d2","type":"delay","z":"f3a075a0.2b2ae","name":"","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"5","randomLast":"10","randomUnits":"seconds","drop":false,"x":540,"y":120,"wires":[["3d9011e2.9a279e","3888711f.dad256"]]},{"id":"16d5b50f.05f7bb","type":"inject","z":"f3a075a0.2b2ae","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":60,"wires":[["3888711f.dad256"]]},{"id":"3d9011e2.9a279e","type":"debug","z":"f3a075a0.2b2ae","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":770,"y":120,"wires":[]},{"id":"3888711f.dad256","type":"change","z":"f3a075a0.2b2ae","name":"","rules":[{"t":"set","p":"delay","pt":"msg","to":"$floor($random()*600000)","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"$floor($random()*10)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":60,"wires":[["3fac4536.38e1d2"]]},{"id":"aad6f390.80961","type":"inject","z":"f3a075a0.2b2ae","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":120,"wires":[["81fa9f84.c2ef3"]]}]
``

I just tried to stay in scope of issue. For full solution I think there is much more needed of course.

thanks for the reset hint.

my current solution:

[{"id":"3716c275.fcba66","type":"subflow","name":"jobs simulation","info":"","category":"","in":[{"x":100,"y":120,"wires":[{"id":"d8b4ec33.0ea608"}]}],"out":[{"x":1180,"y":240,"wires":[{"id":"b93c39d1.206018","port":0}]}]},{"id":"b93c39d1.206018","type":"delay","z":"3716c275.fcba66","name":"","pauseType":"random","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"5","randomLast":"45","randomUnits":"seconds","drop":false,"x":980,"y":240,"wires":[["d8b4ec33.0ea608"]]},{"id":"d8b4ec33.0ea608","type":"delay","z":"3716c275.fcba66","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":240,"y":120,"wires":[["e1e866a1.ee1a"]]},{"id":"cf70b3a4.37554","type":"function","z":"3716c275.fcba66","name":"working hours","func":"var d = new Date();\nvar h = d.getHours();\nif (msg.do=="yes") {\n if (h>=8 && h<=17){\n } else {\n msg.do=="no" \n }\n}\nreturn msg;\n","outputs":1,"noerr":0,"x":600,"y":260,"wires":[["2330084b.7b17d"]]},{"id":"e1e866a1.ee1a","type":"function","z":"3716c275.fcba66","name":"work days","func":"var d = new Date();\n// 0 Sunday\n// 1 Monday\n// 2 Tuesday\n// 3 Wednesday\n// 4 Thursday\n// 5 Friday\n// 6 Saturday\nvar day = d.getDay();\nif (day>=1 && day<=6){\n msg.do="yes";\n} else {\n msg.do="no";\n}\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":260,"wires":[["cf70b3a4.37554"]]},{"id":"2330084b.7b17d","type":"switch","z":"3716c275.fcba66","name":"","property":"do","propertyType":"msg","rules":[{"t":"eq","v":"no","vt":"str"},{"t":"eq","v":"yes","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":770,"y":260,"wires":[["d8b4ec33.0ea608"],["b93c39d1.206018"]],"outputLabels":["no","yes"]},{"id":"33cd4306.9cbb14","type":"comment","z":"3716c275.fcba66","name":"jobs simulation","info":"created by M.Rauch 10/2018\n\nit have a clock\nthen the work days are checked\nthen the working hours are checked\nif not at work the clock ticks\nif at work then the delay simulate a task\n","x":120,"y":40,"wires":}]

In case anyone is still interested, this flow will generate the sort of random events that @MarkusR wants, using only a single function node and no feedback loop. It also illustrates what a pain JavaScript can be (at least for me) when what you want is synchronous execution. I was already working on something similar, so it was worth the effort, but if anyone can suggest less ugly code I would be grateful.

[{"id":"13c7f4da.98d3bb","type":"function","z":"9b4deac7.469e4","name":"","func":"const minDelay = 1, maxDelay =5; // seconds\nvar run = context.get('state') || false;\nif (msg.topic.toLowerCase() === 'control') {\n    run = ! run;\n}\ncontext.set('state',run);\nnode.status({fill:(run) ? 'green':'red'});\nif (run) {\n    randomDelay(maxDelay,minDelay);\n    node.status({fill:'green',text:delay.toFixed(2)});\n}\n\nfunction randomDelay(max,min) {\n    delay = Math.random() * (max - min) + min;\n    setTimeout(sendMessages,1000 * delay);\n    msg.delay = delay;\n    return delay;\n}\n\nfunction sendMessages() {\n    node.send(msg);\n    run = context.get('state');\n    if (run) {\n        randomDelay(maxDelay,minDelay);\n        node.status({fill:'green',text:delay.toFixed(2)});\n    }\n}\n","outputs":1,"noerr":0,"x":530,"y":500,"wires":[["f2d3395f.8d1ad8"]]},{"id":"48ab97b.5d78868","type":"inject","z":"9b4deac7.469e4","name":"start/stop","topic":"control","payload":"random trigger","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":400,"y":500,"wires":[["13c7f4da.98d3bb"]]},{"id":"f2d3395f.8d1ad8","type":"debug","z":"9b4deac7.469e4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":650,"y":500,"wires":[]}]

Here is a way of refactoring the loop to make it a bit neater.

[{"id":"f6d5dfd7.84ec5","type":"function","z":"6dc690a3.1abc88","name":"","func":"const minDelay = 1, maxDelay =5; // seconds\nvar run = context.get('state') || false;\nif (msg.topic.toLowerCase() === 'control') {\n    run = ! run;\n}\ncontext.set('state',run);\nnode.status({fill:(run) ? 'green':'red'});\n\nvar msgToSend = null;\nloop();\nreturn null;\n\nfunction loop() {\n    if (context.get('state')) {\n        node.send(msgToSend);\n        msgToSend = msg;\n        let delay = Math.random() * (maxDelay - minDelay) + minDelay;\n        setTimeout(loop,1000 * delay);\n        node.status({fill:'green',text:delay.toFixed(2)});\n    }\n}\n","outputs":1,"noerr":0,"x":320,"y":209,"wires":[["9e9ea6e4.a82078"]]},{"id":"7defb913.a1ebe","type":"inject","z":"6dc690a3.1abc88","name":"start/stop","topic":"control","payload":"random trigger","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":209,"wires":[["f6d5dfd7.84ec5"]]},{"id":"9e9ea6e4.a82078","type":"debug","z":"6dc690a3.1abc88","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":440,"y":209,"wires":[]}]
1 Like

also posted here HTTP GET request for data and post to website

You could use a dsm node:

[{"id":"a26cd0b7.90aef","type":"dsm","z":"66646ca8.e97654","name":"random trigger","sm_config":"{\n    \"currentState\": \"stopped\",\n    \"states\": {\n        \"stopped\": {\n            \"start\": \"running\"\n        },\n        \"running\": {\n            \"running\": \"running\",\n            \"stop\": \"stopped\"\n        }\n    },\n    \"data\": {\n        \"minDelay\": 1,\n        \"maxDelay\": 5\n    },\n    \"methods\": {\n        \"start\": [\n            \"resume('running', msg);\"\n        ],\n        \"running\": [\n            \"sm.delay = Math.random() * (sm.data.maxDelay - sm.data.minDelay) + sm.data.minDelay;\",\n            \"msg.delay = 'delay '+ sm.delay.toFixed(2);\",\n            \"timeout.delay = setTimeout(function() {\",\n            \"   node.send(msg);\",\n            \"   resume('running',msg);\",\n            \"},sm.delay*1000);\"\n        ],\n        \"stop\": [\n            \"clearTimeout(timeout.delay);\",\n            \"msg.delay = 'stopped';\"\n        ],\n        \"onTransition\": [\n            \"output = false;\"\n        ],\n        \"status\": {\n            \"fill\": {\n                \"get\": \"sm.currentState === 'running' ? 'green' : 'red';\"\n            },\n            \"shape\": \"dot\",\n            \"text\": {\n                \"get\": \"msg.delay;\"\n            }\n        }\n    }\n}","x":330,"y":320,"wires":[["fdb8b530.787a38"]]},{"id":"c78a24cc.f507f8","type":"inject","z":"66646ca8.e97654","name":"start","topic":"start","payload":"random trigger","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":320,"wires":[["a26cd0b7.90aef"]]},{"id":"fdb8b530.787a38","type":"debug","z":"66646ca8.e97654","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":490,"y":320,"wires":[]},{"id":"894976da.9dfca8","type":"inject","z":"66646ca8.e97654","name":"stop","topic":"stop","payload":"random trigger","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":360,"wires":[["a26cd0b7.90aef"]]}]

@Colin, thanks! That certainly is neater -- and just the sort of help I'll need if I am ever going to get really comfortable with JavaScript. I have coded in at least a dozen high-level languages since the mid-1970's, and JS may not be the most difficult or opaque, but somehow I find it the most annoying...

@cflurin That's a really nice solution to the problem. At one time, finite state machines were my bread and butter, so I've looked for opportunities to use your node. Unfortunately, I haven't done much with it for a couple of reasons. One is the learning curve associated with the JSON objects used for programming the node, and the other is that in debugging it is sometimes hard to see the connection between JS error messages and syntax errors in the configuration. I think I will give it another try.

BTW, have you considered developing an edit window that would let the user define the states and transition table with text entry boxes and pull-down menus? Perhaps actions could then be entered simply as blocks of JS code. All this would still be translated into JSON in the background, but the ui might be more accessible to a beginner.

@drmibell: Thanks for your feedback, your answer is dsm specific, so I moved to node-red-contrib-dsm Node-red-contrib-dsm

Hi Guys,

All solution looks cool, but could someone improvised such that, it always run? no need manual intervention?
I did something like this:
image
But it would go haywire/crazy when I click multiple time on inject. And I do not want the possibility of accidentally turn delay off.

I'm not certain exactly what issue you are having, but I suggest that instead of the function node you use node-red-contrib-random-event-generator, which has the same functionality. This flow will start generating triggers at random intervals as soon as it is deployed.

[{"id":"11bbb517.23d813","type":"event","z":"2f7c2f6d.c20a68","name":"","controlTopic":"control","meanInterval":1,"distribution":"exponential","minInterval":1,"maxInterval":2,"outputTopic":"event","outputPayload":"timestamp","x":630,"y":620,"wires":[["c3a06c28.eb70f8"]]},{"id":"c3a06c28.eb70f8","type":"debug","z":"2f7c2f6d.c20a68","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":620,"wires":[]},{"id":"396c67a0.a60bc8","type":"inject","z":"2f7c2f6d.c20a68","name":"start/stop","topic":"control","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":500,"y":620,"wires":[["11bbb517.23d813"]]}]

To prevent accidental shutoffs, you can disable the inject node, although it is probably a good idea to be able to stop the generator if necessary. (Apparently, once the inject node is disabled the flow must be re-deployed to enable it.) I hope that helps.

Thank you, i think this is what I am looking for. Without inject node, this will run indefinitely??
image

No, the random event generator is designed to be stopped when it is first deployed. This prevents messages from flowing before other nodes in the flow are ready to handle them. The flow I posted above will start to run as soon as it is deployed, thanks to the way the inject node is configured. (You can even program a delay, if you want.)

If your application absolutely demands that you not use an inject node, it may be possible to modify node-red-contrib-random-event-generator so that it has an option to start running immediately as soon as it is deployed. If that can be done, I will need some time to test and document the changes. If you raise an issue on GitHub, it will help me track the work.

If you do add an option to autostart, then you should also then remove the input pin as it’s no longer needed.

Actually, it's still needed in case the user wants to stop the generator.

So using an inject to start it is not unreasonable (as that is what is designed to do)

Exactly! But for some reason @eugene objects to using an inject node and says it has caused problems in his flow:

I don't really see much of an issue here, but I thought I could help without much effort or added complexity in the code.

Ah well, up to you. Not sure what this obsession folk have with running with the editor open is about but hey :slight_smile:

Good point. You can't click what you can't see...