mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
114 lines
3.0 KiB
TypeScript
114 lines
3.0 KiB
TypeScript
import { dateMath, dateTime, TimeRange } from '@grafana/data';
|
|
import { PanelChrome } from './PanelChrome';
|
|
|
|
// target is 20hz (50ms), but we poll at 100ms to smooth out jitter
|
|
const interval = 100;
|
|
|
|
interface LiveListener {
|
|
last: number;
|
|
intervalMs: number;
|
|
panel: PanelChrome;
|
|
}
|
|
|
|
class LiveTimer {
|
|
listeners: LiveListener[] = [];
|
|
|
|
budget = 1;
|
|
threshold = 1.5; // trial and error appears about right
|
|
ok = true;
|
|
lastUpdate = Date.now();
|
|
|
|
isLive = false; // the dashboard time range ends in "now"
|
|
timeRange?: TimeRange;
|
|
liveTimeOffset = 0;
|
|
|
|
/** Called when the dashboard time range changes */
|
|
setLiveTimeRange(v?: TimeRange) {
|
|
this.timeRange = v;
|
|
this.isLive = v?.raw?.to === 'now';
|
|
|
|
if (this.isLive) {
|
|
const from = dateMath.parse(v!.raw.from, false)?.valueOf()!;
|
|
const to = dateMath.parse(v!.raw.to, true)?.valueOf()!;
|
|
this.liveTimeOffset = to - from;
|
|
|
|
for (const listener of this.listeners) {
|
|
listener.intervalMs = getLiveTimerInterval(this.liveTimeOffset, listener.panel.props.width);
|
|
}
|
|
}
|
|
}
|
|
|
|
listen(panel: PanelChrome) {
|
|
this.listeners.push({
|
|
last: this.lastUpdate,
|
|
panel: panel,
|
|
intervalMs: getLiveTimerInterval(
|
|
60000, // 1min
|
|
panel.props.width
|
|
),
|
|
});
|
|
}
|
|
|
|
remove(panel: PanelChrome) {
|
|
this.listeners = this.listeners.filter((v) => v.panel !== panel);
|
|
}
|
|
|
|
updateInterval(panel: PanelChrome) {
|
|
if (!this.timeRange || !this.isLive) {
|
|
return;
|
|
}
|
|
for (const listener of this.listeners) {
|
|
if (listener.panel === panel) {
|
|
listener.intervalMs = getLiveTimerInterval(this.liveTimeOffset, listener.panel.props.width);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called at the consistent dashboard interval
|
|
measure = () => {
|
|
const now = Date.now();
|
|
this.budget = (now - this.lastUpdate) / interval;
|
|
this.ok = this.budget <= this.threshold;
|
|
this.lastUpdate = now;
|
|
|
|
// For live dashboards, listen to changes
|
|
if (this.ok && this.isLive && this.timeRange) {
|
|
// when the time-range is relative fire events
|
|
let tr: TimeRange | undefined = undefined;
|
|
for (const listener of this.listeners) {
|
|
if (!listener.panel.props.isInView) {
|
|
continue;
|
|
}
|
|
|
|
const elapsed = now - listener.last;
|
|
if (elapsed >= listener.intervalMs) {
|
|
if (!tr) {
|
|
const { raw } = this.timeRange;
|
|
tr = {
|
|
raw,
|
|
from: dateTime(now - this.liveTimeOffset),
|
|
to: dateTime(now),
|
|
};
|
|
}
|
|
listener.panel.liveTimeChanged(tr);
|
|
listener.last = now;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const FIVE_MINS = 5 * 60 * 1000;
|
|
|
|
export function getLiveTimerInterval(delta: number, width: number): number {
|
|
const millisPerPixel = Math.ceil(delta / width / 100) * 100;
|
|
if (millisPerPixel > FIVE_MINS) {
|
|
return FIVE_MINS;
|
|
}
|
|
return millisPerPixel;
|
|
}
|
|
|
|
export const liveTimer = new LiveTimer();
|
|
setInterval(liveTimer.measure, interval);
|