2024-07-31 23:13:36 +02:00
|
|
|
// Copyright 2024, Command Line Inc.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
import { useHeight } from "@/app/hook/useHeight";
|
|
|
|
import { useWidth } from "@/app/hook/useWidth";
|
2024-08-01 09:57:06 +02:00
|
|
|
import { WOS } from "@/store/global";
|
2024-07-31 23:13:36 +02:00
|
|
|
import { WshServer } from "@/store/wshserver";
|
|
|
|
import * as Plot from "@observablehq/plot";
|
|
|
|
import dayjs from "dayjs";
|
|
|
|
import * as htl from "htl";
|
|
|
|
import * as jotai from "jotai";
|
|
|
|
import * as React from "react";
|
|
|
|
|
|
|
|
import "./cpuplot.less";
|
|
|
|
|
|
|
|
type Point = {
|
2024-08-20 00:01:00 +02:00
|
|
|
time: number; // note this is in seconds not milliseconds
|
2024-07-31 23:13:36 +02:00
|
|
|
value: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CpuPlotViewModel {
|
|
|
|
blockAtom: jotai.Atom<Block>;
|
|
|
|
termMode: jotai.Atom<string>;
|
|
|
|
htmlElemFocusRef: React.RefObject<HTMLInputElement>;
|
|
|
|
blockId: string;
|
|
|
|
viewIcon: jotai.Atom<string>;
|
|
|
|
viewText: jotai.Atom<string>;
|
|
|
|
viewName: jotai.Atom<string>;
|
|
|
|
dataAtom: jotai.PrimitiveAtom<Array<Point>>;
|
|
|
|
addDataAtom: jotai.WritableAtom<unknown, [Point], void>;
|
|
|
|
width: number;
|
2024-08-01 09:57:06 +02:00
|
|
|
incrementCount: jotai.WritableAtom<unknown, [], Promise<void>>;
|
2024-07-31 23:13:36 +02:00
|
|
|
|
|
|
|
constructor(blockId: string) {
|
|
|
|
this.blockId = blockId;
|
2024-08-01 09:57:06 +02:00
|
|
|
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
2024-07-31 23:13:36 +02:00
|
|
|
this.width = 100;
|
|
|
|
this.dataAtom = jotai.atom(this.getDefaultData());
|
|
|
|
this.addDataAtom = jotai.atom(null, (get, set, point) => {
|
|
|
|
// not efficient but should be okay for a demo?
|
|
|
|
const data = get(this.dataAtom);
|
|
|
|
set(this.dataAtom, [...data.slice(1), point]);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.viewIcon = jotai.atom((get) => {
|
|
|
|
return "chart-line"; // should not be hardcoded
|
|
|
|
});
|
|
|
|
this.viewName = jotai.atom((get) => {
|
|
|
|
return "CPU %"; // should not be hardcoded
|
|
|
|
});
|
2024-08-01 09:57:06 +02:00
|
|
|
this.incrementCount = jotai.atom(null, async (get, set) => {
|
|
|
|
const meta = get(this.blockAtom).meta;
|
|
|
|
const count = meta.count ?? 0;
|
|
|
|
await WshServer.SetMetaCommand({ oref: WOS.makeORef("block", this.blockId), meta: { count: count + 1 } });
|
|
|
|
});
|
2024-07-31 23:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getDefaultData(): Array<Point> {
|
|
|
|
// set it back one to avoid backwards line being possible
|
|
|
|
const currentTime = Date.now() / 1000 - 1;
|
|
|
|
const points = [];
|
|
|
|
for (let i = this.width; i > -1; i--) {
|
|
|
|
points.push({ time: currentTime - i, value: 0 });
|
|
|
|
}
|
|
|
|
return points;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeCpuPlotViewModel(blockId: string): CpuPlotViewModel {
|
|
|
|
const cpuPlotViewModel = new CpuPlotViewModel(blockId);
|
|
|
|
return cpuPlotViewModel;
|
|
|
|
}
|
|
|
|
|
2024-08-22 00:49:23 +02:00
|
|
|
function CpuPlotView({ model }: { model: CpuPlotViewModel; blockId: string }) {
|
2024-07-31 23:13:36 +02:00
|
|
|
const containerRef = React.useRef<HTMLInputElement>();
|
|
|
|
const plotData = jotai.useAtomValue(model.dataAtom);
|
|
|
|
const addPlotData = jotai.useSetAtom(model.addDataAtom);
|
|
|
|
const parentHeight = useHeight(containerRef);
|
|
|
|
const parentWidth = useWidth(containerRef);
|
2024-08-01 09:57:06 +02:00
|
|
|
const block = jotai.useAtomValue(model.blockAtom);
|
|
|
|
const incrementCount = jotai.useSetAtom(model.incrementCount); // temporary
|
2024-07-31 23:13:36 +02:00
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const temp = async () => {
|
2024-08-01 09:57:06 +02:00
|
|
|
await incrementCount();
|
2024-07-31 23:13:36 +02:00
|
|
|
const dataGen = WshServer.StreamCpuDataCommand(
|
2024-08-01 09:57:06 +02:00
|
|
|
{ id: model.blockId, count: (block.meta?.count ?? 0) + 1 },
|
2024-07-31 23:13:36 +02:00
|
|
|
{ timeout: 999999999, noresponse: false }
|
|
|
|
);
|
|
|
|
try {
|
|
|
|
for await (const datum of dataGen) {
|
2024-08-20 00:01:00 +02:00
|
|
|
const ts = datum.ts;
|
|
|
|
addPlotData({ time: ts / 1000, value: datum.values?.["cpu"] });
|
2024-07-31 23:13:36 +02:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
temp();
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const plot = Plot.plot({
|
|
|
|
x: { grid: true, label: "time", tickFormat: (d) => `${dayjs.unix(d).format("HH:mm:ss")}` },
|
|
|
|
y: { label: "%", domain: [0, 100] },
|
|
|
|
width: parentWidth,
|
|
|
|
height: parentHeight,
|
|
|
|
marks: [
|
|
|
|
() => htl.svg`<defs>
|
|
|
|
<linearGradient id="gradient" gradientTransform="rotate(90)">
|
|
|
|
<stop offset="0%" stop-color="#58C142" stop-opacity="0.7" />
|
|
|
|
<stop offset="100%" stop-color="#58C142" stop-opacity="0" />
|
|
|
|
</linearGradient>
|
|
|
|
</defs>`,
|
|
|
|
Plot.lineY(plotData, {
|
|
|
|
stroke: "#58C142",
|
|
|
|
strokeWidth: 2,
|
|
|
|
x: "time",
|
|
|
|
y: "value",
|
|
|
|
}),
|
|
|
|
Plot.areaY(plotData, {
|
|
|
|
fill: "url(#gradient)",
|
|
|
|
x: "time",
|
|
|
|
y: "value",
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
});
|
|
|
|
|
|
|
|
if (plot !== undefined) {
|
|
|
|
containerRef.current.append(plot);
|
|
|
|
}
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
if (plot !== undefined) {
|
|
|
|
plot.remove();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}, [plotData, parentHeight, parentWidth]);
|
|
|
|
|
|
|
|
return <div className="plot-view" ref={containerRef} />;
|
|
|
|
}
|
|
|
|
|
|
|
|
export { CpuPlotView, CpuPlotViewModel, makeCpuPlotViewModel };
|