You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

129 lines
3.6 KiB

// file is coppied from: grafana-ui\src\components\GraphNG\nullInsertThreshold.ts
import { ArrayVector, DataFrame, FieldType } from '@grafana/data';
type InsertMode = (prev: number, next: number, threshold: number) => number;
const INSERT_MODES = {
threshold: (prev: number, next: number, threshold: number) => prev + threshold,
midpoint: (prev: number, next: number, threshold: number) => (prev + next) / 2,
// previous time + 1ms to prevent StateTimeline from forward-interpolating prior state
plusone: (prev: number, next: number, threshold: number) => prev + 1,
};
export function applyNullInsertThreshold(
frame: DataFrame,
refFieldName?: string | null,
refFieldPseudoMax: number | null = null,
insertMode: InsertMode = INSERT_MODES.threshold
): DataFrame {
if (frame.length === 0) {
return frame;
}
const refField = frame.fields.find((field) => {
// note: getFieldDisplayName() would require full DF[]
return refFieldName != null ? field.name === refFieldName : field.type === FieldType.time;
});
if (refField == null) {
return frame;
}
const thresholds = frame.fields.map((field) => field.config.custom?.insertNulls ?? refField.config.interval ?? null);
const uniqueThresholds = new Set<number>(thresholds);
uniqueThresholds.delete(null as any);
if (uniqueThresholds.size === 0) {
return frame;
}
if (uniqueThresholds.size === 1) {
const threshold = uniqueThresholds.values().next().value;
if (threshold <= 0) {
return frame;
}
const refValues = refField.values.toArray();
const frameValues = frame.fields.map((field) => field.values.toArray());
const filledFieldValues = nullInsertThreshold(refValues, frameValues, threshold, refFieldPseudoMax, insertMode);
if (filledFieldValues === frameValues) {
return frame;
}
return {
...frame,
length: filledFieldValues[0].length,
fields: frame.fields.map((field, i) => ({
...field,
values: new ArrayVector(filledFieldValues[i]),
})),
};
}
// TODO: unique threshold-per-field (via overrides) is unimplemented
// should be done by processing each (refField + thresholdA-field1 + thresholdA-field2...)
// as a separate nullInsertThreshold() dataset, then re-join into single dataset via join()
return frame;
}
function nullInsertThreshold(
refValues: number[],
frameValues: any[][],
threshold: number,
// will insert a trailing null when refFieldPseudoMax > last datapoint + threshold
refFieldPseudoMax: number | null = null,
getInsertValue: InsertMode
) {
const len = refValues.length;
let prevValue: number = refValues[0];
const refValuesNew: number[] = [prevValue];
for (let i = 1; i < len; i++) {
const curValue = refValues[i];
if (curValue - prevValue > threshold) {
refValuesNew.push(getInsertValue(prevValue, curValue, threshold));
}
refValuesNew.push(curValue);
prevValue = curValue;
}
if (refFieldPseudoMax != null && prevValue + threshold <= refFieldPseudoMax) {
refValuesNew.push(getInsertValue(prevValue, refFieldPseudoMax, threshold));
}
const filledLen = refValuesNew.length;
if (filledLen === len) {
return frameValues;
}
const filledFieldValues: any[][] = [];
for (let fieldValues of frameValues) {
let filledValues;
if (fieldValues !== refValues) {
filledValues = Array(filledLen);
for (let i = 0, j = 0; i < filledLen; i++) {
filledValues[i] = refValues[j] === refValuesNew[i] ? fieldValues[j++] : null;
}
} else {
filledValues = refValuesNew;
}
filledFieldValues.push(filledValues);
}
return filledFieldValues;
}