diff --git a/pkg/datasource/zabbix.go b/pkg/datasource/zabbix.go index 8b9e704..0f1227c 100644 --- a/pkg/datasource/zabbix.go +++ b/pkg/datasource/zabbix.go @@ -1,10 +1,11 @@ package datasource import ( - "github.com/alexanderzobnin/grafana-zabbix/pkg/timeseries" "strings" "time" + "github.com/alexanderzobnin/grafana-zabbix/pkg/timeseries" + "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" @@ -123,6 +124,10 @@ func (ds *ZabbixDatasourceInstance) applyDataProcessing(ctx context.Context, que } } } + + if len(series) > 1 { + series = timeseries.AlignSeriesIntervals(series) + } } series, err := applyFunctions(series, query.Functions) diff --git a/pkg/timeseries/align.go b/pkg/timeseries/align.go index f2781c0..4567f74 100644 --- a/pkg/timeseries/align.go +++ b/pkg/timeseries/align.go @@ -80,3 +80,64 @@ func (ts TimeSeries) DetectInterval() time.Duration { midIndex := int(math.Floor(float64(len(deltas)) * 0.5)) return time.Duration(deltas[midIndex]) * time.Millisecond } + +// AlignSeriesIntervals aligns series to the same time interval +func AlignSeriesIntervals(series []*TimeSeriesData) []*TimeSeriesData { + if len(series) == 0 { + return series + } + + minInterval := *series[0].Meta.Interval + for _, s := range series { + if *s.Meta.Interval < minInterval { + minInterval = *s.Meta.Interval + } + } + + // 0 interval means series is not aligned, so it's tricky to align multiple series + if minInterval == 0 { + return series + } + + for _, s := range series { + if s.Len() < 2 || *s.Meta.Interval == minInterval { + continue + } + + s.TS = s.TS.Interpolate(minInterval) + } + + return series +} + +func (ts TimeSeries) Interpolate(interval time.Duration) TimeSeries { + if interval <= 0 || ts.Len() < 2 { + return ts + } + + alignedTs := NewTimeSeries() + var frameTs = ts[0].Time + var pointFrameTs time.Time + var point TimePoint + var nextPoint TimePoint + + for i := 0; i < ts.Len()-1; i++ { + point = ts[i] + nextPoint = ts[i+1] + pointFrameTs = point.Time + + if point.Value != nil && nextPoint.Value != nil { + frameTs = pointFrameTs.Add(interval) + for frameTs.Before(nextPoint.Time) { + pointValue := linearInterpolation(frameTs, point, nextPoint) + alignedTs = append(alignedTs, TimePoint{Time: frameTs, Value: &pointValue}) + frameTs = frameTs.Add(interval) + } + } + + alignedTs = append(alignedTs, TimePoint{Time: pointFrameTs, Value: point.Value}) + frameTs = frameTs.Add(interval) + } + + return alignedTs +}