mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
fix: add gaps in plots where no data exists (#1065)
Scans the window from the most recent timestamp to however many seconds back are being stored. Then adds blank spaces (NaN values) in between data items that are too far apart. Lastly, if the earliest item is blank, there are probably fewer than the expected number of items. In that case, it adjusts the first timestamp to keep plot width mostly consistent.
This commit is contained in:
parent
bbd530c052
commit
cbb825982b
@ -101,7 +101,8 @@ class SysinfoViewModel {
|
|||||||
viewText: jotai.Atom<string>;
|
viewText: jotai.Atom<string>;
|
||||||
viewName: jotai.Atom<string>;
|
viewName: jotai.Atom<string>;
|
||||||
dataAtom: jotai.PrimitiveAtom<Array<DataItem>>;
|
dataAtom: jotai.PrimitiveAtom<Array<DataItem>>;
|
||||||
addDataAtom: jotai.WritableAtom<unknown, [DataItem[]], void>;
|
addInitialDataAtom: jotai.WritableAtom<unknown, [DataItem[]], void>;
|
||||||
|
addContinuousDataAtom: jotai.WritableAtom<unknown, [DataItem], void>;
|
||||||
incrementCount: jotai.WritableAtom<unknown, [], Promise<void>>;
|
incrementCount: jotai.WritableAtom<unknown, [], Promise<void>>;
|
||||||
loadingAtom: jotai.PrimitiveAtom<boolean>;
|
loadingAtom: jotai.PrimitiveAtom<boolean>;
|
||||||
numPoints: jotai.Atom<number>;
|
numPoints: jotai.Atom<number>;
|
||||||
@ -117,18 +118,57 @@ class SysinfoViewModel {
|
|||||||
this.viewType = viewType;
|
this.viewType = viewType;
|
||||||
this.blockId = blockId;
|
this.blockId = blockId;
|
||||||
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||||
this.addDataAtom = jotai.atom(null, (get, set, points) => {
|
this.addInitialDataAtom = jotai.atom(null, (get, set, points) => {
|
||||||
|
const targetLen = get(this.numPoints) + 1;
|
||||||
|
try {
|
||||||
|
const newDataRaw = [...points];
|
||||||
|
if (newDataRaw.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const latestItemTs = newDataRaw[newDataRaw.length - 1]?.ts ?? 0;
|
||||||
|
const cutoffTs = latestItemTs - 1000 * targetLen;
|
||||||
|
const blankItemTemplate = { ...newDataRaw[newDataRaw.length - 1] };
|
||||||
|
for (const key in blankItemTemplate) {
|
||||||
|
blankItemTemplate[key] = NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDataFiltered = newDataRaw.filter((dataItem) => dataItem.ts >= cutoffTs);
|
||||||
|
if (newDataFiltered.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newDataWithGaps: Array<DataItem> = [];
|
||||||
|
if (newDataFiltered[0].ts > cutoffTs) {
|
||||||
|
const blankItemStart = { ...blankItemTemplate, ts: cutoffTs };
|
||||||
|
const blankItemEnd = { ...blankItemTemplate, ts: newDataFiltered[0].ts - 1 };
|
||||||
|
newDataWithGaps.push(blankItemStart);
|
||||||
|
newDataWithGaps.push(blankItemEnd);
|
||||||
|
}
|
||||||
|
newDataWithGaps.push(newDataFiltered[0]);
|
||||||
|
for (let i = 1; i < newDataFiltered.length; i++) {
|
||||||
|
const prevIdxItem = newDataFiltered[i - 1];
|
||||||
|
const curIdxItem = newDataFiltered[i];
|
||||||
|
const timeDiff = curIdxItem.ts - prevIdxItem.ts;
|
||||||
|
if (timeDiff > 2000) {
|
||||||
|
const blankItemStart = { ...blankItemTemplate, ts: prevIdxItem.ts + 1, blank: 1 };
|
||||||
|
const blankItemEnd = { ...blankItemTemplate, ts: curIdxItem.ts - 1, blank: 1 };
|
||||||
|
newDataWithGaps.push(blankItemStart);
|
||||||
|
newDataWithGaps.push(blankItemEnd);
|
||||||
|
}
|
||||||
|
newDataWithGaps.push(curIdxItem);
|
||||||
|
}
|
||||||
|
set(this.dataAtom, newDataWithGaps);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Error adding data to sysinfo", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.addContinuousDataAtom = jotai.atom(null, (get, set, newPoint) => {
|
||||||
const targetLen = get(this.numPoints) + 1;
|
const targetLen = get(this.numPoints) + 1;
|
||||||
let data = get(this.dataAtom);
|
let data = get(this.dataAtom);
|
||||||
try {
|
try {
|
||||||
if (data.length > targetLen) {
|
const latestItemTs = newPoint?.ts ?? 0;
|
||||||
data = data.slice(data.length - targetLen);
|
const cutoffTs = latestItemTs - 1000 * targetLen;
|
||||||
}
|
data.push(newPoint);
|
||||||
if (data.length < targetLen) {
|
const newData = data.filter((dataItem) => dataItem.ts >= cutoffTs);
|
||||||
const defaultData = this.getDefaultData();
|
|
||||||
data = [...defaultData.slice(defaultData.length - targetLen + data.length), ...data];
|
|
||||||
}
|
|
||||||
const newData = [...data.slice(points.length), ...points];
|
|
||||||
set(this.dataAtom, newData);
|
set(this.dataAtom, newData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Error adding data to sysinfo", e);
|
console.log("Error adding data to sysinfo", e);
|
||||||
@ -188,7 +228,7 @@ class SysinfoViewModel {
|
|||||||
}
|
}
|
||||||
return connValue;
|
return connValue;
|
||||||
});
|
});
|
||||||
this.dataAtom = jotai.atom(this.getDefaultData());
|
this.dataAtom = jotai.atom([]);
|
||||||
this.loadInitialData();
|
this.loadInitialData();
|
||||||
this.connStatus = jotai.atom((get) => {
|
this.connStatus = jotai.atom((get) => {
|
||||||
const blockData = get(this.blockAtom);
|
const blockData = get(this.blockAtom);
|
||||||
@ -214,8 +254,8 @@ class SysinfoViewModel {
|
|||||||
const newData = this.getDefaultData();
|
const newData = this.getDefaultData();
|
||||||
const initialDataItems: DataItem[] = initialData.map(convertWaveEventToDataItem);
|
const initialDataItems: DataItem[] = initialData.map(convertWaveEventToDataItem);
|
||||||
// splice the initial data into the default data (replacing the newest points)
|
// splice the initial data into the default data (replacing the newest points)
|
||||||
newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems);
|
//newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems);
|
||||||
globalStore.set(this.addDataAtom, newData);
|
globalStore.set(this.addInitialDataAtom, initialDataItems);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Error loading initial data for sysinfo", e);
|
console.log("Error loading initial data for sysinfo", e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -301,7 +341,7 @@ function SysinfoView({ model, blockId }: SysinfoViewProps) {
|
|||||||
const connName = jotai.useAtomValue(model.connection);
|
const connName = jotai.useAtomValue(model.connection);
|
||||||
const lastConnName = React.useRef(connName);
|
const lastConnName = React.useRef(connName);
|
||||||
const connStatus = jotai.useAtomValue(model.connStatus);
|
const connStatus = jotai.useAtomValue(model.connStatus);
|
||||||
const addPlotData = jotai.useSetAtom(model.addDataAtom);
|
const addContinuousData = jotai.useSetAtom(model.addContinuousDataAtom);
|
||||||
const loading = jotai.useAtomValue(model.loadingAtom);
|
const loading = jotai.useAtomValue(model.loadingAtom);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -323,7 +363,13 @@ function SysinfoView({ model, blockId }: SysinfoViewProps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dataItem = convertWaveEventToDataItem(event);
|
const dataItem = convertWaveEventToDataItem(event);
|
||||||
addPlotData([dataItem]);
|
const prevData = globalStore.get(model.dataAtom);
|
||||||
|
const prevLastTs = prevData[prevData.length - 1]?.ts ?? 0;
|
||||||
|
if (dataItem.ts - prevLastTs > 2000) {
|
||||||
|
model.loadInitialData();
|
||||||
|
} else {
|
||||||
|
addContinuousData(dataItem);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log("subscribe to sysinfo", connName);
|
console.log("subscribe to sysinfo", connName);
|
||||||
@ -348,6 +394,7 @@ type SingleLinePlotProps = {
|
|||||||
defaultColor: string;
|
defaultColor: string;
|
||||||
title?: boolean;
|
title?: boolean;
|
||||||
sparkline?: boolean;
|
sparkline?: boolean;
|
||||||
|
targetLen: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
function SingleLinePlot({
|
function SingleLinePlot({
|
||||||
@ -358,6 +405,7 @@ function SingleLinePlot({
|
|||||||
defaultColor,
|
defaultColor,
|
||||||
title = false,
|
title = false,
|
||||||
sparkline = false,
|
sparkline = false,
|
||||||
|
targetLen,
|
||||||
}: SingleLinePlotProps) {
|
}: SingleLinePlotProps) {
|
||||||
const containerRef = React.useRef<HTMLInputElement>();
|
const containerRef = React.useRef<HTMLInputElement>();
|
||||||
const domRect = useDimensionsWithExistingRef(containerRef, 300);
|
const domRect = useDimensionsWithExistingRef(containerRef, 300);
|
||||||
@ -440,12 +488,15 @@ function SingleLinePlot({
|
|||||||
);
|
);
|
||||||
let maxY = resolveDomainBound(yvalMeta?.maxy, plotData[plotData.length - 1]) ?? 100;
|
let maxY = resolveDomainBound(yvalMeta?.maxy, plotData[plotData.length - 1]) ?? 100;
|
||||||
let minY = resolveDomainBound(yvalMeta?.miny, plotData[plotData.length - 1]) ?? 0;
|
let minY = resolveDomainBound(yvalMeta?.miny, plotData[plotData.length - 1]) ?? 0;
|
||||||
|
let maxX = plotData[plotData.length - 1].ts;
|
||||||
|
let minX = maxX - targetLen * 1000;
|
||||||
const plot = Plot.plot({
|
const plot = Plot.plot({
|
||||||
axis: !sparkline,
|
axis: !sparkline,
|
||||||
x: {
|
x: {
|
||||||
grid: true,
|
grid: true,
|
||||||
label: "time",
|
label: "time",
|
||||||
tickFormat: (d) => `${dayjs.unix(d / 1000).format("HH:mm:ss")}`,
|
tickFormat: (d) => `${dayjs.unix(d / 1000).format("HH:mm:ss")}`,
|
||||||
|
domain: [minX, maxX],
|
||||||
},
|
},
|
||||||
y: { label: labelY, domain: [minY, maxY] },
|
y: { label: labelY, domain: [minY, maxY] },
|
||||||
width: plotWidth,
|
width: plotWidth,
|
||||||
@ -469,6 +520,7 @@ const SysinfoViewInner = React.memo(({ model }: SysinfoViewProps) => {
|
|||||||
const yvals = jotai.useAtomValue(model.metrics);
|
const yvals = jotai.useAtomValue(model.metrics);
|
||||||
const plotMeta = jotai.useAtomValue(model.plotMetaAtom);
|
const plotMeta = jotai.useAtomValue(model.plotMetaAtom);
|
||||||
const osRef = React.useRef<OverlayScrollbarsComponentRef>();
|
const osRef = React.useRef<OverlayScrollbarsComponentRef>();
|
||||||
|
const targetLen = jotai.useAtomValue(model.numPoints) + 1;
|
||||||
let title = false;
|
let title = false;
|
||||||
let cols2 = false;
|
let cols2 = false;
|
||||||
if (yvals.length > 1) {
|
if (yvals.length > 1) {
|
||||||
@ -491,6 +543,7 @@ const SysinfoViewInner = React.memo(({ model }: SysinfoViewProps) => {
|
|||||||
blockId={model.blockId}
|
blockId={model.blockId}
|
||||||
defaultColor={"var(--accent-color)"}
|
defaultColor={"var(--accent-color)"}
|
||||||
title={title}
|
title={title}
|
||||||
|
targetLen={targetLen}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
Loading…
Reference in New Issue
Block a user