added dynamic input based on graph

This commit is contained in:
gabriel 2025-03-28 18:17:45 +05:30
parent 813f620b4d
commit 49af0ea3c9
12 changed files with 1022 additions and 406 deletions

View File

@ -9,3 +9,6 @@ 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

187
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,115 +1,215 @@
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 LineGrapInput = (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 [selectedOption, setSelectedOption] = useState('1h') // const [selectedOption, setSelectedOption] = useState('1h')
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); // const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const handleSelectDuration = (option: string) => { // const handleSelectDuration = (option: string) => {
updateDuration(option); // Normalize for key matching // updateDuration(option); // Normalize for key matching
}; // };
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();
}, []); // }, []);
useEffect(() => { // useEffect(() => {
console.log(selections); // console.log(selections);
}, [selections]) // }, [selections])
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) { // if (selectedData === null) {
const newSelections = { ...prev }; // const newSelections = { ...prev };
delete newSelections[inputKey]; // delete newSelections[inputKey];
return newSelections; // return newSelections;
} else { // } else {
return { // return {
...prev, // ...prev,
[inputKey]: selectedData // [inputKey]: selectedData
}; // };
} // }
}); // });
}; // };
interface Measurement { // interface Measurement {
name: string; // name: string;
fields: string; // fields: string;
} // }
interface InputData { // interface InputData {
[key: string]: Measurement; // [key: string]: Measurement;
} // }
const extractMeasurements = (input: InputData): Measurement[] => { // const extractMeasurements = (input: InputData): Measurement[] => {
return Object.values(input); // return Object.values(input);
}; // };
useEffect(() => { // useEffect(() => {
const measurementsData = extractMeasurements(selections); // const measurementsData = extractMeasurements(selections);
setMeasurements(measurementsData); // setMeasurements(measurementsData);
}, [selections]); // }, [selections]);
return ( // return (
<> // <>
<div className="inputs-wrapper"> // <div className="inputs-wrapper">
{[...Array(6)].map((_, index) => { // {[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`; // const inputKey = `input${index + 1}`;
return ( // return (
<div key={index} className="datas"> // <div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div> // <div className="datas__label">Input {index + 1}</div>
<div className="datas__class"> // <div className="datas__class">
<MultiLevelDropdown // <MultiLevelDropdown
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]}
/> // />
<div className="icon"> // <div className="icon">
<AddIcon /> // <AddIcon />
</div> // </div>
</div> // </div>
</div> // </div>
); // );
})} // })}
</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}
options={["1h", "2h", "12h"]} // options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration} // onSelect={handleSelectDuration}
search={false} // search={false}
/> // />
</div> // </div>
</div> // </div>
</div> // </div>
</> // </>
) // )
} // }
export default LineGrapInput // 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 { measurements, setMeasurements, updateDuration, duration } = useChartStore();
const [dropDowndata, setDropDownData] = useState({});
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>(measurements);
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
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();
}, []);
// Sync Zustand state when component mounts
useEffect(() => {
setSelections(measurements);
}, [measurements]);
const handleSelect = (inputKey: string, selectedData: { name: string; fields: string } | null) => {
setSelections((prev) => {
const newSelections = { ...prev };
if (selectedData === null) {
delete newSelections[inputKey];
} else {
newSelections[inputKey] = selectedData;
}
setMeasurements(newSelections); // Update Zustand store
return newSelections;
});
};
const handleSelectDuration = (option: string) => {
updateDuration(option);
};
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]} // Load from Zustand
/>
<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;

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: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1",
borderColor: "#ffffff",
borderWidth: 2,
fill: false,
},
],
};
return <Bar data={chartData} options={options} />; 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;
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,60 +18,55 @@ 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", const defaultData = {
[themeColor] labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
); datasets: [
const buttonAbortColor = useMemo( {
() => themeColor[1] || "#ffffff", label: "Dataset",
[themeColor] data: [12, 19, 3, 5, 2, 3],
); backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
// Memoize Font Weight Mapping borderWidth: 1,
const chartFontWeightMap = useMemo( },
() => ({ ],
Light: "lighter" as const, };
Regular: "normal" as const,
Bold: "bold" as const, // Memoize Theme Colors
}), const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
[] const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
);
// Memoize Font Styling
// Parse and Memoize Font Size const chartFontWeightMap = useMemo(
const fontSizeValue = useMemo( () => ({
() => (fontSize ? parseInt(fontSize) : 12), Light: "lighter" as const,
[fontSize] Regular: "normal" as const,
); Bold: "bold" as const,
}),
// Determine and Memoize Font Weight []
const fontWeightValue = useMemo( );
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap] const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
); const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
// Memoize Chart Font Style const chartFontStyle = useMemo(
const chartFontStyle = useMemo( () => ({
() => ({ family: fontFamily || "Arial",
family: fontFamily || "Arial", size: fontSizeValue,
size: fontSizeValue, weight: fontWeightValue,
weight: fontWeightValue, }),
}), [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: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#ffffff",
borderWidth: 2,
},
],
};
return <Pie data={chartData} options={options} />; 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;
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,7 +6,7 @@ const MqttEvents = () => {
const { setTouch, setTemperature, setHumidity } = useDrieUIValue(); const { setTouch, setTemperature, setHumidity } = useDrieUIValue();
useEffect(() => { useEffect(() => {
const client = mqtt.connect("ws://192.168.0.192:1884", { const client = mqtt.connect("ws://192.168.0.193:1884", {
username: "gabby", username: "gabby",
password: "gabby" password: "gabby"
}); });

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;