summaryrefslogtreecommitdiffstats
path: root/modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js
blob: a198dbc02d6dfd0e4cf0293f293fc46874b36b97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
'use strict';
'require baseclass';
'require rpc';

const callHistory = rpc.declare({
	object: 'luci.dashboard',
	method: 'history',
	params: [ 'since', 'id' ]
});

const SEQ = 0, TIME = 1;

return baseclass.extend({
	samples: [],
	step: 5000,
	slots: 60,

	fetch() {
		const last = this.samples[this.samples.length - 1];

		return L.resolveDefault(callHistory(last ? last[SEQ] : 0, this.id || ''), null).then(res => {
			this.failed = (!L.isObject(res) || !Array.isArray(res.samples));

			if (this.failed) {
				// Old samples must not appear as current CPU usage or live traffic.
				this.samples = [];
				return;
			}

			if (res.id != this.id)
				this.samples = [];

			this.id = res.id;
			this.step = res.step;
			this.slots = res.slots;
			this.samples = this.samples.concat(res.samples).filter(sample => sample[TIME] > res.now - res.step * res.slots);
		});
	},

	load() {
		const now = performance.now();

		if (this.pending == null || now - this.asked > 1000) {
			this.asked = now;
			this.pending = this.fetch();
		}

		return this.pending;
	},

	status() {
		return this.failed ? _('No data received') : _('Collecting data...');
	},

	span() {
		return this.slots * this.step / 1000;
	},

	near(prev, cur) {
		return (prev != null && cur[TIME] - prev[TIME] <= 2.5 * this.step);
	},

	pair() {
		const cur = this.samples[this.samples.length - 1];
		const prev = this.samples[this.samples.length - 2];

		return this.near(prev, cur) ? [ prev, cur ] : null;
	},

	// Points `{ t, v }` for a chart. `fn(cur, prev, seconds)` gives a value or
	// null; `prev` is null where sampling had paused, which leaves a gap.
	series(fn) {
		const points = [];

		this.samples.forEach((cur, i) => {
			const prev = this.samples[i - 1];
			const near = this.near(prev, cur);

			if (prev != null && !near)
				points.push({ t: cur[TIME] - 1, v: null });

			points.push({ t: cur[TIME], v: fn(cur, near ? prev : null, near ? (cur[TIME] - prev[TIME]) / 1000 : 0) });
		});

		return points;
	}
});