Sends Authorization: Basic <base64(user:pass)> with configurable credentials.
Accepts --user and --pass, or env CPL_USER/CPL_PASS; defaults to Littwin/Littwin.
Uses an https.Agent({ rejectUnauthorized: false }) when --insecure is set.
Corrected output folder to cable-monitoring-data.
CLI parser supports both --key=value and --key value (PowerShell friendly).
Quick usage (PowerShell)
All 32 slots, both types (iso=3, rsl=4), all DIA modes, last 30 days:
npm run mocks:cable
Specific date range (e.g., 2025-07-13 to 2025-08-12), all slots and types:
node .\mocks\scripts\fetchCableData.mjs --from 2025-07-13 --to 2025-08-12 --insecure
Only slot 0, Isolationswiderstand, DIA1:
node .\mocks\scripts\fetchCableData.mjs --slots 0 --modes DIA1 --types iso --from 2025-07-13 --to 2025-08-12 --insecure
Provide credentials explicitly:
node .\mocks\scripts\fetchCableData.mjs --user Littwin --pass Littwin --insecure
Or via environment variables for the session:
$env:CPL_USER = "Littwin"; $env:CPL_PASS = "Littwin"
node .\mocks\scripts\fetchCableData.mjs --insecure
Output structure
mocks/device-cgi-simulator/cable-monitoring-data/slot{0..31}/
isolationswiderstand/DIA0.json, DIA1.json, DIA2.json
schleifenwiderstand/DIA0.json, DIA1.json, DIA2.json
I smoke-tested slot 0, DIA1, iso with login and it produced DIA1.json under slot0/isolationswiderstand. If you need me to also add a convenience npm script with user/pass placeholders, say the credentials source you prefer (env vs args), and I’ll wire it.
214 lines
6.2 KiB
JavaScript
214 lines
6.2 KiB
JavaScript
#!/usr/bin/env node
|
|
// Fetch and update cable monitoring mock data for 32 slots (0-31)
|
|
// Writes to mocks/device-cgi-simulator/cable-monitoring-data/slot{N}/{isolationswiderstand|schleifenwiderstand}/DIA{0|1|2}.json
|
|
// Usage examples (PowerShell):
|
|
// node mocks/scripts/fetchCableData.mjs --base https://10.10.0.118 --from 2025-07-13 --to 2025-08-12 --insecure
|
|
// node mocks/scripts/fetchCableData.mjs --slots 0-3 --modes DIA0,DIA1 --types iso --insecure
|
|
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import https from "node:https";
|
|
|
|
// Simple CLI args parser supporting "--key=value" and "--key value"
|
|
const args = (() => {
|
|
const out = {};
|
|
const argv = process.argv.slice(2);
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const tok = argv[i];
|
|
if (tok.startsWith("--")) {
|
|
const eq = tok.indexOf("=");
|
|
if (eq !== -1) {
|
|
const key = tok.slice(2, eq);
|
|
const val = tok.slice(eq + 1);
|
|
out[key] = val;
|
|
} else {
|
|
const key = tok.slice(2);
|
|
const next = argv[i + 1];
|
|
if (next && !next.startsWith("--")) {
|
|
out[key] = next;
|
|
i++;
|
|
} else {
|
|
out[key] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out;
|
|
})();
|
|
|
|
const BASE_URL =
|
|
args.base || process.env.CPL_DEVICE_BASE_URL || "https://10.10.0.118";
|
|
const MODES = String(args.modes || "DIA0,DIA1,DIA2")
|
|
.split(",")
|
|
.map((s) => s.trim())
|
|
.filter(Boolean);
|
|
const TYPES = String(args.types || "iso,rsl")
|
|
.split(",")
|
|
.map((s) => s.trim())
|
|
.filter(Boolean); // iso -> 3, rsl -> 4
|
|
const SLOTS = (() => {
|
|
if (args.slots) {
|
|
if (String(args.slots).includes("-")) {
|
|
const [a, b] = String(args.slots).split("-").map(Number);
|
|
return Array.from({ length: b - a + 1 }, (_, i) => a + i);
|
|
}
|
|
return String(args.slots)
|
|
.split(",")
|
|
.map((s) => Number(s.trim()));
|
|
}
|
|
return Array.from({ length: 32 }, (_, i) => i);
|
|
})();
|
|
|
|
const today = new Date();
|
|
const defaultFrom = new Date(today);
|
|
defaultFrom.setDate(today.getDate() - 30);
|
|
|
|
const fromISO =
|
|
args.from ||
|
|
process.env.CPL_FROM_DATE ||
|
|
defaultFrom.toISOString().slice(0, 10); // YYYY-MM-DD
|
|
const toISO =
|
|
args.to || process.env.CPL_TO_DATE || today.toISOString().slice(0, 10);
|
|
|
|
const OUT_DIR =
|
|
args.out ||
|
|
path.join(
|
|
process.cwd(),
|
|
"mocks",
|
|
"device-cgi-simulator",
|
|
"cable-monitoring-data"
|
|
);
|
|
const INSECURE = Boolean(args.insecure || process.env.CPL_INSECURE);
|
|
if (INSECURE) {
|
|
// Allow self-signed certs for local device
|
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
}
|
|
|
|
// Basic Auth credentials (same default as fetchSystemData.mjs)
|
|
const USERNAME = args.user || process.env.CPL_USER || "Littwin";
|
|
const PASSWORD = args.pass || process.env.CPL_PASS || "Littwin";
|
|
const AUTH_HEADER =
|
|
"Basic " + Buffer.from(`${USERNAME}:${PASSWORD}`).toString("base64");
|
|
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
const toACPDate = (iso) => {
|
|
const [y, m, d] = iso.split("-");
|
|
return `${y};${m};${d}`;
|
|
};
|
|
|
|
const typeToFolderAndCode = (typeKey) => {
|
|
switch (typeKey) {
|
|
case "iso":
|
|
return { folder: "isolationswiderstand", code: 3 };
|
|
case "rsl":
|
|
case "schleife":
|
|
return { folder: "schleifenwiderstand", code: 4 };
|
|
default:
|
|
throw new Error(`Unknown type '${typeKey}'. Use iso or rsl.`);
|
|
}
|
|
};
|
|
|
|
const buildUrl = (mode, from, to, slot, typeCode) =>
|
|
`${BASE_URL}/CPL?seite.ACP&${mode}=${toACPDate(from)};${toACPDate(
|
|
to
|
|
)};${slot};${typeCode};`;
|
|
|
|
async function fetchJson(url) {
|
|
// Prefer node-fetch when INSECURE to pass https.Agent; otherwise use global fetch when available
|
|
let doFetch = globalThis.fetch;
|
|
if (INSECURE || typeof doFetch === "undefined") {
|
|
const { default: nf } = await import("node-fetch");
|
|
doFetch = nf;
|
|
}
|
|
const init = { headers: { Authorization: AUTH_HEADER } };
|
|
if (INSECURE) {
|
|
init.agent = new https.Agent({ rejectUnauthorized: false });
|
|
}
|
|
const res = await doFetch(url, init);
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => "");
|
|
throw new Error(`HTTP ${res.status} ${res.statusText} for ${url}\n${text}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
async function ensureDir(dir) {
|
|
await fs.mkdir(dir, { recursive: true });
|
|
}
|
|
|
|
async function writeJson(file, data) {
|
|
await ensureDir(path.dirname(file));
|
|
await fs.writeFile(file, JSON.stringify(data, null, 2), "utf8");
|
|
}
|
|
|
|
async function main() {
|
|
console.log(`➡️ Fetch cable data`);
|
|
console.log(` Base: ${BASE_URL}`);
|
|
console.log(` Range: ${fromISO} → ${toISO}`);
|
|
console.log(` Slots: ${SLOTS.join(",")}`);
|
|
console.log(` Modes: ${MODES.join(",")}`);
|
|
console.log(` Types: ${TYPES.join(",")}`);
|
|
console.log(` Out: ${OUT_DIR}`);
|
|
if (INSECURE) console.log(` TLS: insecure (self-signed allowed)`);
|
|
|
|
// Small concurrency to avoid hammering the device
|
|
const MAX_CONCURRENCY = Number(args.concurrency || 3);
|
|
let running = 0;
|
|
let idx = 0;
|
|
|
|
const tasks = [];
|
|
for (const slot of SLOTS) {
|
|
for (const typeKey of TYPES) {
|
|
const { folder, code } = typeToFolderAndCode(typeKey);
|
|
for (const mode of MODES) {
|
|
tasks.push({ slot, typeKey, folder, code, mode });
|
|
}
|
|
}
|
|
}
|
|
|
|
const runNext = async () => {
|
|
if (idx >= tasks.length) return;
|
|
const task = tasks[idx++];
|
|
running++;
|
|
try {
|
|
const url = buildUrl(task.mode, fromISO, toISO, task.slot, task.code);
|
|
console.log(`→ [slot ${task.slot}] ${task.folder} ${task.mode}`);
|
|
const data = await fetchJson(url);
|
|
const file = path.join(
|
|
OUT_DIR,
|
|
`slot${task.slot}`,
|
|
task.folder,
|
|
`${task.mode}.json`
|
|
);
|
|
await writeJson(file, data);
|
|
} catch (e) {
|
|
console.error(
|
|
`✖ Fehler bei slot ${task.slot} ${task.folder} ${task.mode}:`,
|
|
e.message || e
|
|
);
|
|
} finally {
|
|
running--;
|
|
// polite delay between requests
|
|
await sleep(Number(args.delay || 150));
|
|
if (idx < tasks.length) runNext();
|
|
}
|
|
};
|
|
|
|
// Start initial workers
|
|
const starters = Math.min(MAX_CONCURRENCY, tasks.length);
|
|
for (let i = 0; i < starters; i++) runNext();
|
|
|
|
// Wait for all to finish
|
|
while (running > 0 || idx < tasks.length) {
|
|
await sleep(100);
|
|
}
|
|
|
|
console.log("✅ Fertig.");
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error("Unerwarteter Fehler:", e);
|
|
process.exit(1);
|
|
});
|