main.js aktualisiert

This commit is contained in:
2025-09-24 19:30:49 +00:00
parent bba2e2c416
commit 45276945dc

482
main.js
View File

@@ -1,244 +1,238 @@
// main.js // main.js
const { app, BrowserWindow } = require('electron'); const { app, BrowserWindow } = require('electron');
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const chokidar = require("chokidar"); const chokidar = require("chokidar");
const axios = require("axios"); const axios = require("axios");
let OUT_DIR; // endgültiger Eingangsordner (wird in app.whenReady gesetzt) let OUT_DIR; // endgültiger Eingangsordner (wird in app.whenReady gesetzt)
let IMG_DIR; // Ausgabeordner für latest.png let IMG_DIR; // Ausgabeordner für PNGs (History)
let win; let win;
function createWindow() { function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
width: 1000, width: 1000,
height: 800, height: 800,
webPreferences: { nodeIntegration: true, contextIsolation: false } webPreferences: { nodeIntegration: true, contextIsolation: false }
}); });
win.loadFile(path.join(__dirname, "index.html")); win.loadFile(path.join(__dirname, "index.html"));
} }
app.whenReady().then(() => { app.whenReady().then(() => {
// Versuche zuerst C:\ZIR_Output (praktisch für Windows Local Port) // Versuche zuerst C:\ZIR_Output (praktisch für Windows Local Port)
const preferedRoot = "C:\\ZIR_Output"; const preferedRoot = "C:\\ZIR_Output";
let dataDir = app.getPath("userData"); // fallback let dataDir = app.getPath("userData"); // fallback
try { try {
// Versuch: C:\ZIR_Output anlegen (falls möglich) // Versuch: C:\ZIR_Output anlegen (falls möglich)
if (!fs.existsSync(preferedRoot)) { if (!fs.existsSync(preferedRoot)) {
fs.mkdirSync(preferedRoot, { recursive: true }); fs.mkdirSync(preferedRoot, { recursive: true });
} }
OUT_DIR = preferedRoot; OUT_DIR = preferedRoot;
} catch (e) { } catch (e) {
// Falls es schiefgeht (Rechte), fallback in userData // Falls es schiefgeht (Rechte), fallback in userData
OUT_DIR = path.join(dataDir, "ZIR_Input"); OUT_DIR = path.join(dataDir, "ZIR_Input");
} }
// IMG_DIR im userData (schützt vor Schreibrechten) // IMG_DIR im userData (schützt vor Schreibrechten)
IMG_DIR = path.join(dataDir, "zpl_out"); IMG_DIR = path.join(dataDir, "zpl_out");
// Ordner anlegen, falls nicht existierend // Ordner anlegen, falls nicht existierend
try { if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true }); } catch(e) {} try { if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true }); } catch(e) {}
try { if (!fs.existsSync(IMG_DIR)) fs.mkdirSync(IMG_DIR, { recursive: true }); } catch(e) {} try { if (!fs.existsSync(IMG_DIR)) fs.mkdirSync(IMG_DIR, { recursive: true }); } catch(e) {}
console.log("ZIR Input (Eingangsordner):", OUT_DIR); console.log("ZIR Input (Eingangsordner):", OUT_DIR);
console.log("ZIR Output (PNG-Ordner):", IMG_DIR); console.log("ZIR Output (PNG-Ordner):", IMG_DIR);
createWindow(); createWindow();
const watcher = chokidar.watch(OUT_DIR, { ignoreInitial: true, awaitWriteFinish: {stabilityThreshold: 500, pollInterval: 100} }); const watcher = chokidar.watch(OUT_DIR, { ignoreInitial: true, awaitWriteFinish: {stabilityThreshold: 500, pollInterval: 100} });
watcher.on("add", file => { watcher.on("add", file => {
if (!file.toLowerCase().endsWith(".zpl")) return; if (!file.toLowerCase().endsWith(".zpl")) return;
processFile(file); processFile(file);
}); });
watcher.on("change", file => { watcher.on("change", file => {
if (!file.toLowerCase().endsWith(".zpl")) return; if (!file.toLowerCase().endsWith(".zpl")) return;
processFile(file); processFile(file);
}); });
app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); });
}); });
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
/* --- Helferfunktionen --- */ /* --- Helferfunktionen --- */
function extractZPL(raw) { function extractZPL(raw) {
const start = raw.indexOf("^XA"); const start = raw.indexOf("^XA");
const end = raw.lastIndexOf("^XZ"); const end = raw.lastIndexOf("^XZ");
if (start >= 0 && end >= 0 && end > start) return raw.slice(start, end + 3); if (start >= 0 && end >= 0 && end > start) return raw.slice(start, end + 3);
if (start >= 0) return raw.slice(start) + (raw.includes("^XZ") ? "" : "^XZ"); if (start >= 0) return raw.slice(start) + (raw.includes("^XZ") ? "" : "^XZ");
return "^XA\n" + raw + "\n^XZ"; return "^XA\n" + raw + "\n^XZ";
} }
function cleanControlChars(s) { function cleanControlChars(s) {
return s.replace(/[\x00-\x1F]/g, ''); return s.replace(/[\x00-\x1F]/g, '');
} }
function parsePWLL(zpl) { function parsePWLL(zpl) {
const pwMatch = zpl.match(/\^PW\s*?(\d+)/i) || zpl.match(/\^PW(\d+)/i); const pwMatch = zpl.match(/\^PW\s*?(\d+)/i) || zpl.match(/\^PW(\d+)/i);
const llMatch = zpl.match(/\^LL\s*?(\d+)/i) || zpl.match(/\^LL(\d+)/i); const llMatch = zpl.match(/\^LL\s*?(\d+)/i) || zpl.match(/\^LL(\d+)/i);
const pw = pwMatch ? parseInt(pwMatch[1], 10) : null; const pw = pwMatch ? parseInt(pwMatch[1], 10) : null;
const ll = llMatch ? parseInt(llMatch[1], 10) : null; const ll = llMatch ? parseInt(llMatch[1], 10) : null;
return { pw, ll }; return { pw, ll };
} }
function parseMaxFO(zpl) { function parseMaxFO(zpl) {
let maxX = 0, maxY = 0; let maxX = 0, maxY = 0;
const re = /\^FO\s*?(\d+)\s*,\s*(\d+)/g; const re = /\^FO\s*?(\d+)\s*,\s*(\d+)/g;
let m; let m;
while ((m = re.exec(zpl)) !== null) { while ((m = re.exec(zpl)) !== null) {
const x = parseInt(m[1], 10); const x = parseInt(m[1], 10);
const y = parseInt(m[2], 10); const y = parseInt(m[2], 10);
if (!isNaN(x) && x > maxX) maxX = x; if (!isNaN(x) && x > maxX) maxX = x;
if (!isNaN(y) && y > maxY) maxY = y; if (!isNaN(y) && y > maxY) maxY = y;
} }
return { maxX, maxY }; return { maxX, maxY };
} }
function dotsToInches(dots, dpi) { function dotsToInches(dots, dpi) {
if (!dots || !dpi) return null; if (!dots || !dpi) return null;
return dots / dpi; return dots / dpi;
} }
function fmtInches(v) { function fmtInches(v) {
return (Math.round(v * 100) / 100).toString(); return (Math.round(v * 100) / 100).toString();
} }
async function tryLabelaryRender(zpl, printerKey, widthInches, heightInches) { async function tryLabelaryRender(zpl, printerKey, widthInches, heightInches) {
const url = `http://api.labelary.com/v1/printers/${printerKey}/labels/${widthInches}x${heightInches}/0/`; const url = `http://api.labelary.com/v1/printers/${printerKey}/labels/${widthInches}x${heightInches}/0/`;
try { try {
const resp = await axios.post(url, zpl, { const resp = await axios.post(url, zpl, {
headers: { "Accept": "image/png" }, headers: { "Accept": "image/png" },
responseType: "arraybuffer", responseType: "arraybuffer",
timeout: 15000 timeout: 15000
}); });
return { ok: true, buffer: resp.data }; return { ok: true, buffer: resp.data };
} catch (err) { } catch (err) {
const status = err.response ? err.response.status : null; const status = err.response ? err.response.status : null;
return { ok: false, status, error: err.message }; return { ok: false, status, error: err.message };
} }
} }
/* --- Größen & Druckeroptionen --- */ /* --- Größen & Druckeroptionen --- */
const STANDARD_SIZES = [ const STANDARD_SIZES = [
{ w: 8.27, h: 11.69, name: "A4" }, { w: 8.27, h: 11.69, name: "A4" },
{ w: 8.5, h: 11, name: "Letter" }, { w: 8.5, h: 11, name: "Letter" },
{ w: 4, h: 6, name: "4x6" }, { w: 4, h: 6, name: "4x6" },
{ w: 3, h: 2, name: "3x2" }, { w: 3, h: 2, name: "3x2" },
{ w: 4, h: 3, name: "4x3" }, { w: 4, h: 3, name: "4x3" },
{ w: 2, h: 6, name: "2x6" }, { w: 2, h: 6, name: "2x6" },
{ w: 1, h: 1, name: "1x1" } { w: 1, h: 1, name: "1x1" }
]; ];
const PRINTER_OPTIONS = [ const PRINTER_OPTIONS = [
{ key: "8dpmm", dpi: 203 }, { key: "8dpmm", dpi: 203 },
{ key: "12dpmm", dpi: 305 } { key: "12dpmm", dpi: 305 }
]; ];
/* --- Hauptverarbeitung --- */ /* --- Hauptverarbeitung --- */
let busy = false; let busy = false;
async function processFile(filePath) { async function processFile(filePath) {
if (busy) { if (busy) {
setTimeout(() => processFile(filePath), 400); setTimeout(() => processFile(filePath), 400);
return; return;
} }
busy = true; busy = true;
try { try {
console.log("Verarbeite:", filePath); console.log("Verarbeite:", filePath);
let raw = fs.readFileSync(filePath, "utf-8"); let raw = fs.readFileSync(filePath, "utf-8");
raw = cleanControlChars(raw); raw = cleanControlChars(raw);
let zpl = extractZPL(raw); let zpl = extractZPL(raw);
const { pw, ll } = parsePWLL(zpl); const { pw, ll } = parsePWLL(zpl);
const { maxX, maxY } = parseMaxFO(zpl); const { maxX, maxY } = parseMaxFO(zpl);
console.log("Parsed PW/LL:", pw, ll, "maxFO:", maxX, maxY); console.log("Parsed PW/LL:", pw, ll, "maxFO:", maxX, maxY);
// sizesToTry aufbauen (Priorität: PW/LL -> geschätzte große Formate -> Standardgrößen) // sizesToTry aufbauen (Priorität: PW/LL -> geschätzte große Formate -> Standardgrößen)
const sizesToTry = []; const sizesToTry = [];
if (pw && ll) { if (pw && ll) {
for (const p of PRINTER_OPTIONS) { for (const p of PRINTER_OPTIONS) {
const wIn = dotsToInches(pw, p.dpi); const wIn = dotsToInches(pw, p.dpi);
const hIn = dotsToInches(ll, p.dpi); const hIn = dotsToInches(ll, p.dpi);
if (wIn && hIn) { if (wIn && hIn) {
sizesToTry.push({ w: wIn, h: hIn, printer: p }); sizesToTry.push({ w: wIn, h: hIn, printer: p });
sizesToTry.push({ w: hIn, h: wIn, printer: p, rotated: true }); sizesToTry.push({ w: hIn, h: wIn, printer: p, rotated: true });
} }
} }
} }
const estDefaultDPI = 203; const estDefaultDPI = 203;
const estWIn = maxX ? dotsToInches(maxX + 100, estDefaultDPI) : null; // margin const estWIn = maxX ? dotsToInches(maxX + 100, estDefaultDPI) : null; // margin
const estHIn = maxY ? dotsToInches(maxY + 100, estDefaultDPI) : null; const estHIn = maxY ? dotsToInches(maxY + 100, estDefaultDPI) : null;
if (estWIn && estHIn) { if (estWIn && estHIn) {
if (estWIn > 6 || estHIn > 8 || estWIn > 8 || estHIn > 6) { if (estWIn > 6 || estHIn > 8 || estWIn > 8 || estHIn > 6) {
for (const p of PRINTER_OPTIONS) { for (const p of PRINTER_OPTIONS) {
sizesToTry.push({ w: 8.27, h: 11.69, printer: p, name: "A4" }); sizesToTry.push({ w: 8.27, h: 11.69, printer: p, name: "A4" });
sizesToTry.push({ w: 11.69, h: 8.27, printer: p, name: "A4_rot" }); sizesToTry.push({ w: 11.69, h: 8.27, printer: p, name: "A4_rot" });
sizesToTry.push({ w: 8.5, h: 11, printer: p, name: "Letter" }); sizesToTry.push({ w: 8.5, h: 11, printer: p, name: "Letter" });
sizesToTry.push({ w: 11, h: 8.5, printer: p, name: "Letter_rot" }); sizesToTry.push({ w: 11, h: 8.5, printer: p, name: "Letter_rot" });
} }
} }
} }
for (const p of PRINTER_OPTIONS) { for (const p of PRINTER_OPTIONS) {
for (const s of STANDARD_SIZES) { for (const s of STANDARD_SIZES) {
const exists = sizesToTry.some(x => Math.abs(x.w - s.w) < 0.01 && Math.abs(x.h - s.h) < 0.01 && x.printer.key === p.key); const exists = sizesToTry.some(x => Math.abs(x.w - s.w) < 0.01 && Math.abs(x.h - s.h) < 0.01 && x.printer.key === p.key);
if (!exists) sizesToTry.push({ w: s.w, h: s.h, printer: p, name: s.name || `${s.w}x${s.h}` }); if (!exists) sizesToTry.push({ w: s.w, h: s.h, printer: p, name: s.name || `${s.w}x${s.h}` });
} }
} }
// Duplikate entfernen & Reihenfolge behalten // Duplikate entfernen & Reihenfolge behalten
const uniqSizes = []; const uniqSizes = [];
const seen = new Set(); const seen = new Set();
for (const s of sizesToTry) { for (const s of sizesToTry) {
const key = `${s.printer.key}-${Math.round(s.w*100)}/${Math.round(s.h*100)}`; const key = `${s.printer.key}-${Math.round(s.w*100)}/${Math.round(s.h*100)}`;
if (!seen.has(key)) { seen.add(key); uniqSizes.push(s); } if (!seen.has(key)) { seen.add(key); uniqSizes.push(s); }
} }
// Versuche rendern // Versuche rendern
let renderResult = null; let renderResult = null;
for (const s of uniqSizes) { for (const s of uniqSizes) {
const wStr = fmtInches(s.w); const wStr = fmtInches(s.w);
const hStr = fmtInches(s.h); const hStr = fmtInches(s.h);
console.log(`Versuche rendern: printer=${s.printer.key} size=${wStr}x${hStr} (${s.name || ""})`); console.log(`Versuche rendern: printer=${s.printer.key} size=${wStr}x${hStr} (${s.name || ""})`);
renderResult = await tryLabelaryRender(zpl, s.printer.key, wStr, hStr); renderResult = await tryLabelaryRender(zpl, s.printer.key, wStr, hStr);
if (renderResult.ok) { console.log(`Erfolg mit ${s.printer.key} ${wStr}x${hStr}`); break; } if (renderResult.ok) { console.log(`Erfolg mit ${s.printer.key} ${wStr}x${hStr}`); break; }
else { console.log(`Fehler (${renderResult.status}) mit ${s.printer.key} ${wStr}x${hStr}`); } else { console.log(`Fehler (${renderResult.status}) mit ${s.printer.key} ${wStr}x${hStr}`); }
} }
if (!renderResult || !renderResult.ok) { if (!renderResult || !renderResult.ok) {
console.error("Alle Render-Versuche fehlgeschlagen, Abbruch."); console.error("Alle Render-Versuche fehlgeschlagen, Abbruch.");
busy = false; busy = false;
return; return;
} }
// PNG speichern (latest.png) — alte PNGs löschen // PNG speichern — neue Datei mit Timestamp (History)
const pngBuffer = renderResult.buffer; const pngBuffer = renderResult.buffer;
try { const timestamp = Date.now();
fs.readdirSync(IMG_DIR).forEach(f => { const outFile = path.join(IMG_DIR, `label-${timestamp}.png`);
const fileToDelete = path.join(IMG_DIR, f); fs.writeFileSync(outFile, pngBuffer);
try { if (fs.lstatSync(fileToDelete).isFile()) fs.unlinkSync(fileToDelete); } catch(e){} console.log("PNG erstellt:", outFile);
});
} catch(e){} // ZPL-Datei löschen (optional)
try { fs.unlinkSync(filePath); } catch(e){ console.warn("Konnte ZPL nicht löschen:", e.message); }
const outFile = path.join(IMG_DIR, "latest.png");
fs.writeFileSync(outFile, pngBuffer); // Fenster benachrichtigen (sende neuen PNG-Pfad)
console.log("PNG erstellt:", outFile); win.webContents.send("show-png", outFile);
// verarbeitete ZPL-Datei löschen (optional) } catch (err) {
try { fs.unlinkSync(filePath); } catch(e){ console.warn("Konnte ZPL nicht löschen:", e.message); } console.error("Fehler beim Rendern:", err && err.message ? err.message : err);
} finally {
// Fenster benachrichtigen busy = false;
win.webContents.send("show-png", outFile); }
}
} catch (err) {
console.error("Fehler beim Rendern:", err && err.message ? err.message : err);
} finally {
busy = false;
}
}