diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 8014e06f43f..85821c19742 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; // Types -import { TimeRange, LoadingState, DataQueryResponse, TimeSeries } from 'app/types'; +import { TimeRange, LoadingState, DataQueryOptions, DataQueryResponse, TimeSeries } from 'app/types'; interface RenderProps { loading: LoadingState; @@ -82,14 +82,14 @@ export class DataPanel extends Component { const dataSourceSrv = getDatasourceSrv(); const ds = await dataSourceSrv.get(datasource); - const queryOptions = { + const queryOptions: DataQueryOptions = { timezone: 'browser', panelId: panelId, dashboardId: dashboardId, range: timeRange, rangeRaw: timeRange.raw, interval: '1s', - intervalMs: 1000, + intervalMs: 60000, targets: queries, maxDataPoints: 500, scopedVars: {}, diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 7a8e24539f7..bee9b9ec9b0 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -339,6 +339,7 @@ class GraphElement { callPlot(options, incrementRenderCounter) { try { + console.log('plot', this.sortedSeries); this.plot = $.plot(this.elem, this.sortedSeries, options); if (this.ctrl.renderError) { delete this.ctrl.error; diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index e8d594e7968..c2b8c355440 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -4,12 +4,10 @@ import React, { PureComponent } from 'react'; // Components import Graph from 'app/viz/Graph'; - -// Utils import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; // Types -import { PanelProps } from 'app/types'; +import { PanelProps, NullValueMode } from 'app/types'; interface Options { showBars: boolean; @@ -26,10 +24,13 @@ export class Graph2 extends PureComponent { render() { const { timeSeries, timeRange } = this.props; - const viewModels = getTimeSeriesVMs({ timeSeries }); - console.log(viewModels); - return ; + const vmSeries = getTimeSeriesVMs({ + timeSeries: timeSeries, + nullValueMode: NullValueMode.Ignore, + }); + + return ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index d274c99253c..b45b5f3e320 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -15,6 +15,8 @@ import { TimeSeries, TimeSeriesVM, TimeSeriesVMs, + TimeSeriesStats, + NullValueMode, DataQuery, DataQueryResponse, DataQueryOptions, @@ -62,6 +64,8 @@ export { TimeSeries, TimeSeriesVM, TimeSeriesVMs, + NullValueMode, + TimeSeriesStats, DataQuery, DataQueryResponse, DataQueryOptions, diff --git a/public/app/types/series.ts b/public/app/types/series.ts index 9a906377636..5396880611b 100644 --- a/public/app/types/series.ts +++ b/public/app/types/series.ts @@ -33,6 +33,30 @@ export interface TimeSeriesVM { label: string; color: string; data: TimeSeriesValue[][]; + stats: TimeSeriesStats; +} + +export interface TimeSeriesStats { + total: number; + max: number; + min: number; + logmin: number; + avg: number | null; + current: number | null; + first: number | null; + delta: number; + diff: number | null; + range: number | null; + timeStep: number; + count: number; + allIsNull: boolean; + allIsZero: boolean; +} + +export enum NullValueMode { + Null = 'null', + Ignore = 'connected', + AsZero = 'null as zero', } /** View model projection of many time series */ @@ -56,6 +80,10 @@ export interface DataQueryOptions { panelId: number; dashboardId: number; cacheTimeout?: string; + interval: string; + intervalMs: number; + maxDataPoints: number; + scopedVars: object; } export interface DataSourceApi { diff --git a/public/app/viz/Graph.tsx b/public/app/viz/Graph.tsx index 38d29af8da9..fab65225715 100644 --- a/public/app/viz/Graph.tsx +++ b/public/app/viz/Graph.tsx @@ -108,6 +108,7 @@ export class Graph extends PureComponent { ...dynamicOptions, }; + console.log('plot', timeSeries, options); $.plot(this.element, timeSeries, options); } diff --git a/public/app/viz/state/timeSeries.ts b/public/app/viz/state/timeSeries.ts index 7658a2b2120..e22cb4681b7 100644 --- a/public/app/viz/state/timeSeries.ts +++ b/public/app/viz/state/timeSeries.ts @@ -1,19 +1,166 @@ +// Libraries +import _ from 'lodash'; + +// Utils import colors from 'app/core/utils/colors'; -import { TimeSeries, TimeSeriesVMs } from 'app/types'; + +// Types +import { TimeSeries, TimeSeriesVMs, NullValueMode } from 'app/types'; interface Options { timeSeries: TimeSeries[]; + nullValueMode: NullValueMode; } -export function getTimeSeriesVMs({ timeSeries }: Options): TimeSeriesVMs { +export function getTimeSeriesVMs({ timeSeries, nullValueMode }: Options): TimeSeriesVMs { const vmSeries = timeSeries.map((item, index) => { const colorIndex = index % colors.length; const label = item.target; + const result = []; + + // stat defaults + let total = 0; + let max = -Number.MAX_VALUE; + let min = Number.MAX_VALUE; + let logmin = Number.MAX_VALUE; + let avg = null; + let current = null; + let first = null; + let delta = 0; + let diff = null; + let range = null; + let timeStep = Number.MAX_VALUE; + let allIsNull = true; + let allIsZero = true; + + const ignoreNulls = nullValueMode === NullValueMode.Ignore; + const nullAsZero = nullValueMode === NullValueMode.AsZero; + + let currentTime; + let currentValue; + let nonNulls = 0; + let previousTime; + let previousValue = 0; + let previousDeltaUp = true; + + for (let i = 0; i < item.datapoints.length; i++) { + currentValue = item.datapoints[i][0]; + currentTime = item.datapoints[i][1]; + + // Due to missing values we could have different timeStep all along the series + // so we have to find the minimum one (could occur with aggregators such as ZimSum) + if (previousTime !== undefined) { + const currentStep = currentTime - previousTime; + if (currentStep < timeStep) { + timeStep = currentStep; + } + } + + previousTime = currentTime; + + if (currentValue === null) { + if (ignoreNulls) { + continue; + } + if (nullAsZero) { + currentValue = 0; + } + } + + if (currentValue !== null) { + if (_.isNumber(currentValue)) { + total += currentValue; + allIsNull = false; + nonNulls++; + } + + if (currentValue > max) { + max = currentValue; + } + + if (currentValue < min) { + min = currentValue; + } + + if (first === null) { + first = currentValue; + } else { + if (previousValue > currentValue) { + // counter reset + previousDeltaUp = false; + if (i === item.datapoints.length - 1) { + // reset on last + delta += currentValue; + } + } else { + if (previousDeltaUp) { + delta += currentValue - previousValue; // normal increment + } else { + delta += currentValue; // account for counter reset + } + previousDeltaUp = true; + } + } + previousValue = currentValue; + + if (currentValue < logmin && currentValue > 0) { + logmin = currentValue; + } + + if (currentValue !== 0) { + allIsZero = false; + } + } + + result.push([currentTime, currentValue]); + } + + if (max === -Number.MAX_VALUE) { + max = null; + } + + if (min === Number.MAX_VALUE) { + min = null; + } + + if (result.length && !allIsNull) { + avg = total / nonNulls; + current = result[result.length - 1][1]; + if (current === null && result.length > 1) { + current = result[result.length - 2][1]; + } + } + + if (max !== null && min !== null) { + range = max - min; + } + + if (current !== null && first !== null) { + diff = current - first; + } + + const count = result.length; return { - data: item.datapoints, + data: result, label: label, color: colors[colorIndex], + stats: { + total, + min, + max, + current, + logmin, + avg, + diff, + delta, + timeStep, + range, + count, + first, + allIsZero, + allIsNull, + }, }; });