feat: local-cpl-sim.mjs digitalInputs /Messwerteingänge

This commit is contained in:
ISA
2025-09-04 13:24:10 +02:00
parent 0286670b81
commit 9c7ad37233
6 changed files with 135 additions and 6 deletions

View File

@@ -14,6 +14,30 @@ const ROOT = path.join(process.cwd(), "out");
const textCache = new Map(); // key: absolute path, value: { mtimeMs, body, contentType }
let messagesAllCache = null; // cache parsed meldungen/messages_all.json
// Helper to read JSON body from POST requests
function readRequestBody(req) {
return new Promise((resolve, reject) => {
let data = "";
req.on("data", (chunk) => {
data += chunk;
// Basic guard against huge bodies in local dev
if (data.length > 1_000_000) {
req.destroy();
reject(new Error("Request body too large"));
}
});
req.on("end", () => {
try {
const json = data ? JSON.parse(data) : {};
resolve(json);
} catch {
resolve({});
}
});
req.on("error", reject);
});
}
function exists(p) {
try {
fs.accessSync(p, fs.constants.F_OK);
@@ -453,7 +477,7 @@ function notFound(res) {
res.end("Not Found");
}
const server = http.createServer((req, res) => {
const server = http.createServer(async (req, res) => {
try {
const url = new URL(req.url, `http://localhost:${PORT}`);
const pathname = decodeURIComponent(url.pathname);
@@ -501,6 +525,106 @@ const server = http.createServer((req, res) => {
return;
}
// Dev API: digital inputs getter used by exported app on localhost
if (pathname === "/api/cpl/getDigitalInputsHandler") {
try {
const p = path.join(
process.cwd(),
"mocks",
"device-cgi-simulator",
"SERVICE",
"digitalInputsMockData.json"
);
const raw = fs.readFileSync(p, "utf8");
res.writeHead(200, {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
});
res.end(raw);
} catch {
res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Interner Serverfehler" }));
}
return;
}
// Dev API: digital inputs updater used by UI Save button in dev/exported mode
if (pathname === "/api/cpl/updateDigitalInputs") {
if (req.method !== "POST") {
res.writeHead(405, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Only POST supported" }));
return;
}
try {
const body = await readRequestBody(req);
const {
id,
label,
invert,
timeFilter,
weighting,
zaehlerAktiv,
eingangOffline,
} = body || {};
if (typeof id !== "number" || id < 1 || id > 32) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Ungültige ID (132 erlaubt)" }));
return;
}
const fp = path.join(
process.cwd(),
"mocks",
"device-cgi-simulator",
"SERVICE",
"digitalInputsMockData.json"
);
const current = JSON.parse(fs.readFileSync(fp, "utf8"));
const idx = id - 1;
if (typeof label === "string" && Array.isArray(current.win_de_label))
current.win_de_label[idx] = label;
if (typeof invert === "number" && Array.isArray(current.win_de_invert))
current.win_de_invert[idx] = invert;
if (
typeof timeFilter === "number" &&
Array.isArray(current.win_de_time_filter)
)
current.win_de_time_filter[idx] = timeFilter;
if (
typeof weighting === "number" &&
Array.isArray(current.win_de_weighting)
)
current.win_de_weighting[idx] = weighting;
if (
typeof zaehlerAktiv === "number" &&
Array.isArray(current.win_de_counter_active)
)
current.win_de_counter_active[idx] = zaehlerAktiv;
if (
typeof eingangOffline === "number" &&
Array.isArray(current.win_de_offline)
)
current.win_de_offline[idx] = eingangOffline;
fs.writeFileSync(fp, JSON.stringify(current, null, 2), "utf8");
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
message: `Update erfolgreich für ID ${id}`,
id,
label,
invert,
timeFilter,
weighting,
eingangOffline,
})
);
} catch {
res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Update fehlgeschlagen" }));
}
return;
}
// CPL? mapping: /CPL?/CPL/... -> /CPL/... and service commands
if (pathname === "/CPL" && rawQuery) {
const q = decodeURIComponent(rawQuery);