248 lines
7.1 KiB
TypeScript
248 lines
7.1 KiB
TypeScript
import { Html } from "@react-three/drei";
|
|
import React, { useEffect, useMemo, useState } from "react";
|
|
import { Line } from "react-chartjs-2";
|
|
import {
|
|
Chart as ChartJS,
|
|
CategoryScale,
|
|
LinearScale,
|
|
PointElement,
|
|
LineElement,
|
|
Title,
|
|
Tooltip,
|
|
Legend,
|
|
ChartData,
|
|
ChartOptions,
|
|
} from "chart.js";
|
|
import { ThroughputIcon } from "../../../icons/3dChartIcons";
|
|
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
|
import useChartStore from "../../../../store/useChartStore";
|
|
import axios from "axios";
|
|
import io from "socket.io-client";
|
|
|
|
// Register Chart.js components
|
|
ChartJS.register(
|
|
CategoryScale,
|
|
LinearScale,
|
|
PointElement,
|
|
LineElement,
|
|
Title,
|
|
Tooltip,
|
|
Legend
|
|
);
|
|
|
|
// Define Props for LineGraphComponent
|
|
interface LineGraphProps {
|
|
data: ChartData<"line">; // Type for chart data
|
|
options?: ChartOptions<"line">; // Type for chart options (optional)
|
|
}
|
|
|
|
// LineGraphComponent using react-chartjs-2
|
|
const LineGraphComponent: React.FC<LineGraphProps> = ({ data, options }) => {
|
|
return <Line data={data} options={options} />;
|
|
};
|
|
|
|
interface ThroughputProps {
|
|
id: string;
|
|
type: string;
|
|
position: [number, number, number];
|
|
rotation: [number, number, number];
|
|
onContextMenu?: (event: React.MouseEvent) => void;
|
|
}
|
|
|
|
const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
|
|
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
|
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
|
const [measurements, setmeasurements] = useState<any>({});
|
|
const [duration, setDuration] = useState("1h")
|
|
const [name, setName] = useState("Widget")
|
|
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
|
labels: [],
|
|
datasets: [],
|
|
});
|
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
|
const email = localStorage.getItem("email") || "";
|
|
const organization = email?.split("@")[1]?.split(".")[0]
|
|
|
|
// Sample data for the line graph
|
|
const graphData: ChartData<"line"> = {
|
|
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
|
|
datasets: [
|
|
{
|
|
label: "Throughput",
|
|
data: [1000, 1200, 1100, 1300, 1250, 1400], // Example throughput values
|
|
borderColor: "rgba(75, 192, 192, 1)",
|
|
backgroundColor: "rgba(75, 192, 192, 0.2)",
|
|
fill: true,
|
|
},
|
|
],
|
|
};
|
|
|
|
// Options for the line graph
|
|
const graphOptions: ChartOptions<"line"> = {
|
|
responsive: true,
|
|
plugins: {
|
|
legend: {
|
|
position: "top",
|
|
display: false,
|
|
},
|
|
title: {
|
|
display: true,
|
|
text: "Throughput Over Time",
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
grid: {
|
|
display: true, // Show vertical grid lines
|
|
},
|
|
ticks: {
|
|
display: false, // Hide x-axis labels
|
|
},
|
|
},
|
|
y: {
|
|
grid: {
|
|
display: false, // Hide horizontal grid lines
|
|
},
|
|
ticks: {
|
|
display: false, // Hide y-axis labels
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
|
|
|
const socket = io(`http://${iotApiUrl}`);
|
|
|
|
const inputData = {
|
|
measurements,
|
|
duration,
|
|
interval: 1000,
|
|
};
|
|
|
|
|
|
const startStream = () => {
|
|
socket.emit("lineInput", inputData);
|
|
};
|
|
|
|
socket.on("connect", startStream);
|
|
|
|
socket.on("lineOutput", (response) => {
|
|
const responseData = response.data;
|
|
|
|
// Extract timestamps and values
|
|
const labels = responseData.time;
|
|
const datasets = Object.keys(measurements).map((key) => {
|
|
const measurement = measurements[key];
|
|
const datasetKey = `${measurement.name}.${measurement.fields}`;
|
|
return {
|
|
label: datasetKey,
|
|
data: responseData[datasetKey]?.values ?? [],
|
|
borderColor: "rgba(75, 192, 192, 1)",
|
|
backgroundColor: "rgba(75, 192, 192, 0.2)",
|
|
fill: true,
|
|
};
|
|
});
|
|
|
|
setChartData({ labels, datasets });
|
|
});
|
|
|
|
return () => {
|
|
socket.off("lineOutput");
|
|
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
|
socket.disconnect();
|
|
};
|
|
}, [measurements, duration, iotApiUrl]);
|
|
|
|
const fetchSavedInputes = async () => {
|
|
|
|
if (id !== "") {
|
|
try {
|
|
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
|
if (response.status === 200) {
|
|
setmeasurements(response.data.Data.measurements)
|
|
setDuration(response.data.Data.duration)
|
|
setName(response.data.widgetName)
|
|
} else {
|
|
console.log("Unexpected response:", response);
|
|
}
|
|
} catch (error) {
|
|
console.error("There was an error!", error);
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
fetchSavedInputes();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (selectedChartId?.id === id) {
|
|
fetchSavedInputes();
|
|
}
|
|
}
|
|
, [chartMeasurements, chartDuration, widgetName])
|
|
const rotationDegrees = {
|
|
x: (rotation[0] * 180) / Math.PI,
|
|
y: (rotation[1] * 180) / Math.PI,
|
|
z: (rotation[2] * 180) / Math.PI,
|
|
};
|
|
|
|
const transformStyle = {
|
|
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
|
};
|
|
|
|
return (
|
|
<Html position={[position[0], position[1], position[2]]}
|
|
scale={[0.5, 0.5, 0.5]}
|
|
transform
|
|
zIndexRange={[1, 0]}
|
|
sprite
|
|
style={{
|
|
transform: transformStyle.transform,
|
|
transformStyle: 'preserve-3d',
|
|
transition: 'transform 0.1s ease-out'
|
|
|
|
}}
|
|
>
|
|
<div className="throughput-wrapper"
|
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
|
onContextMenu={onContextMenu}
|
|
>
|
|
<div className="header">{name}</div>
|
|
<div className="display-value">
|
|
<div className="left">
|
|
<div className="icon">
|
|
<ThroughputIcon />
|
|
</div>
|
|
<div className="value-container">
|
|
<div className="value-wrapper">
|
|
<div className="value">1,200</div>
|
|
<div className="key"> Units/hr</div>
|
|
</div>
|
|
<div className="total-sales">
|
|
<div className="value">316</div>
|
|
<div className="key">sales</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="right">
|
|
<div className="percent-increase">5.77%</div>
|
|
</div>
|
|
</div>
|
|
<div className="line-graph">
|
|
{/* Line graph using react-chartjs-2 */}
|
|
<LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
|
</div>
|
|
<div className="footer">
|
|
You made an extra <span className="value">$1256.13</span> this month
|
|
</div>
|
|
</div>
|
|
</Html>
|
|
);
|
|
};
|
|
|
|
export default Throughput;
|