Merge pull request 'realTimeVisulization' (#20) from realTimeVisulization into main

Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/20
This commit is contained in:
Vishnu 2025-03-28 13:54:16 +00:00
commit b10613f360
11 changed files with 1039 additions and 405 deletions

View File

@ -10,5 +10,8 @@ REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000
# Base URL for the server marketplace API. # Base URL for the server marketplace API.
REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011
# base url for IoT socket server
REACT_APP_IOT_SOCKET_SERVER_URL =185.100.212.76:5010
# Base URL for the server mqtt. # Base URL for the server mqtt.
REACT_APP_SERVER_MQTT_URL=185.100.212.76:23457 REACT_APP_SERVER_MQTT_URL=185.100.212.76:23457

205
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
"@react-three/drei": "^9.113.0", "@react-three/drei": "^9.113.0",
"@react-three/fiber": "^8.17.7", "@react-three/fiber": "^8.17.7",
"@react-three/postprocessing": "^2.16.3", "@react-three/postprocessing": "^2.16.3",
"@recast-navigation/core": "^0.39.0",
"@recast-navigation/three": "^0.39.0", "@recast-navigation/three": "^0.39.0",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",

View File

@ -18,7 +18,7 @@ const sampleData = {
{ {
data: [65, 59, 80, 81, 56, 55, 40], data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", backgroundColor: "#6f42c1",
borderColor: "#ffffff", borderColor: "#b392f0",
borderWidth: 1, borderWidth: 1,
}, },
], ],

View File

@ -1,77 +1,177 @@
import React, { useEffect, useState } from 'react' // import React, { useEffect, useState } from 'react'
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown' // import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
import { AddIcon } from '../../../../icons/ExportCommonIcons' // import { AddIcon } from '../../../../icons/ExportCommonIcons'
import RegularDropDown from '../../../../ui/inputs/RegularDropDown' // import RegularDropDown from '../../../../ui/inputs/RegularDropDown'
import useChartStore from '../../../../../store/useChartStore' // import useChartStore from '../../../../../store/useChartStore'
import axios from 'axios' // import axios from 'axios'
type Props = {} // type Props = {}
// const LineGrapInput = (props: Props) => {
// const [dropDowndata, setDropDownData] = useState({})
// const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
// const [selectedOption, setSelectedOption] = useState('1h')
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const handleSelectDuration = (option: string) => {
// updateDuration(option); // Normalize for key matching
// };
// useEffect(() => {
// const fetchZoneData = async () => {
// try {
// const response = await axios.get(`http://${iotApiUrl}/getinput`);
// if (response.status === 200) {
// console.log('dropdown data:', response.data);
// setDropDownData(response.data)
// } else {
// console.log('Unexpected response:', response);
// }
// } catch (error) {
// console.error('There was an error!', error);
// }
// };
// fetchZoneData();
// }, []);
// useEffect(() => {
// console.log(selections);
// }, [selections])
// const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => {
// setSelections(prev => {
// if (selectedData === null) {
// const newSelections = { ...prev };
// delete newSelections[inputKey];
// return newSelections;
// } else {
// return {
// ...prev,
// [inputKey]: selectedData
// };
// }
// });
// };
// interface Measurement {
// name: string;
// fields: string;
// }
// interface InputData {
// [key: string]: Measurement;
// }
// const extractMeasurements = (input: InputData): Measurement[] => {
// return Object.values(input);
// };
// useEffect(() => {
// const measurementsData = extractMeasurements(selections);
// setMeasurements(measurementsData);
// }, [selections]);
// return (
// <>
// <div className="inputs-wrapper">
// {[...Array(6)].map((_, index) => {
// const inputKey = `input${index + 1}`;
// return (
// <div key={index} className="datas">
// <div className="datas__label">Input {index + 1}</div>
// <div className="datas__class">
// <MultiLevelDropdown
// data={dropDowndata}
// onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
// onUnselect={() => handleSelect(inputKey, null)}
// selectedValue={selections[inputKey]}
// />
// <div className="icon">
// <AddIcon />
// </div>
// </div>
// </div>
// );
// })}
// </div>
// <div>
// <div className="datas">
// <div className="datas__label">duration</div>
// <div className="datas__class">
// <RegularDropDown
// header={duration}
// options={["1h", "2h", "12h"]}
// onSelect={handleSelectDuration}
// search={false}
// />
// </div>
// </div>
// </div>
// </>
// )
// }
// export default LineGrapInput
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import axios from "axios";
type Props = {};
const LineGrapInput = (props: Props) => { const LineGrapInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
const [selectedOption, setSelectedOption] = useState('1h')
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
const handleSelectDuration = (option: string) => { const [dropDowndata, setDropDownData] = useState({});
updateDuration(option); // Normalize for key matching const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>(measurements);
}; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get('http://192.168.0.192:5010/getinput'); const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); console.log("dropdown data:", response.data);
setDropDownData(response.data) setDropDownData(response.data);
} else { } else {
console.log('Unexpected response:', response); console.log("Unexpected response:", response);
} }
} catch (error) { } catch (error) {
console.error('There was an error!', error); console.error("There was an error!", error);
} }
}; };
fetchZoneData(); fetchZoneData();
}, []); }, []);
// Sync Zustand state when component mounts
useEffect(() => { useEffect(() => {
console.log(selections); setSelections(measurements);
}, [selections]) }, [measurements]);
const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => { const handleSelect = (inputKey: string, selectedData: { name: string; fields: string } | null) => {
setSelections(prev => { setSelections((prev) => {
if (selectedData === null) {
const newSelections = { ...prev }; const newSelections = { ...prev };
if (selectedData === null) {
delete newSelections[inputKey]; delete newSelections[inputKey];
return newSelections;
} else { } else {
return { newSelections[inputKey] = selectedData;
...prev,
[inputKey]: selectedData
};
} }
setMeasurements(newSelections); // Update Zustand store
return newSelections;
}); });
}; };
interface Measurement { const handleSelectDuration = (option: string) => {
name: string; updateDuration(option);
fields: string;
}
interface InputData {
[key: string]: Measurement;
}
const extractMeasurements = (input: InputData): Measurement[] => {
return Object.values(input);
}; };
useEffect(() => {
const measurementsData = extractMeasurements(selections);
setMeasurements(measurementsData);
}, [selections]);
return ( return (
<> <>
<div className="inputs-wrapper"> <div className="inputs-wrapper">
@ -85,7 +185,7 @@ const LineGrapInput = (props: Props) => {
data={dropDowndata} data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} selectedValue={selections[inputKey]} // Load from Zustand
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />
@ -97,7 +197,7 @@ const LineGrapInput = (props: Props) => {
</div> </div>
<div> <div>
<div className="datas"> <div className="datas">
<div className="datas__label">duration</div> <div className="datas__label">Duration</div>
<div className="datas__class"> <div className="datas__class">
<RegularDropDown <RegularDropDown
header={duration} header={duration}
@ -109,7 +209,7 @@ const LineGrapInput = (props: Props) => {
</div> </div>
</div> </div>
</> </>
) );
} };
export default LineGrapInput export default LineGrapInput;

View File

@ -8,11 +8,12 @@ type Props = {}
const PieChartInput = (props: Props) => { const PieChartInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({}) const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({}) const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({})
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get('http://192.168.0.192:5010/getinput'); const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); console.log('dropdown data:', response.data);
setDropDownData(response.data) setDropDownData(response.data)

View File

@ -237,14 +237,14 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
data={{ // data={{
measurements: [ // measurements: [
{ name: "testDevice", fields: "powerConsumption" }, // { name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" }, // { name: "furnace", fields: "powerConsumption" },
], // ],
interval: 1000, // interval: 1000,
duration: "1h", // duration: "1h",
}} // }}
/> />
)} )}
{widget.type === "bar" && ( {widget.type === "bar" && (
@ -253,14 +253,14 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
data={{ // data={{
measurements: [ // measurements: [
{ name: "testDevice", fields: "powerConsumption" }, // { name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" }, // { name: "furnace", fields: "powerConsumption" },
], // ],
interval: 1000, // interval: 1000,
duration: "1h", // duration: "1h",
}} // }}
/> />
)} )}
{widget.type === "pie" && ( {widget.type === "pie" && (
@ -269,14 +269,14 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
data={{ // data={{
measurements: [ // measurements: [
{ name: "testDevice", fields: "powerConsumption" }, // { name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" }, // { name: "furnace", fields: "powerConsumption" },
], // ],
interval: 1000, // interval: 1000,
duration: "1h", // duration: "1h",
}} // }}
/> />
)} )}
{widget.type === "doughnut" && ( {widget.type === "doughnut" && (

View File

@ -1,6 +1,195 @@
import { useMemo } from "react"; // import React, { useEffect, useRef, useMemo, useState } from "react";
// import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../store/useThemeStore";
// import io from "socket.io-client";
// import { Bar } from 'react-chartjs-2';
// import useChartStore from "../../../../store/useChartStore";
// // WebSocket Connection
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const LineGraphComponent = ({
// type,
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// data,
// }: ChartComponentProps) => {
// const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
// labels: [],
// datasets: [],
// });
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const defaultData = {
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
// datasets: [
// {
// label: "Dataset",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: ["#6f42c1"],
// borderColor: "#ffffff",
// borderWidth: 2,
// },
// ],
// };
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
// () => themeColor[0] || "#5c87df",
// [themeColor]
// );
// const buttonAbortColor = useMemo(
// () => themeColor[1] || "#ffffff",
// [themeColor]
// );
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// // Memoize Chart Data
// // const data = useMemo(() => propsData, [propsData]);
// // Memoize Chart Options
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
// },
// }),
// [title, chartFontStyle]
// );
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// useEffect(() => {
// const socket = io(`http://${iotApiUrl}`);
// if ( measurements.length > 0 ) {
// var inputes = {
// measurements: measurements,
// duration: duration,
// interval: 1000,
// }
// // Start stream
// const startStream = () => {
// socket.emit("lineInput", inputes);
// }
// socket.on('connect', startStream);
// socket.on("lineOutput", (response) => {
// const responceData = response.data;
// console.log("Received data:", responceData);
// // Extract timestamps and values
// const labels = responceData.time;
// const datasets = measurements.map((measurement: any) => {
// const key = `${measurement.name}.${measurement.fields}`;
// return {
// label: key,
// data: responceData[key]?.values ?? [], // Ensure it exists
// backgroundColor: "#6f42c1",
// borderColor: "#ffffff",
// };
// });
// setChartData({ labels, datasets });
// });
// }
// return () => {
// socket.off("lineOutput");
// socket.emit("stop_stream"); // Stop streaming when component unmounts
// };
// }, [measurements, duration]);
// // useEffect(() => {
// // if (!canvasRef.current) return;
// // const ctx = canvasRef.current.getContext("2d");
// // if (!ctx) return;
// // const chart = new Chart(ctx, {
// // type,
// // data: chartData,
// // options: options,
// // });
// // return () => chart.destroy();
// // }, [chartData, type, title]);
// return <Bar data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
// };
// export default LineGraphComponent;
import React, { useEffect, useMemo, useState } from "react";
import { Bar } from "react-chartjs-2"; import { Bar } from "react-chartjs-2";
import io from "socket.io-client";
import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore";
interface ChartComponentProps { interface ChartComponentProps {
type: any; type: any;
@ -8,16 +197,42 @@ interface ChartComponentProps {
fontFamily?: string; fontFamily?: string;
fontSize?: string; fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold"; fontWeight?: "Light" | "Regular" | "Bold";
data: any;
} }
const LineGraphComponent = ({ const BarGraphComponent = ({
type,
title, title,
fontFamily, fontFamily,
fontSize, fontSize,
fontWeight = "Regular", fontWeight = "Regular",
}: ChartComponentProps) => { }: ChartComponentProps) => {
// Memoize Font Weight Mapping const { themeColor } = useThemeStore();
const { measurements, duration } = useChartStore(); // Zustand Store
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo( const chartFontWeightMap = useMemo(
() => ({ () => ({
Light: "lighter" as const, Light: "lighter" as const,
@ -27,19 +242,9 @@ const LineGraphComponent = ({
[] []
); );
// Parse and Memoize Font Size const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontSizeValue = useMemo( const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily || "Arial",
@ -49,6 +254,7 @@ const LineGraphComponent = ({
[fontFamily, fontSizeValue, fontWeightValue] [fontFamily, fontSizeValue, fontWeightValue]
); );
// Memoized Chart Options
const options = useMemo( const options = useMemo(
() => ({ () => ({
responsive: true, responsive: true,
@ -66,7 +272,7 @@ const LineGraphComponent = ({
scales: { scales: {
x: { x: {
ticks: { ticks: {
display: false, // This hides the x-axis labels display: true,
}, },
}, },
}, },
@ -74,21 +280,53 @@ const LineGraphComponent = ({
[title, chartFontStyle] [title, chartFontStyle]
); );
const chartData = { useEffect(() => {
labels: ["January", "February", "March", "April", "May", "June", "July"], if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
datasets: [
{ const socket = io(`http://${iotApiUrl}`);
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40], const inputData = {
backgroundColor: "#6f42c1", measurements,
borderColor: "#ffffff", duration,
borderWidth: 2, interval: 1000,
fill: false,
},
],
}; };
return <Bar data={chartData} options={options} />; const startStream = () => {
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
const responseData = response.data;
console.log("Received data:", responseData);
// 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 ?? [],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
};
});
setChartData({ labels, datasets });
});
return () => {
socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
return <Bar data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
}; };
export default LineGraphComponent; export default BarGraphComponent;

View File

@ -1,115 +1,15 @@
// import { useMemo } from "react"; import React, { useEffect, useMemo, useState } from "react";
// import { Line } from "react-chartjs-2"; import { Line } from "react-chartjs-2";
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const LineGraphComponent = ({
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// }: ChartComponentProps) => {
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
// },
// }),
// [title, chartFontStyle]
// );
// const chartData = {
// labels: ["January", "February", "March", "April", "May", "June", "July"],
// datasets: [
// {
// label: "My First Dataset",
// data: [65, 59, 80, 81, 56, 55, 40],
// backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
// borderColor: "#ffffff", // Keeping border color white
// borderWidth: 2,
// fill: false,
// },
// ],
// };
// return <Line data={chartData} options={options} />;
// };
// export default LineGraphComponent;
import React, { useEffect, useRef, useMemo, useState } from "react";
import { Chart } from "chart.js/auto";
import { useThemeStore } from "../../../../store/useThemeStore";
import io from "socket.io-client"; import io from "socket.io-client";
import { Line } from 'react-chartjs-2'; import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore"; import useChartStore from "../../../../store/useChartStore";
// WebSocket Connection
// const socket = io("http://localhost:5000"); // Adjust to your backend URL
interface ChartComponentProps { interface ChartComponentProps {
type: any; type: any;
title: string; title: string;
fontFamily?: string; fontFamily?: string;
fontSize?: string; fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold"; fontWeight?: "Light" | "Regular" | "Bold";
data: any;
} }
const LineGraphComponent = ({ const LineGraphComponent = ({
@ -118,26 +18,34 @@ const LineGraphComponent = ({
fontFamily, fontFamily,
fontSize, fontSize,
fontWeight = "Regular", fontWeight = "Regular",
data,
}: ChartComponentProps) => { }: ChartComponentProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const { themeColor } = useThemeStore(); const { themeColor } = useThemeStore();
const { measurements, duration } = useChartStore(); // Zustand Store
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [], labels: [],
datasets: [], datasets: [],
}); });
// Memoize Theme Colors to Prevent Unnecessary Recalculations const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const buttonActionColor = useMemo(
() => themeColor[0] || "#5c87df",
[themeColor]
);
const buttonAbortColor = useMemo(
() => themeColor[1] || "#ffffff",
[themeColor]
);
// Memoize Font Weight Mapping const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo( const chartFontWeightMap = useMemo(
() => ({ () => ({
Light: "lighter" as const, Light: "lighter" as const,
@ -147,19 +55,9 @@ const LineGraphComponent = ({
[] []
); );
// Parse and Memoize Font Size const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontSizeValue = useMemo( const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily || "Arial",
@ -169,9 +67,6 @@ const LineGraphComponent = ({
[fontFamily, fontSizeValue, fontWeightValue] [fontFamily, fontSizeValue, fontWeightValue]
); );
// Memoize Chart Data
// const data = useMemo(() => propsData, [propsData]);
// Memoize Chart Options // Memoize Chart Options
const options = useMemo( const options = useMemo(
() => ({ () => ({
@ -198,67 +93,54 @@ const LineGraphComponent = ({
[title, chartFontStyle] [title, chartFontStyle]
); );
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); useEffect(() => {console.log(measurements);
},[measurements])
useEffect(() => { useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io("http://192.168.0.192:5010"); const socket = io(`http://${iotApiUrl}`);
if ( measurements.length > 0 ) { const inputData = {
var inputes = { measurements,
measurements: measurements, duration,
duration: duration,
interval: 1000, interval: 1000,
} };
// Start stream
const startStream = () => { const startStream = () => {
socket.emit("lineInput", inputes); socket.emit("lineInput", inputData);
} };
socket.on('connect', startStream); socket.on("connect", startStream);
socket.on("lineOutput", (response) => { socket.on("lineOutput", (response) => {
const responceData = response.data; const responseData = response.data;
console.log("Received data:", responceData);
// Extract timestamps and values // Extract timestamps and values
const labels = responceData.time; const labels = responseData.time;
const datasets = measurements.map((measurement: any) => { const datasets = Object.keys(measurements).map((key) => {
const key = `${measurement.name}.${measurement.fields}`; const measurement = measurements[key];
const datasetKey = `${measurement.name}.${measurement.fields}`;
return { return {
label: key, label: datasetKey,
data: responceData[key]?.values ?? [], // Ensure it exists data: responseData[datasetKey]?.values ?? [],
backgroundColor: themeColor[0] || "#5c87df", backgroundColor: "#6f42c1",
borderColor: themeColor[1] || "#ffffff", borderColor: "#b392f0",
borderWidth: 1,
}; };
}); });
setChartData({ labels, datasets }); setChartData({ labels, datasets });
}); });
}
return () => { return () => {
socket.off("lineOutput"); socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
}; };
}, [measurements, duration]); }, [measurements, duration, iotApiUrl]);
// useEffect(() => { return <Line data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
// if (!canvasRef.current) return;
// const ctx = canvasRef.current.getContext("2d");
// if (!ctx) return;
// const chart = new Chart(ctx, {
// type,
// data: chartData,
// options: options,
// });
// return () => chart.destroy();
// }, [chartData, type, title]);
return <Line data={chartData} options={options} />;
}; };
export default LineGraphComponent; export default LineGraphComponent;

View File

@ -1,5 +1,195 @@
import { useMemo } from "react"; // import React, { useEffect, useRef, useMemo, useState } from "react";
// import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../store/useThemeStore";
// import io from "socket.io-client";
// import { Pie } from 'react-chartjs-2';
// import useChartStore from "../../../../store/useChartStore";
// // WebSocket Connection
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const PieChartComponent = ({
// type,
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// data,
// }: ChartComponentProps) => {
// const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
// labels: [],
// datasets: [],
// });
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const defaultData = {
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
// datasets: [
// {
// label: "Dataset",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: ["#6f42c1"],
// borderColor: "#ffffff",
// borderWidth: 2,
// },
// ],
// };
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
// () => themeColor[0] || "#6f42c1",
// [themeColor]
// );
// const buttonAbortColor = useMemo(
// () => themeColor[1] || "#ffffff",
// [themeColor]
// );
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// // Memoize Chart Data
// // const data = useMemo(() => propsData, [propsData]);
// // Memoize Chart Options
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// // x: {
// // ticks: {
// // display: true, // This hides the x-axis labels
// // },
// // },
// },
// }),
// [title, chartFontStyle]
// );
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// useEffect(() => {
// const socket = io(`http://${iotApiUrl}`);
// if ( measurements.length > 0 ) {
// var inputes = {
// measurements: measurements,
// duration: duration,
// interval: 1000,
// }
// // Start stream
// const startStream = () => {
// socket.emit("lineInput", inputes);
// }
// socket.on('connect', startStream);
// socket.on("lineOutput", (response) => {
// const responceData = response.data;
// console.log("Received data:", responceData);
// // Extract timestamps and values
// const labels = responceData.time;
// const datasets = measurements.map((measurement: any) => {
// const key = `${measurement.name}.${measurement.fields}`;
// return {
// label: key,
// data: responceData[key]?.values ?? [], // Ensure it exists
// backgroundColor: "#6f42c1",
// borderColor: "#ffffff",
// };
// });
// setChartData({ labels, datasets });
// });
// }
// return () => {
// socket.off("lineOutput");
// socket.emit("stop_stream"); // Stop streaming when component unmounts
// };
// }, [measurements, duration]);
// // useEffect(() => {
// // if (!canvasRef.current) return;
// // const ctx = canvasRef.current.getContext("2d");
// // if (!ctx) return;
// // const chart = new Chart(ctx, {
// // type,
// // data: chartData,
// // options: options,
// // });
// // return () => chart.destroy();
// // }, [chartData, type, title]);
// return <Pie data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
// };
// export default PieChartComponent;
import React, { useEffect, useMemo, useState } from "react";
import { Pie } from "react-chartjs-2"; import { Pie } from "react-chartjs-2";
import io from "socket.io-client";
import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore";
interface ChartComponentProps { interface ChartComponentProps {
type: any; type: any;
@ -7,16 +197,42 @@ interface ChartComponentProps {
fontFamily?: string; fontFamily?: string;
fontSize?: string; fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold"; fontWeight?: "Light" | "Regular" | "Bold";
data: any;
} }
const PieChartComponent = ({ const PieChartComponent = ({
type,
title, title,
fontFamily, fontFamily,
fontSize, fontSize,
fontWeight = "Regular", fontWeight = "Regular",
}: ChartComponentProps) => { }: ChartComponentProps) => {
// Memoize Font Weight Mapping const { themeColor } = useThemeStore();
const { measurements, duration } = useChartStore(); // Zustand Store
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo( const chartFontWeightMap = useMemo(
() => ({ () => ({
Light: "lighter" as const, Light: "lighter" as const,
@ -26,19 +242,9 @@ const PieChartComponent = ({
[] []
); );
// Parse and Memoize Font Size const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontSizeValue = useMemo( const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily || "Arial",
@ -48,12 +254,7 @@ const PieChartComponent = ({
[fontFamily, fontSizeValue, fontWeightValue] [fontFamily, fontSizeValue, fontWeightValue]
); );
// Access the CSS variable for the primary accent color // Memoized Chart Options
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue("--accent-color")
.trim();
console.log("accentColor: ", accentColor);
const options = useMemo( const options = useMemo(
() => ({ () => ({
responsive: true, responsive: true,
@ -68,24 +269,64 @@ const PieChartComponent = ({
display: false, display: false,
}, },
}, },
scales: {
// x: {
// ticks: {
// display: true,
// },
// },
},
}), }),
[title, chartFontStyle] [title, chartFontStyle]
); );
const chartData = { useEffect(() => {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
datasets: [
{ const socket = io(`http://${iotApiUrl}`);
label: "Dataset",
data: [12, 19, 3, 5, 2, 3], const inputData = {
backgroundColor: ["#6f42c1"], measurements,
borderColor: "#ffffff", duration,
borderWidth: 2, interval: 1000,
},
],
}; };
return <Pie data={chartData} options={options} />; const startStream = () => {
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
const responseData = response.data;
console.log("Received data:", responseData);
// 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 ?? [],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
};
});
setChartData({ labels, datasets });
});
return () => {
socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
return <Pie data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
}; };
export default PieChartComponent; export default PieChartComponent;

View File

@ -6,15 +6,15 @@ interface Measurement {
} }
interface MeasurementStore { interface MeasurementStore {
measurements: Measurement[]; measurements: Record<string, Measurement>; // Change array to Record<string, Measurement>
interval: number; interval: number;
duration: string; duration: string;
setMeasurements: (newMeasurements: Measurement[]) => void; setMeasurements: (newMeasurements: Record<string, Measurement>) => void;
updateDuration: (newDuration: string) => void; updateDuration: (newDuration: string) => void;
} }
const useChartStore = create<MeasurementStore>((set) => ({ const useChartStore = create<MeasurementStore>((set) => ({
measurements: [], measurements: {}, // Initialize as an empty object
interval: 1000, interval: 1000,
duration: "1h", duration: "1h",
@ -26,3 +26,4 @@ const useChartStore = create<MeasurementStore>((set) => ({
})); }));
export default useChartStore; export default useChartStore;