Display line chart data as Elapsed Time since the first event was received

Normally, the ui_chart node expects the x-axis values to be real-time dates or timestamps. In this case, we want to see 00:00:00 on the left side of the chart, with elapsed time offsets along the x-axis. Currently, this mode is not supported by the chart node.

elapsed time chart example

Instead of passing each msg individually to the chart, the function node context is used to hold a growing array of relative timestamps, which is passed into the chart node every time a new msg arrives. NB: In order to show the elapsed time starting at 00:00:00 (local time), each relative timestamp must be adjusted for the current timezone's offset from UTC at the time of the epoch.

elapsed time flow sample

The function node's context can be reset by passing in an empty array, or can be "pre-loaded" with a saved data array. The format of the saved data array must match the sample "old data" inject node in this flow -- namely, the first data point's x-value is the absolute timestamp, and the rest of the data points are relative offsets (in millis) to that timestamp:

[[1497621600000,0],[18000000,86],[18030004,75],[18060007,92],[18090008,90]]

Finally, the array is trimmed to remove old data once it gets above a certain size (e.g. 100 points). This prevents the flow from unbounded growth, and avoids overloading the chart with points that cannot be differentiated.

[
    {
        "id": "f0434b54.e1d088",
        "type": "inject",
        "z": "d388adbc.37c1c",
        "name": "",
        "topic": "Current",
        "payload": "",
        "payloadType": "date",
        "repeat": "30",
        "crontab": "",
        "once": false,
        "x": 160,
        "y": 740,
        "wires": [
            [
                "1decfb98.ae60d4"
            ]
        ]
    },
    {
        "id": "1decfb98.ae60d4",
        "type": "function",
        "z": "d388adbc.37c1c",
        "name": "Random",
        "func": "msg.payload = Math.round(Math.random() * 100);\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 360,
        "y": 740,
        "wires": [
            [
                "f601569c.b2ebe8",
                "48d2c199.c9ff4",
                "4ec2099d.b43a08"
            ]
        ]
    },
    {
        "id": "d15c669e.b12458",
        "type": "inject",
        "z": "d388adbc.37c1c",
        "name": "Clear all data",
        "topic": "",
        "payload": "[]",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 190,
        "y": 800,
        "wires": [
            [
                "4ec2099d.b43a08"
            ]
        ]
    },
    {
        "id": "4d54bec5.f29c",
        "type": "debug",
        "z": "d388adbc.37c1c",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "false",
        "x": 750,
        "y": 780,
        "wires": []
    },
    {
        "id": "a3109e12.c21cb",
        "type": "ui_chart",
        "z": "d388adbc.37c1c",
        "name": "",
        "group": "f3e75316.ec407",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Elapsed time",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "x": 570,
        "y": 800,
        "wires": [
            [
                "4d54bec5.f29c"
            ],
            []
        ]
    },
    {
        "id": "4ec2099d.b43a08",
        "type": "function",
        "z": "d388adbc.37c1c",
        "name": "relative data",
        "func": "// get current time and context array\nvar now = Date.now();\nvar off = new Date(70, 0, 1) - new Date(0);\nvar arr = context.get(\"data\") || [];\n\n// reset the data if empty array is received\nif (Array.isArray(msg.payload)) {\n    if (msg.payload.length === 0) {\n        context.set(\"data\", msg.payload);\n        return msg; // to clear the chart\n    }\n    else {\n        arr = msg.payload;\n    }\n}\nelse {\n    // get/set the initial (absolute) timestamp\n    var dt0 = arr.length ? arr[0][0] : now;\n    if (arr.length === 0) {\n        arr.push([ dt0, 0 ]);\n    }\n    \n    // calculate time since last msg\n    var dt1 = msg.timestamp || now;\n    var dlt = (dt1 - dt0) + off;\n    \n    // add new relative data point to array\n    arr.push([ dlt, msg.payload ]);\n}\n\n// limit the array to the last 100 points\nvar lim = 100;\nif (arr.length > lim+1) {\n    // remove the oldest point(s)\n    arr.splice(1, arr.length-lim-1);\n}\ncontext.set(\"data\", arr);\n\n// build the chart msg with the array\nvar key = msg.topic || \"Test Data\";\nmsg.payload = [{ \"key\": key, \"values\": arr.slice(1) }];\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 390,
        "y": 800,
        "wires": [
            [
                "a3109e12.c21cb"
            ]
        ]
    },
    {
        "id": "f01469e4.4d9838",
        "type": "inject",
        "z": "d388adbc.37c1c",
        "name": "Historical data",
        "topic": "History",
        "payload": "[[1497621600000,0],[18000000,86],[18030004,5],[18060007,92],[18090008,90]]",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 190,
        "y": 840,
        "wires": [
            [
                "4ec2099d.b43a08"
            ]
        ]
    },
    {
        "id": "f3e75316.ec407",
        "type": "ui_group",
        "z": "",
        "name": "Relative",
        "tab": "379107b8.4087e8",
        "order": 2,
        "disp": true,
        "width": "12"
    },
    {
        "id": "379107b8.4087e8",
        "type": "ui_tab",
        "z": "",
        "name": "Random",
        "icon": "dashboard"
    }
]
shrickus

Flow Info

created 4 months, 2 weeks ago

Node Types

Core
  • debug (x1)
  • function (x2)
  • inject (x3)
Other
  • ui_chart (x1)
  • ui_group (x1)
  • ui_tab (x1)

Tags

  • chart
  • elapsed
  • time
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option