Flow-timer questions

Hi,

I want to monitor production time on several machines, based on the machine status (run, error, maintenance, job finished).

Basically, a timer that would start for a given batch (based on the program name that is running), would pause for any error/maintenance events, and would continue if running again without changing the name program, then stop and record the time when the job was finished.

I saw that flow-timer might be useful for what I have in mind. However, I would need to base the timer name on a message property (topic, for instance) instead of hard-coding it in the node.

I see that I could use a, environment variable, but that would affect all the flow, not a particular one, right?

If based on a message property, it should create a different timer per machine model/program name, and a single flow would work.

Otherwise, I'd have to create one flow per machine (I have about 45 different ones, and things can get HUGE very quickly).

Any ideas?

cronplus?

As far as I've seen cronplus is a scheduler, not a time monitor.

In case anyone's interested:

I took the concept of flow-timer and created my own timer/stopwatch subflow:

[
    {
        "id": "a83b77052e22c05e",
        "type": "subflow",
        "name": "StopWatch",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 60,
                "y": 40,
                "wires": [
                    {
                        "id": "ba4169e0fb4f892f"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 710,
                "y": 40,
                "wires": [
                    {
                        "id": "ba4169e0fb4f892f",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [],
        "meta": {},
        "color": "#87A980",
        "inputLabels": [
            "command input"
        ],
        "outputLabels": [
            "return"
        ],
        "icon": "font-awesome/fa-clock-o"
    },
    {
        "id": "ba4169e0fb4f892f",
        "type": "function",
        "z": "a83b77052e22c05e",
        "name": "stopwatch",
        "func": "var chrono = global.get(\"stopwatch\") || {};\n\nif (msg.stopwatch !== undefined) {\n    if (chrono[msg.stopwatch.name] === undefined){\n        chrono[msg.stopwatch.name] = { job:\"\", elapsed:0, startPoint:null, stopPoint: null, lastCommand: \"\" };\n    }\n    \n    if (msg.stopwatch.command == \"start\"){\n        if ((chrono[msg.stopwatch.name].lastCommand != \"start\") ||\n            ((chrono[msg.stopwatch.name].lastCommand == \"start\") && (chrono[msg.stopwatch.name].order != msg.stopwatch.order))){\n            if ((chrono[msg.stopwatch.name].lastCommand == \"end\") || (chrono[msg.stopwatch.name].order != msg.stopwatch.order)){\n                chrono[msg.stopwatch.name].job = msg.stopwatch.job;\n                chrono[msg.stopwatch.name].elapsed = 0;\n            }\n            msg.stopwatch.startPoint = Date.now();\n            chrono[msg.stopwatch.name].startPoint = msg.stopwatch.startPoint;\n        }\n    } else if ((msg.stopwatch.command == \"stop\") || (msg.stopwatch.command == \"end\")) {\n            msg.stopwatch.stopPoint = Date.now();\n            if (chrono[msg.stopwatch.name].startPoint !== null){\n                chrono[msg.stopwatch.name].stopPoint = msg.stopwatch.stopPoint;\n                chrono[msg.stopwatch.name].elapsed += chrono[msg.stopwatch.name].stopPoint - chrono[msg.stopwatch.name].startPoint;\n                chrono[msg.stopwatch.name].stopPoint = null;\n                chrono[msg.stopwatch.name].startPoint = null;\n            }\n    }\n    msg.stopwatch.elapsed = chrono[msg.stopwatch.name].elapsed;\n    chrono[msg.stopwatch.name].lastCommand = msg.stopwatch.command;\n    global.set(\"stopwatch\", chrono);\n}\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 340,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "c6555eda535400fe",
        "type": "subflow:a83b77052e22c05e",
        "z": "b8bde5487c0b24fa",
        "name": "",
        "x": 430,
        "y": 120,
        "wires": [
            [
                "4665f495b811c70a"
            ]
        ]
    }
]

The basic idea is that you add a stopwatch property in the message. Here you can send:

  • command : "start", "stop", or "end".
  • name : the name of the process/machine you're monitoring
  • job: the name of the recipe/job that you are monitoring within the machine or process.

The subflow will check first if the machine has a timer on the global variables or not. If not, it will create it.

If the command sent is "start" and the last command was blank or was "end", it will record the timestamp (in ms) for the starting point, as well as the job name. If the last command sent to this machine was also "start" but for a different job, the start point will be overwritten. In both cases, the elapsed time will be set to 0. If the last command was "stop", it will keep the elapsed time as intact.

If the command sent was "stop", and the last one was "start", it will calculate the difference between the stop and start timestamps, and add it to the "elapsed" property in the timer. It won't do anything for any other case.

If the command sent is "end", it will calculate the difference and add it to elapsed, but it will set both start and stop timestamps to null.

In all cases, the node will store the timer values in the global context, plus will send the elapsed time, timestamps, and last command to msg.stopwatch (for further processing down the line).

The basic idea is that you have a timer that you can start and stop as many times as you want, and it will continue adding the incremental time until you end the measurement. This is useful for controlling uptime/downtime in industrial machines or processes, where you can start a machine, then have an error, and when that error is cleared it will continue production. You can set different machine codes for uptime/downtime and control them independently, and control different recipes/procedures on the same machine by using the "job" property.

2 Likes

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