feat: API für Systemspannung +5V erfolgreich implementiert
- API-Handler `getSystemspannung5VplusHandler.ts` erstellt - JSON-Daten werden aus dem Verzeichnis `mocks/device-cgi-simulator/chartsData/systemspannung5Vplus/` geladen - unterstützt die Parameter DIA0, DIA1, DIA2 für unterschiedliche Datenfrequenzen - Fehlerbehandlung bei ungültigen Typen und fehlenden Dateien eingebaut - API getestet unter `/api/cpl/getSystemspannung5VplusHandler?typ=DIA0`
This commit is contained in:
@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
|
|||||||
NEXT_PUBLIC_EXPORT_STATIC=false
|
NEXT_PUBLIC_EXPORT_STATIC=false
|
||||||
NEXT_PUBLIC_USE_CGI=false
|
NEXT_PUBLIC_USE_CGI=false
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.527
|
NEXT_PUBLIC_APP_VERSION=1.6.530
|
||||||
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
|
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
|
|||||||
NEXT_PUBLIC_EXPORT_STATIC=true
|
NEXT_PUBLIC_EXPORT_STATIC=true
|
||||||
NEXT_PUBLIC_USE_CGI=true
|
NEXT_PUBLIC_USE_CGI=true
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.527
|
NEXT_PUBLIC_APP_VERSION=1.6.530
|
||||||
NEXT_PUBLIC_CPL_MODE=production
|
NEXT_PUBLIC_CPL_MODE=production
|
||||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,3 +1,18 @@
|
|||||||
|
## [1.6.530] – 2025-07-03
|
||||||
|
|
||||||
|
- fix: KÜ Firmwareupdate
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.529] – 2025-07-03
|
||||||
|
|
||||||
|
- fix: KÜ Firmwareupdate
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.528] – 2025-07-03
|
||||||
|
|
||||||
|
- fix: KÜ Firmwareupdate
|
||||||
|
|
||||||
|
---
|
||||||
## [1.6.527] – 2025-07-03
|
## [1.6.527] – 2025-07-03
|
||||||
|
|
||||||
- fix: Firmware-Update läuft nun exakt 5 Minuten bis 100 % Fortschritt
|
- fix: Firmware-Update läuft nun exakt 5 Minuten bis 100 % Fortschritt
|
||||||
|
|||||||
96
components/main/system/DetailModal.tsx
Normal file
96
components/main/system/DetailModal.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// components/main/system/DetailModal.tsx
|
||||||
|
"use client";
|
||||||
|
import React from "react";
|
||||||
|
import { Line } from "react-chartjs-2";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "@/redux/store";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen: boolean;
|
||||||
|
selectedKey: string | null;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DetailModal = ({ isOpen, selectedKey, onClose }: Props) => {
|
||||||
|
// Mapping: Welcher Redux-Zweig zu welchem selectedKey gehört
|
||||||
|
const typMap: Record<string, "DIA0" | "DIA1" | "DIA2"> = {
|
||||||
|
"+5V": "DIA1",
|
||||||
|
"+15V": "DIA1",
|
||||||
|
"-15V": "DIA1",
|
||||||
|
"-98V": "DIA1",
|
||||||
|
};
|
||||||
|
|
||||||
|
const typ = selectedKey ? typMap[selectedKey] : null;
|
||||||
|
|
||||||
|
// Redux State abrufen
|
||||||
|
const reduxData = useSelector((state: RootState) =>
|
||||||
|
typ ? state.systemspannung5Vplus[typ] : []
|
||||||
|
);
|
||||||
|
|
||||||
|
// Zeitstempel und Werte extrahieren
|
||||||
|
const labels = reduxData.map((e: any) => e.t);
|
||||||
|
const values = reduxData.map((e: any) => e.i);
|
||||||
|
|
||||||
|
const baseOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: false,
|
||||||
|
grid: { color: "rgba(200,200,200,0.2)" },
|
||||||
|
title: { display: true, text: "Wert" },
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: { color: "rgba(200,200,200,0.2)" },
|
||||||
|
title: { display: true, text: "Zeit" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: { position: "bottom" as const },
|
||||||
|
title: { display: true, text: `Verlauf – ${selectedKey}` },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isOpen || !selectedKey) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-50">
|
||||||
|
<div className="bg-white p-6 rounded-xl w-[90%] max-w-[1200px] h-[80vh] overflow-auto shadow-2xl">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-xl font-semibold">
|
||||||
|
Detailansicht: {selectedKey}
|
||||||
|
</h2>
|
||||||
|
<button onClick={onClose} className="text-red-500 hover:text-red-700">
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="mr-2 font-medium">Zeitraum:</label>
|
||||||
|
<select className="border px-2 py-1 rounded">
|
||||||
|
<option>Letzte 24 Stunden</option>
|
||||||
|
{/* Optional: dynamische Filter */}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-[500px]">
|
||||||
|
<Line
|
||||||
|
data={{
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: selectedKey,
|
||||||
|
data: values,
|
||||||
|
borderColor: "rgba(59,130,246,1)",
|
||||||
|
backgroundColor: "rgba(59,130,246,0.2)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
options={baseOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
144
components/main/system/SystemCharts.tsx
Normal file
144
components/main/system/SystemCharts.tsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// components/main/system/SystemCharts.tsx
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Line } from "react-chartjs-2";
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
} from "chart.js";
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend
|
||||||
|
);
|
||||||
|
|
||||||
|
type HistoryEntry = {
|
||||||
|
time: string | number | Date;
|
||||||
|
"+5V": number;
|
||||||
|
"+15V": number;
|
||||||
|
"-15V": number;
|
||||||
|
"-98V": number;
|
||||||
|
"ADC Temp": number;
|
||||||
|
"CPU Temp": number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
history: HistoryEntry[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SystemCharts = ({ history }: Props) => {
|
||||||
|
const labels = history.map((h) => new Date(h.time).toLocaleTimeString());
|
||||||
|
const formatValue = (v: number) => v.toFixed(2);
|
||||||
|
|
||||||
|
const baseOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: false,
|
||||||
|
grid: { color: "rgba(200,200,200,0.2)" },
|
||||||
|
title: { display: true, text: "Wert" },
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: { color: "rgba(200,200,200,0.2)" },
|
||||||
|
title: { display: true, text: "Zeit" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: { position: "bottom" as const },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
||||||
|
<div className="h-[300px]">
|
||||||
|
<Line
|
||||||
|
data={{
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "+5V",
|
||||||
|
data: history.map((h) => formatValue(h["+5V"])),
|
||||||
|
borderColor: "rgba(59,130,246,1)",
|
||||||
|
backgroundColor: "rgba(59,130,246,0.5)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "+15V",
|
||||||
|
data: history.map((h) => formatValue(h["+15V"])),
|
||||||
|
borderColor: "rgba(34,197,94,1)",
|
||||||
|
backgroundColor: "rgba(34,197,94,0.5)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "-15V",
|
||||||
|
data: history.map((h) => formatValue(h["-15V"])),
|
||||||
|
borderColor: "rgba(239,68,68,1)",
|
||||||
|
backgroundColor: "rgba(239,68,68,0.5)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "-98V",
|
||||||
|
data: history.map((h) => formatValue(h["-98V"])),
|
||||||
|
borderColor: "rgba(234,179,8,1)",
|
||||||
|
backgroundColor: "rgba(234,179,8,0.5)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
...baseOptions,
|
||||||
|
plugins: {
|
||||||
|
...baseOptions.plugins,
|
||||||
|
title: { display: true, text: "Systemspannungen" },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-[300px]">
|
||||||
|
<Line
|
||||||
|
data={{
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "ADC Temp",
|
||||||
|
data: history.map((h) => h["ADC Temp"]),
|
||||||
|
borderColor: "rgba(168,85,247,1)",
|
||||||
|
backgroundColor: "rgba(168,85,247,0.5)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "CPU Temp",
|
||||||
|
data: history.map((h) =>
|
||||||
|
parseFloat(formatValue(h["CPU Temp"]))
|
||||||
|
),
|
||||||
|
borderColor: "rgba(251,191,36,1)",
|
||||||
|
backgroundColor: "rgba(251,191,36,0.5)",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
...baseOptions,
|
||||||
|
plugins: {
|
||||||
|
...baseOptions.plugins,
|
||||||
|
title: { display: true, text: "Systemtemperaturen" },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
33
components/main/system/SystemOverviewGrid.tsx
Normal file
33
components/main/system/SystemOverviewGrid.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// components/main/system/SystemOverviewGrid.tsx
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
voltages: Record<string, number>;
|
||||||
|
onOpenDetail: (key: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SystemOverviewGrid = ({ voltages, onOpenDetail }: Props) => {
|
||||||
|
const formatValue = (value: number) => value.toFixed(2);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-2 gap-4 mb-8">
|
||||||
|
{Object.entries(voltages).map(([key, value]) => {
|
||||||
|
const unit = key.includes("Temp") ? "°C" : "V";
|
||||||
|
return (
|
||||||
|
<div key={key} className="p-4 border rounded shadow">
|
||||||
|
<h2 className="font-semibold">{key}</h2>
|
||||||
|
<p>
|
||||||
|
{formatValue(value)} {unit}
|
||||||
|
<button
|
||||||
|
onClick={() => onOpenDetail(key)}
|
||||||
|
className="ml-2 text-blue-600 hover:underline text-sm"
|
||||||
|
>
|
||||||
|
Details
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.527",
|
"version": "1.6.530",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.527",
|
"version": "1.6.530",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/roboto": "^5.1.0",
|
"@fontsource/roboto": "^5.1.0",
|
||||||
"@iconify-icons/ri": "^1.2.10",
|
"@iconify-icons/ri": "^1.2.10",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.527",
|
"version": "1.6.530",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
35
pages/api/cpl/getSystemspannung5VplusHandler.ts
Normal file
35
pages/api/cpl/getSystemspannung5VplusHandler.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// @/pages/api/cpl/getSystemspannung5VplusHandler.ts
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
const { typ = "DIA0" } = req.query;
|
||||||
|
|
||||||
|
if (!["DIA0", "DIA1", "DIA2"].includes(typ as string)) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "Ungültiger Typ. Nur DIA0, DIA1 oder DIA2 erlaubt." });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Verwende __dirname um relativen Pfad zur API-Datei zu bestimmen
|
||||||
|
const filePath = path.join(
|
||||||
|
process.cwd(), // ← Root-Projektverzeichnis
|
||||||
|
"mocks/device-cgi-simulator/chartsData/systemspannung5Vplus",
|
||||||
|
`${typ}.json`
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileContent = await fs.readFile(filePath, "utf-8");
|
||||||
|
const json = JSON.parse(fileContent);
|
||||||
|
return res.status(200).json(json);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Fehler beim Lesen der Datei:", error);
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.json({ error: "Datei konnte nicht gelesen werden." });
|
||||||
|
}
|
||||||
|
}
|
||||||
186
pages/system.tsx
186
pages/system.tsx
@@ -1,30 +1,12 @@
|
|||||||
"use client"; // /pages/system.tsx
|
// pages/system.tsx
|
||||||
import React, { useEffect } from "react";
|
"use client";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { AppDispatch, RootState } from "../redux/store";
|
import { AppDispatch, RootState } from "../redux/store";
|
||||||
import { getSystemVoltTempThunk } from "../redux/thunks/getSystemVoltTempThunk";
|
import { getSystemVoltTempThunk } from "../redux/thunks/getSystemVoltTempThunk";
|
||||||
import {
|
import { SystemOverviewGrid } from "@/components/main/system/SystemOverviewGrid";
|
||||||
Chart as ChartJS,
|
import { SystemCharts } from "@/components/main/system/SystemCharts";
|
||||||
CategoryScale,
|
import { DetailModal } from "@/components/main/system/DetailModal";
|
||||||
LinearScale,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
TooltipItem,
|
|
||||||
} from "chart.js";
|
|
||||||
import { Line } from "react-chartjs-2";
|
|
||||||
|
|
||||||
ChartJS.register(
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Tooltip,
|
|
||||||
Legend
|
|
||||||
);
|
|
||||||
|
|
||||||
const SystemPage = () => {
|
const SystemPage = () => {
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
@@ -35,6 +17,9 @@ const SystemPage = () => {
|
|||||||
(state: RootState) => state.systemVoltTemp.history
|
(state: RootState) => state.systemVoltTemp.history
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [selectedKey, setSelectedKey] = useState<string | null>(null);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(getSystemVoltTempThunk());
|
dispatch(getSystemVoltTempThunk());
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -45,96 +30,14 @@ const SystemPage = () => {
|
|||||||
|
|
||||||
const labels = history.map((h) => new Date(h.time).toLocaleTimeString());
|
const labels = history.map((h) => new Date(h.time).toLocaleTimeString());
|
||||||
|
|
||||||
const formatValue = (value: number) => value.toFixed(2);
|
const handleOpenDetail = (key: string) => {
|
||||||
|
setSelectedKey(key);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const voltageDatasets = [
|
const handleCloseDetail = () => {
|
||||||
{
|
setSelectedKey(null);
|
||||||
label: "+5V",
|
setIsModalOpen(false);
|
||||||
data: history.map((h) => formatValue(h["+5V"])),
|
|
||||||
borderColor: "rgba(59,130,246,1)",
|
|
||||||
backgroundColor: "rgba(59,130,246,0.5)",
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "+15V",
|
|
||||||
data: history.map((h) => formatValue(h["+15V"])),
|
|
||||||
borderColor: "rgba(34,197,94,1)",
|
|
||||||
backgroundColor: "rgba(34,197,94,0.5)",
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "-15V",
|
|
||||||
data: history.map((h) => formatValue(h["-15V"])),
|
|
||||||
borderColor: "rgba(239,68,68,1)",
|
|
||||||
backgroundColor: "rgba(239,68,68,0.5)",
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "-98V",
|
|
||||||
data: history.map((h) => formatValue(h["-98V"])),
|
|
||||||
borderColor: "rgba(234,179,8,1)",
|
|
||||||
backgroundColor: "rgba(234,179,8,0.5)",
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const temperatureDatasets = [
|
|
||||||
{
|
|
||||||
label: "ADC Temp",
|
|
||||||
data: history.map((h) => h["ADC Temp"]),
|
|
||||||
borderColor: "rgba(168,85,247,1)",
|
|
||||||
backgroundColor: "rgba(168,85,247,0.5)",
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "CPU Temp",
|
|
||||||
data: history.map((h) => Number(formatValue(h["CPU Temp"]))),
|
|
||||||
borderColor: "rgba(251,191,36,1)",
|
|
||||||
backgroundColor: "rgba(251,191,36,0.5)",
|
|
||||||
fill: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const baseChartOptions = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: false,
|
|
||||||
grid: {
|
|
||||||
color: "rgba(200,200,200,0.2)",
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: "Wert",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
grid: {
|
|
||||||
color: "rgba(200,200,200,0.2)",
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: "Zeit",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: "bottom" as const,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
label: function (context: TooltipItem<"line">) {
|
|
||||||
const label = context.dataset.label || "";
|
|
||||||
const value =
|
|
||||||
context.parsed.y !== null ? context.parsed.y.toFixed(2) : "";
|
|
||||||
const unit = label.includes("Temp") ? "°C" : "V";
|
|
||||||
return `${label}: ${value} ${unit}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -142,55 +45,14 @@ const SystemPage = () => {
|
|||||||
<h1 className="text-xl font-bold mb-4">
|
<h1 className="text-xl font-bold mb-4">
|
||||||
System Spannungen & Temperaturen
|
System Spannungen & Temperaturen
|
||||||
</h1>
|
</h1>
|
||||||
|
<SystemOverviewGrid voltages={voltages} onOpenDetail={handleOpenDetail} />
|
||||||
<div className="grid grid-cols-2 gap-4 mb-8">
|
<SystemCharts history={history} />
|
||||||
{Object.entries(voltages).map(([key, value]) => {
|
<DetailModal
|
||||||
const formattedValue = formatValue(value);
|
isOpen={isModalOpen}
|
||||||
const unit = key.includes("Temp") ? "°C" : "V";
|
selectedKey={selectedKey}
|
||||||
return (
|
history={history}
|
||||||
<div key={key} className="p-4 border rounded shadow">
|
onClose={handleCloseDetail}
|
||||||
<h2 className="font-semibold">{key}</h2>
|
/>
|
||||||
<p>
|
|
||||||
{formattedValue} {unit}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
|
||||||
<div className="h-[300px]">
|
|
||||||
<Line
|
|
||||||
data={{ labels, datasets: voltageDatasets }}
|
|
||||||
options={{
|
|
||||||
...baseChartOptions,
|
|
||||||
plugins: {
|
|
||||||
...baseChartOptions.plugins,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: "Systemspannungen",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="h-[300px]">
|
|
||||||
<Line
|
|
||||||
data={{ labels, datasets: temperatureDatasets }}
|
|
||||||
options={{
|
|
||||||
...baseChartOptions,
|
|
||||||
plugins: {
|
|
||||||
...baseChartOptions.plugins,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: "Systemtemperaturen",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
{
|
{
|
||||||
"win_systemVoltTemp": ["AAV09", "AAV11", "AAV15", "AAV16", "AAV17", "AAV18"]
|
"win_systemVoltTemp": [
|
||||||
|
"<%=AAV09%>",
|
||||||
|
"<%=AAV11%>",
|
||||||
|
"<%=AAV15%>",
|
||||||
|
"<%=AAV16%>",
|
||||||
|
"<%=AAV17%>",
|
||||||
|
"<%=AAV18%>"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
48
redux/slices/systemspannung5VplusSlice.ts
Normal file
48
redux/slices/systemspannung5VplusSlice.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// redux/slices/systemspannung5VplusSlice.ts
|
||||||
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import { getSystemspannung5VplusThunk } from "../thunks/getSystemspannung5VplusThunk";
|
||||||
|
|
||||||
|
type StateType = {
|
||||||
|
DIA0: any[]; // alle Werte
|
||||||
|
DIA1: any[]; // stündlich
|
||||||
|
DIA2: any[]; // täglich
|
||||||
|
isLoading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: StateType = {
|
||||||
|
DIA0: [],
|
||||||
|
DIA1: [],
|
||||||
|
DIA2: [],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const systemspannung5VplusSlice = createSlice({
|
||||||
|
name: "systemspannung5Vplus",
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(getSystemspannung5VplusThunk.pending, (state) => {
|
||||||
|
state.isLoading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
getSystemspannung5VplusThunk.fulfilled,
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ typ: "DIA0" | "DIA1" | "DIA2"; data: any[] }>
|
||||||
|
) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
state[action.payload.typ] = action.payload.data;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(getSystemspannung5VplusThunk.rejected, (state, action) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.error = action.payload as string;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default systemspannung5VplusSlice.reducer;
|
||||||
@@ -29,6 +29,7 @@ import messagesReducer from "./slices/messagesSlice";
|
|||||||
import firmwareUpdateReducer from "@/redux/slices/firmwareUpdateSlice";
|
import firmwareUpdateReducer from "@/redux/slices/firmwareUpdateSlice";
|
||||||
import confirmModalReducer from "./slices/confirmModalSlice";
|
import confirmModalReducer from "./slices/confirmModalSlice";
|
||||||
import firmwareProgressReducer from "./slices/firmwareProgressSlice";
|
import firmwareProgressReducer from "./slices/firmwareProgressSlice";
|
||||||
|
import systemspannung5VplusReducer from "./slices/systemspannung5VplusSlice";
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@@ -60,6 +61,7 @@ const store = configureStore({
|
|||||||
firmwareUpdate: firmwareUpdateReducer,
|
firmwareUpdate: firmwareUpdateReducer,
|
||||||
confirmModal: confirmModalReducer,
|
confirmModal: confirmModalReducer,
|
||||||
firmwareProgress: firmwareProgressReducer,
|
firmwareProgress: firmwareProgressReducer,
|
||||||
|
systemspannung5Vplus: systemspannung5VplusReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
16
redux/thunks/getSystemspannung5VplusThunk.ts
Normal file
16
redux/thunks/getSystemspannung5VplusThunk.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// redux/thunks/getSystemspannung5VplusThunk.ts
|
||||||
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
|
import { fetchSystemspannung5VplusService } from "@/services/fetchSystemspannung5VplusService";
|
||||||
|
|
||||||
|
export const getSystemspannung5VplusThunk = createAsyncThunk(
|
||||||
|
"systemspannung5Vplus/fetch",
|
||||||
|
async (typ: "DIA0" | "DIA1" | "DIA2", thunkAPI) => {
|
||||||
|
try {
|
||||||
|
const data = await fetchSystemspannung5VplusService(typ);
|
||||||
|
return { typ, data };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler in getSystemspannung5VplusThunk:", error);
|
||||||
|
return thunkAPI.rejectWithValue("Fehler beim Laden der 5V-Daten");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
25
services/fetchSystemspannung5VplusService.ts
Normal file
25
services/fetchSystemspannung5VplusService.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// services/fetchSystemspannung5VplusService.ts
|
||||||
|
|
||||||
|
type Typ = "DIA0" | "DIA1" | "DIA2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holt Messwerte für +5V aus der passenden JSON-Datei über die API
|
||||||
|
* @param typ - Typ der Daten: DIA0 = alle, DIA1 = stündlich, DIA2 = täglich
|
||||||
|
*/
|
||||||
|
export const fetchSystemspannung5VplusService = async (typ: Typ) => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/cpl/getSystemspannung5VplusHandler?typ=${typ}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Fehler beim Abrufen: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Fehler in fetchSystemspannung5VplusService:", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user