Skip to content

Commit a60a579

Browse files
authored
Merge pull request #1992 from FlowFuse/1986-improve-hisrocal-chart-data-pt2
Improve historical chart data processing
2 parents 177593b + 5e09b8e commit a60a579

4 files changed

Lines changed: 151 additions & 73 deletions

File tree

cypress/fixtures/flows/dashboard-chart-line-max-3-points.json renamed to cypress/fixtures/flows/dashboard-chart-line-max-limits.json

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
{
33
"id": "chart-1",
44
"type": "ui-chart",
5-
"z": "9524c548fb0b956c",
5+
"z": "b508b2cf4781499a",
66
"group": "dashboard-ui-group",
7-
"name": "line-append-max-3-points",
8-
"label": "line-append-max-3-points",
7+
"name": "line-append-max-3-seconds",
8+
"label": "line-append-max-3-seconds",
99
"order": 2,
1010
"chartType": "line",
1111
"category": "",
@@ -16,18 +16,22 @@
1616
"xAxisType": "time",
1717
"xAxisFormat": "",
1818
"xAxisFormatType": "auto",
19+
"xmin": "",
20+
"xmax": "",
1921
"yAxisLabel": "",
2022
"yAxisProperty": "",
23+
"yAxisPropertyType": "property",
2124
"ymin": "",
2225
"ymax": "",
26+
"bins": "",
2327
"action": "append",
2428
"stackSeries": false,
2529
"pointShape": "circle",
2630
"pointRadius": 4,
2731
"showLegend": true,
28-
"removeOlder": 1,
29-
"removeOlderUnit": "3600",
30-
"removeOlderPoints": "3",
32+
"removeOlder": "3",
33+
"removeOlderUnit": "1",
34+
"removeOlderPoints": "",
3135
"colors": [
3236
"#1f77b4",
3337
"#aec7e8",
@@ -50,16 +54,17 @@
5054
"width": 6,
5155
"height": "4",
5256
"className": "",
53-
"x": 1690,
54-
"y": 640,
57+
"interpolation": "linear",
58+
"x": 580,
59+
"y": 40,
5560
"wires": [
5661
[]
5762
]
5863
},
5964
{
6065
"id": "da1332f160b6a255",
6166
"type": "inject",
62-
"z": "9524c548fb0b956c",
67+
"z": "b508b2cf4781499a",
6368
"name": "",
6469
"props": [
6570
{
@@ -77,8 +82,8 @@
7782
"topic": "",
7883
"payload": "[]",
7984
"payloadType": "json",
80-
"x": 1490,
81-
"y": 680,
85+
"x": 370,
86+
"y": 80,
8287
"wires": [
8388
[
8489
"chart-1"
@@ -88,7 +93,7 @@
8893
{
8994
"id": "13bf93c81c0bd0a7",
9095
"type": "change",
91-
"z": "9524c548fb0b956c",
96+
"z": "b508b2cf4781499a",
9297
"name": "rand",
9398
"rules": [
9499
{
@@ -104,8 +109,8 @@
104109
"from": "",
105110
"to": "",
106111
"reg": false,
107-
"x": 1490,
108-
"y": 640,
112+
"x": 370,
113+
"y": 40,
109114
"wires": [
110115
[
111116
"chart-1"
@@ -115,11 +120,11 @@
115120
{
116121
"id": "12a55e898bb4162f",
117122
"type": "ui-button",
118-
"z": "9524c548fb0b956c",
123+
"z": "b508b2cf4781499a",
119124
"group": "dashboard-ui-group",
120125
"name": "random",
121126
"label": "random",
122-
"order": 0,
127+
"order": 1,
123128
"width": 0,
124129
"height": 0,
125130
"emulateClick": true,
@@ -136,8 +141,9 @@
136141
"buttonColor": "",
137142
"textColor": "",
138143
"iconColor": "",
139-
"x": 1340,
140-
"y": 640,
144+
"enableClick": true,
145+
"x": 220,
146+
"y": 40,
141147
"wires": [
142148
[
143149
"13bf93c81c0bd0a7"
@@ -161,33 +167,36 @@
161167
"id": "dashboard-ui-page-1",
162168
"type": "ui-page",
163169
"name": "import demos",
164-
"ui": "dashboard-ui-base",
170+
"ui": "72c1e5a9ec204878",
165171
"path": "/page4",
166172
"icon": "home",
167173
"layout": "grid",
168174
"theme": "dashboard-ui-theme",
169-
"order": 1,
175+
"order": 2,
170176
"className": "",
171177
"visible": "true",
172178
"disabled": "false"
173179
},
174180
{
175-
"id": "dashboard-ui-base",
181+
"id": "72c1e5a9ec204878",
176182
"type": "ui-base",
177-
"name": "base",
183+
"name": "My Dashboard",
178184
"path": "/dashboard",
185+
"appIcon": "",
179186
"includeClientData": true,
180187
"acceptsClientConfig": [
181188
"ui-notification",
182189
"ui-control"
183190
],
184191
"showPathInSidebar": false,
192+
"headerContent": "page",
185193
"navigationStyle": "default",
186-
"titleBarStyle": "default"
194+
"titleBarStyle": "default",
195+
"showReconnectNotification": true,
196+
"notificationDisplayTime": 5,
197+
"showDisconnectNotification": true,
198+
"allowInstall": true
187199
},
188-
189-
190-
191200
{
192201
"id": "dashboard-ui-theme",
193202
"type": "ui-theme",
@@ -207,4 +216,4 @@
207216
"density": "default"
208217
}
209218
}
210-
]
219+
]

cypress/tests/widgets/chart-line-max-3-points.spec.js

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/// <reference types="cypress" />
2+
import should from 'should'
3+
4+
/* eslint-disable cypress/no-unnecessary-waiting */
5+
describe('Node/-RED Dashboard 2.0 - Chart Widget - line - max limits', () => {
6+
it('should limit the chart data to 3 points', () => {
7+
const overrides = [
8+
{
9+
id: 'chart-1', // the ID in the base flow fixture (required)
10+
mode: 'merge',
11+
data: {
12+
name: 'line-append-max-3-points',
13+
label: 'line-append-max-3-points',
14+
removeOlder: 1, // 1 of unit (e.g. 1 x 3600 seconds == 1 hour)
15+
removeOlderUnit: '3600', // seconds
16+
removeOlderPoints: '3' // maximum of 3 points
17+
}
18+
}
19+
]
20+
cy.deployFixture('dashboard-chart-line-max-limits', overrides)
21+
cy.visit('/dashboard/page1')
22+
cy.reload()
23+
24+
cy.get('#nrdb-ui-widget-chart-1 > div > div').should('exist')
25+
// operate input button 5 times
26+
cy.clickAndWait(cy.get('button').contains('random'))
27+
cy.wait(10)
28+
cy.clickAndWait(cy.get('button').contains('random'))
29+
cy.wait(10)
30+
cy.clickAndWait(cy.get('button').contains('random'))
31+
cy.wait(10)
32+
cy.clickAndWait(cy.get('button').contains('random'))
33+
cy.wait(10)
34+
cy.clickAndWait(cy.get('button').contains('random'))
35+
cy.wait(10)
36+
37+
// eslint-disable-next-line promise/catch-or-return, promise/always-return
38+
cy.window().then(win => {
39+
should(win.uiCharts).is.not.empty()
40+
const chart = win.uiCharts['chart-1']
41+
should(chart).is.not.empty()
42+
const options = chart.chart.getOption()
43+
should(options.series).be.an.Array()
44+
45+
should(options.series).be.an.Array().and.have.length(1)
46+
should(options.series[0].data).be.an.Array().and.have.length(3)
47+
})
48+
})
49+
it('should limit the chart data to 3 seconds', () => {
50+
const overrides = [
51+
{
52+
id: 'chart-1', // the ID in the base flow fixture (required)
53+
mode: 'merge',
54+
data: {
55+
name: 'line-append-max-3-seconds',
56+
label: 'line-append-max-3-seconds',
57+
removeOlder: '3', // 3 of unit (e.g. 3 x 1 seconds == 3 seconds)
58+
removeOlderUnit: '1', // seconds
59+
removeOlderPoints: ''
60+
}
61+
}
62+
]
63+
cy.deployFixture('dashboard-chart-line-max-limits', overrides)
64+
cy.visit('/dashboard/page1')
65+
cy.reload()
66+
67+
cy.get('#nrdb-ui-widget-chart-1 > div > div').should('exist')
68+
// operate input button (add 3 points)
69+
cy.clickAndWait(cy.get('button').contains('random'))
70+
cy.wait(10)
71+
cy.clickAndWait(cy.get('button').contains('random'))
72+
cy.wait(10)
73+
cy.clickAndWait(cy.get('button').contains('random'))
74+
75+
// wait some time to allow data pruning to occur
76+
cy.wait(3500)
77+
78+
// add 2 more points
79+
cy.clickAndWait(cy.get('button').contains('random'))
80+
cy.wait(10)
81+
cy.clickAndWait(cy.get('button').contains('random'))
82+
cy.wait(10)
83+
84+
// eslint-disable-next-line promise/catch-or-return, promise/always-return
85+
cy.window().then(win => {
86+
should(win.uiCharts).is.not.empty()
87+
const chart = win.uiCharts['chart-1']
88+
should(chart).is.not.empty()
89+
const options = chart.chart.getOption()
90+
should(options.series).be.an.Array()
91+
92+
should(options.series).be.an.Array().and.have.length(1)
93+
should(options.series[0].data).be.an.Array().and.have.length(2)
94+
})
95+
})
96+
})

nodes/widgets/ui_chart.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -266,21 +266,31 @@ module.exports = function (RED) {
266266
if (maxPoints && config.removeOlderPoints) {
267267
// account for multiple lines?
268268
// client-side does this for _each_ line
269-
// remove older points
269+
// remove older points using datastore.filter instead of saving the whole array
270270
const lineCounts = {}
271-
const _msg = datastore.get(node.id)
272-
// trawl through in reverse order, and only keep the latest points (up to maxPoints) for each label
271+
const _msg = datastore.get(node.id) || []
272+
273+
// determine which message objects to keep (latest maxPoints per label)
274+
const keepIndexes = []
275+
let doFiltering = false
273276
for (let i = _msg.length - 1; i >= 0; i--) {
274-
const msg = _msg[i]
275-
const label = msg.topic
277+
const m = _msg[i]
278+
const label = m.topic
276279
lineCounts[label] = lineCounts[label] || 0
277-
if (lineCounts[label] >= maxPoints) {
278-
_msg.splice(i, 1)
279-
} else {
280+
if (lineCounts[label] < maxPoints) {
281+
keepIndexes[i] = true
280282
lineCounts[label]++
283+
} else {
284+
doFiltering = true
281285
}
282286
}
283-
datastore.save(base, node, _msg)
287+
288+
// filter the datastore to only keep the selected messages
289+
if (doFiltering) {
290+
datastore.filter(base, node, (m, i) => {
291+
return keepIndexes[i]
292+
})
293+
}
284294
}
285295

286296
if (config.xAxisType === 'time' && config.removeOlder && config.removeOlderUnit) {

0 commit comments

Comments
 (0)