feat: DetailModal um Min/Max/Durchschnitt ergänzt

- Chart zeigt jetzt zusätzlich zu Messwert auch Minimal-, Maximal- und Durchschnittswerte an
- Datenstruktur an Redux angepasst (i, a, g)
- Darstellung entspricht jetzt LoopMeasurementChart
This commit is contained in:
Ismail Ali
2025-07-10 19:11:38 +02:00
parent 3a1d85dbe2
commit 420989dc9f
13 changed files with 77 additions and 470 deletions

View File

@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
NEXT_PUBLIC_EXPORT_STATIC=false
NEXT_PUBLIC_USE_CGI=false
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.581
NEXT_PUBLIC_APP_VERSION=1.6.583
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)

View File

@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
NEXT_PUBLIC_EXPORT_STATIC=true
NEXT_PUBLIC_USE_CGI=true
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.581
NEXT_PUBLIC_APP_VERSION=1.6.583
NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,13 @@
## [1.6.583] 2025-07-10
- eslintrc.json : "@typescript-eslint/no-unused-vars": "warn"
---
## [1.6.582] 2025-07-10
- eslintrc.json : "@typescript-eslint/no-unused-vars": "warn"
---
## [1.6.581] 2025-07-10
- fix: Bei System: Detailansicht: Zeitraum von bis fehlt. Ganzseitenansicht fehlt noch. gelöst mit zoom und pan

View File

@@ -38,14 +38,31 @@ ChartJS.register(
const initialChartData = {
datasets: [
{
label: "Messdaten",
label: "Minimum",
data: [],
borderColor: "rgba(61, 176, 242, 1)",
borderColor: "lightgrey",
backgroundColor: "rgba(211,211,211,0.3)",
borderWidth: 2,
pointRadius: 0,
tension: 0.1,
},
{
label: "Maximum",
data: [],
borderColor: "lightgrey",
backgroundColor: "rgba(211,211,211,0.3)",
borderWidth: 2,
pointRadius: 0,
tension: 0.1,
},
{
label: "Durchschnitt",
data: [],
borderColor: "rgba(59,130,246,1)",
backgroundColor: "rgba(59,130,246,0.3)",
borderWidth: 2,
pointRadius: 0,
tension: 0.1,
fill: false,
},
],
};
@@ -182,10 +199,20 @@ export const DetailModal = ({
useEffect(() => {
if (chartRef.current) {
const chart = chartRef.current;
chart.data.datasets[0].data = [...reduxData]
.reverse()
.map((p) => ({ x: new Date(p.t), y: p.i }));
chart.update("none");
const sortedData = [...reduxData].reverse();
chart.data.datasets[0].data = sortedData.map((p) => ({
x: new Date(p.t),
y: p.i,
}));
chart.data.datasets[1].data = sortedData.map((p) => ({
x: new Date(p.t),
y: p.a,
}));
chart.data.datasets[2].data = sortedData.map((p) => ({
x: new Date(p.t),
y: p.g,
}));
}
}, [reduxData]);

View File

@@ -1,17 +0,0 @@
{
"win_appVersion": "0.02",
"win_deviceName": "CPLV4 Ismail Rastede",
"win_mac1": "0 48 86 81 46 143",
"win_ip": "10.10.0.243",
"win_subnet": "255.255.255.0",
"win_gateway": "10.10.0.1",
"win_cplInternalTimestamp": "23.10.24 15:10:28 Uhr",
"win_opcState": "1",
"win_opcSessions": "0",
"win_opcName": "CPL V4 OPC UA Application Deutsche Bahne",
"win_ntp1": "192.53.103.108",
"win_ntp2": "0.0.0.0",
"win_ntp3": "0.0.0.0",
"win_ntpTimezone": "2",
"win_ntpActive": "1"
}

View File

@@ -1,14 +0,0 @@
{
"win_da_state": [
0,
0,
1,
1
],
"win_da_bezeichnung": [
"DA1",
"Ausgang2",
"Ausgang3",
"Ausgang4"
]
}

View File

@@ -1,200 +0,0 @@
{
"win_kueOnline": [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1
],
"win_kuePSTmMinus96V": [
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_kueCableBreak": [
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1
],
"win_kueGroundFault": [
1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_kueAlarm1": [
1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0
],
"win_kueAlarm2": [
1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_kueOverflow": [
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_kueIso": [
10, 10, 10, 10.5, 10, 10, 10, 10, 10.5, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10.5, 10, 10, 10, 10, 10, 10.5, 10, 200, 200, 200, 200
],
"win_kueLimit1": [
3, 9.9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
],
"win_kueDelay1": [
3, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420,
420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420,
420, 420
],
"win_kueResidence": [
0, 0.612, 0, 0.645, 0.822, 0.97, 0, 0, 1.452, 0, 0.734, 0.37, 0.566, 0,
0.738, 0.684, 1.166, 0.595, 0, 1.651, 1.18, 1.387, 1.214, 0, 1.475, 0.615,
0.494, 1.217, 65, 65, 65, 65
],
"win_kueLimit2Low": [
3, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
0.1, 0.1
],
"win_kueLimit2High": [
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1
],
"win_kueLoopInterval": [
3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6
],
"win_kueVersion": [
420, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419,
419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419,
419, 419
],
"win_kueID": [
"Test3",
"B23",
"Kabel 3",
"Kabel 4",
"Kabel 5",
"Kabel 6",
"FTZ4562",
"Kabel 8",
"12344",
"Kabel 10",
"Kabel 11",
"Kabel 12",
"Kabel 13",
"Kabel 14",
"Kabel 15",
"H56-77",
"Kabel 17",
"Kabel 18",
"Kabel 19",
"Kabel 20",
"Kabel 21",
"Kabel 22",
"Kabel 23",
"Kabel 24",
"Kabel 25",
"Kabel 26",
"Kabel 27",
"Kabel 28",
"Kabel 29",
"Kabel 30",
"Kabel 31",
"Kabel 32"
],
"win_kueName": [
"Linie 2",
"Edewecht 3",
"",
"Linie 4",
"Linie 5",
"",
"",
"Kabel_8",
"Kabel_9",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"Kabel 32"
],
"win_tdrActive": [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1
],
"win_tdrAtten": [
11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2
],
"win_tdrSpeed": [
112, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100
],
"win_tdrTrigger": [
102, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80
],
"win_tdrPulse": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_tdrAmp": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_tdrLocation": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
],
"win_tdrLast": [
"2024-10-17 07:51:54:000",
"2024-09-30 08:38:50:000",
"?",
"?",
"?",
"?",
"?",
"?",
"2024-09-30 08:36:43:000",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?"
],
"win_memoryInterval": [
5, 0, 15, 0, 0, 15, 15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0
]
}

View File

@@ -1,142 +0,0 @@
[
{
"id": 25068,
"code": "02101",
"timestamp": "2025-04-22 04:56:28",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25067,
"code": "02101",
"timestamp": "2025-04-22 04:55:43",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25066,
"code": "02101",
"timestamp": "2025-04-22 04:48:39",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25065,
"code": "02101",
"timestamp": "2025-04-22 04:46:02",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25064,
"code": "02101",
"timestamp": "2025-04-22 04:38:58",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25063,
"code": "02101",
"timestamp": "2025-04-22 04:36:44",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25062,
"code": "02401",
"timestamp": "2025-04-22 04:35:38",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25061,
"code": "02401",
"timestamp": "2025-04-22 04:28:33",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25060,
"code": "02101",
"timestamp": "2025-04-22 02:56:28",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25059,
"code": "02101",
"timestamp": "2025-04-22 02:56:06",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25058,
"code": "02101",
"timestamp": "2025-04-22 02:40:27",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25057,
"code": "02101",
"timestamp": "2025-04-22 02:40:05",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25056,
"code": "02101",
"timestamp": "2025-04-22 02:26:40",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25055,
"code": "02101",
"timestamp": "2025-04-22 02:26:17",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25054,
"code": "02101",
"timestamp": "2025-04-22 02:16:56",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25053,
"code": "02101",
"timestamp": "2025-04-22 02:16:34",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25052,
"code": "02101",
"timestamp": "2025-04-22 02:09:30",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25051,
"code": "02101",
"timestamp": "2025-04-22 02:01:18",
"message": "Isofehler kommend",
"status": 1
},
{
"id": 25050,
"code": "02101",
"timestamp": "2025-04-22 01:54:35",
"message": "Isofehler gehend",
"status": 0
},
{
"id": 25049,
"code": "02101",
"timestamp": "2025-04-22 01:54:13",
"message": "Isofehler kommend",
"status": 1
}
]

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "cpl-v4",
"version": "1.6.581",
"version": "1.6.583",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cpl-v4",
"version": "1.6.581",
"version": "1.6.583",
"dependencies": {
"@fontsource/roboto": "^5.1.0",
"@headlessui/react": "^2.2.4",

View File

@@ -1,6 +1,6 @@
{
"name": "cpl-v4",
"version": "1.6.581",
"version": "1.6.583",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -8,26 +8,14 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
try {
/* if (mode === "json") {
const filePath = path.join(
process.cwd(),
"mocks/api/SERVICE/analogInputsMockData.json"
);
const content = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(content);
return res.status(200).json(data);
} */
if (mode === "json") {
const filePath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json"
);
const jsonContent = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(jsonContent);
res.status(200).json(data);
return;
}
const filePath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json"
);
const jsonContent = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(jsonContent);
res.status(200).json(data);
return;
} catch (error) {
console.error(error);
res.status(500).json({ error: "Internal Server Error" });

View File

@@ -3,7 +3,6 @@
import { NextApiRequest, NextApiResponse } from "next";
import path from "path";
import fs from "fs/promises";
import { readFileSync } from "fs";
export default async function handler(
req: NextApiRequest,
@@ -11,25 +10,12 @@ export default async function handler(
) {
const mode = process.env.NEXT_PUBLIC_CPL_MODE ?? "json";
if (mode === "json") {
// Lese JSON-Datei z.B. digitalOutputsMockData.json
const filePath = path.join(
process.cwd(),
"mocks/api/SERVICE/digitalOutputsMockData.json"
);
const content = await fs.readFile(filePath, "utf-8");
const data = JSON.parse(content);
return res.status(200).json(data);
}
if (mode === "jsSimulatedProd") {
const digitalOutputsScript = readFileSync(
"mocks/device-cgi-simulator/SERVICE/digitalOutputsMockData.js"
);
res.setHeader("Content-Type", "application/javascript");
res.status(200).send(digitalOutputsScript);
return;
}
return res.status(400).json({ error: "Unsupported mode" });
// Lese JSON-Datei z.B. digitalOutputsMockData.json
const filePath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/SERVICE/digitalOutputsMockData.json"
);
const content = await fs.readFile(filePath, "utf-8");
const data = JSON.parse(content);
return res.status(200).json(data);
}

View File

@@ -15,51 +15,20 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(400).json({ error: "Missing or invalid updates array" });
}
if (mode === "json") {
const filePath = path.join(
process.cwd(),
"mocks/api/SERVICE/analogInputsMockData.json"
);
const content = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(content);
const filePath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json"
);
const content = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(content);
for (const update of updates) {
const { key, index, value } = update;
if (Array.isArray(data[key]) && index < data[key].length) {
data[key][index] = value;
}
for (const update of updates) {
const { key, index, value } = update;
if (Array.isArray(data[key]) && index < data[key].length) {
data[key][index] = value;
}
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
return res.status(200).json({ success: true });
}
if (mode === "jsSimulatedProd") {
const filePath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.js"
);
let content = fs.readFileSync(filePath, "utf-8");
for (const update of updates) {
const { key, index, value } = update;
const regex = new RegExp(`var\\s+${key}\\s*=\\s*\\[([\\s\\S]*?)\\];`);
const match = content.match(regex);
if (!match) continue;
const items = match[1].split(",").map((s) => s.trim());
if (index >= items.length) continue;
const isString = typeof value === "string";
items[index] = isString ? `"${value}"` : `${value}`;
const updatedLine = `var ${key} = [${items.join(", ")}];`;
content = content.replace(regex, updatedLine);
}
fs.writeFileSync(filePath, content, "utf-8");
return res.status(200).json({ success: true });
}
return res.status(400).json({ error: "Unsupported mode" });
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
return res.status(200).json({ success: true });
}