diff --git a/frontend/app/view/sysinfo/sysinfo.tsx b/frontend/app/view/sysinfo/sysinfo.tsx index f4392fc26..8dcd20f1d 100644 --- a/frontend/app/view/sysinfo/sysinfo.tsx +++ b/frontend/app/view/sysinfo/sysinfo.tsx @@ -101,7 +101,8 @@ class SysinfoViewModel { viewText: jotai.Atom; viewName: jotai.Atom; dataAtom: jotai.PrimitiveAtom>; - addDataAtom: jotai.WritableAtom; + addInitialDataAtom: jotai.WritableAtom; + addContinuousDataAtom: jotai.WritableAtom; incrementCount: jotai.WritableAtom>; loadingAtom: jotai.PrimitiveAtom; numPoints: jotai.Atom; @@ -117,18 +118,57 @@ class SysinfoViewModel { this.viewType = viewType; this.blockId = blockId; this.blockAtom = WOS.getWaveObjectAtom(`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 = []; + 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; let data = get(this.dataAtom); try { - if (data.length > targetLen) { - data = data.slice(data.length - targetLen); - } - if (data.length < targetLen) { - const defaultData = this.getDefaultData(); - data = [...defaultData.slice(defaultData.length - targetLen + data.length), ...data]; - } - const newData = [...data.slice(points.length), ...points]; + const latestItemTs = newPoint?.ts ?? 0; + const cutoffTs = latestItemTs - 1000 * targetLen; + data.push(newPoint); + const newData = data.filter((dataItem) => dataItem.ts >= cutoffTs); set(this.dataAtom, newData); } catch (e) { console.log("Error adding data to sysinfo", e); @@ -188,7 +228,7 @@ class SysinfoViewModel { } return connValue; }); - this.dataAtom = jotai.atom(this.getDefaultData()); + this.dataAtom = jotai.atom([]); this.loadInitialData(); this.connStatus = jotai.atom((get) => { const blockData = get(this.blockAtom); @@ -214,8 +254,8 @@ class SysinfoViewModel { const newData = this.getDefaultData(); const initialDataItems: DataItem[] = initialData.map(convertWaveEventToDataItem); // splice the initial data into the default data (replacing the newest points) - newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems); - globalStore.set(this.addDataAtom, newData); + //newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems); + globalStore.set(this.addInitialDataAtom, initialDataItems); } catch (e) { console.log("Error loading initial data for sysinfo", e); } finally { @@ -301,7 +341,7 @@ function SysinfoView({ model, blockId }: SysinfoViewProps) { const connName = jotai.useAtomValue(model.connection); const lastConnName = React.useRef(connName); const connStatus = jotai.useAtomValue(model.connStatus); - const addPlotData = jotai.useSetAtom(model.addDataAtom); + const addContinuousData = jotai.useSetAtom(model.addContinuousDataAtom); const loading = jotai.useAtomValue(model.loadingAtom); React.useEffect(() => { @@ -323,7 +363,13 @@ function SysinfoView({ model, blockId }: SysinfoViewProps) { return; } 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); @@ -348,6 +394,7 @@ type SingleLinePlotProps = { defaultColor: string; title?: boolean; sparkline?: boolean; + targetLen: number; }; function SingleLinePlot({ @@ -358,6 +405,7 @@ function SingleLinePlot({ defaultColor, title = false, sparkline = false, + targetLen, }: SingleLinePlotProps) { const containerRef = React.useRef(); const domRect = useDimensionsWithExistingRef(containerRef, 300); @@ -440,12 +488,15 @@ function SingleLinePlot({ ); let maxY = resolveDomainBound(yvalMeta?.maxy, plotData[plotData.length - 1]) ?? 100; 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({ axis: !sparkline, x: { grid: true, label: "time", tickFormat: (d) => `${dayjs.unix(d / 1000).format("HH:mm:ss")}`, + domain: [minX, maxX], }, y: { label: labelY, domain: [minY, maxY] }, width: plotWidth, @@ -469,6 +520,7 @@ const SysinfoViewInner = React.memo(({ model }: SysinfoViewProps) => { const yvals = jotai.useAtomValue(model.metrics); const plotMeta = jotai.useAtomValue(model.plotMetaAtom); const osRef = React.useRef(); + const targetLen = jotai.useAtomValue(model.numPoints) + 1; let title = false; let cols2 = false; if (yvals.length > 1) { @@ -491,6 +543,7 @@ const SysinfoViewInner = React.memo(({ model }: SysinfoViewProps) => { blockId={model.blockId} defaultColor={"var(--accent-color)"} title={title} + targetLen={targetLen} /> ); })}