123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- import { BehaviorSubject } from 'rxjs';
- 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 = new BehaviorSubject(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;
- const oldOk = this.ok.getValue();
- const newOk = this.budget <= this.threshold;
- if (oldOk !== newOk) {
- this.ok.next(newOk);
- }
- this.lastUpdate = now;
- // For live dashboards, listen to changes
- if (this.isLive && this.ok.getValue() && 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);
|