Erster Commit
This commit is contained in:
348
js/watcher-bridge.js
Normal file
348
js/watcher-bridge.js
Normal file
@@ -0,0 +1,348 @@
|
||||
/**
|
||||
* Watcher Bridge – Renderer-side logic for folder watcher
|
||||
* Communicates with Electron main process via window.electronAPI
|
||||
*
|
||||
* Flow:
|
||||
* 1. Main process detects new file via chokidar
|
||||
* 2. Main sends file content to renderer via 'watcher-file-detected'
|
||||
* 3. This bridge converts using the EDIBridge converters
|
||||
* 4. Calls back to main to write output file
|
||||
* 5. Calls back to main to move source file to archiv/
|
||||
*/
|
||||
window.EDIBridge = window.EDIBridge || {};
|
||||
|
||||
class WatcherBridge {
|
||||
constructor() {
|
||||
this.status = 'stopped'; // 'stopped', 'running', 'paused'
|
||||
this.log = [];
|
||||
this.maxLogEntries = 200;
|
||||
this.onStatusChange = null;
|
||||
this.onLogEntry = null;
|
||||
this.config = null;
|
||||
|
||||
this._setupListeners();
|
||||
}
|
||||
|
||||
_setupListeners() {
|
||||
if (!window.electronAPI) return;
|
||||
|
||||
window.electronAPI.onFileDetected((data) => {
|
||||
this._processDetectedFile(data);
|
||||
});
|
||||
|
||||
window.electronAPI.onWatcherError((data) => {
|
||||
this._addLog('error', data.filePath || '', data.error || 'Unbekannter Fehler');
|
||||
});
|
||||
|
||||
window.electronAPI.onWatcherReady((data) => {
|
||||
this._addLog('info', '', `Watcher bereit. Überwache: ${data.inputDir}`);
|
||||
});
|
||||
}
|
||||
|
||||
async _processDetectedFile(data) {
|
||||
const { filePath, fileName, content, outputDir } = data;
|
||||
this._addLog('info', fileName, 'Datei erkannt, starte Konvertierung...');
|
||||
|
||||
let konvertierungsmodus = 'Unbekannt';
|
||||
let quellformat = null;
|
||||
let zielformat = null;
|
||||
let convertedContent = null;
|
||||
let pError = null;
|
||||
let outFileName = null;
|
||||
|
||||
try {
|
||||
// Try to use a global config if this.config is not set (e.g. from app.js)
|
||||
if (!this.config && window.app) {
|
||||
this.config = window.app.config;
|
||||
}
|
||||
|
||||
const result = this._convertContent(content, fileName);
|
||||
|
||||
if (result.error) {
|
||||
this._addLog('error', fileName, result.error);
|
||||
pError = result.error;
|
||||
} else {
|
||||
konvertierungsmodus = result.type;
|
||||
quellformat = result.format === 'vda' ? 'EDIFACT DELFOR' : 'VDA 4913';
|
||||
zielformat = result.targetFormat || (result.format === 'vda' ? 'VDA 4905' : (result.format === 'xml' ? 'IFM DELVRY03' : 'Bosch DESADV'));
|
||||
convertedContent = result.output;
|
||||
|
||||
// Build output filename
|
||||
const baseName = fileName.replace(/\.[^.]+$/, '');
|
||||
const outExt = result.format === 'vda' ? '.vda' : (result.format === 'xml' ? '.xml' : '.edi');
|
||||
outFileName = baseName + '_converted' + outExt;
|
||||
const outPath = outputDir + '\\' + outFileName;
|
||||
|
||||
// Write converted file
|
||||
const writeResult = await window.electronAPI.writeFile(outPath, result.output);
|
||||
|
||||
if (writeResult.error) {
|
||||
this._addLog('error', fileName, `Schreibfehler: ${writeResult.error}`);
|
||||
pError = writeResult.error;
|
||||
} else {
|
||||
this._addLog('success', fileName, `Konvertiert → ${outFileName} (${result.type})`);
|
||||
|
||||
// Move source file to archiv
|
||||
const archiveResult = await window.electronAPI.moveToArchive(filePath);
|
||||
|
||||
if (archiveResult.error) {
|
||||
this._addLog('error', fileName, `Archiv-Fehler: ${archiveResult.error}`);
|
||||
} else {
|
||||
this._addLog('info', fileName, 'Quelldatei nach archiv/ verschoben.');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this._addLog('error', fileName, `Fehler: ${e.message}`);
|
||||
pError = e.message;
|
||||
}
|
||||
|
||||
// Log to database
|
||||
if (window.app && typeof window.app._logConversion === 'function') {
|
||||
await window.app._logConversion({
|
||||
dateiname: fileName,
|
||||
ausgangsdateiname: outFileName,
|
||||
quellformat: quellformat,
|
||||
zielformat: zielformat,
|
||||
konvertierungsmodus: konvertierungsmodus,
|
||||
eingang_daten: content,
|
||||
ausgang_daten: convertedContent,
|
||||
status: pError ? 'FEHLERHAFT' : 'ERFOLGREICH',
|
||||
fehlermeldung: pError,
|
||||
quelle: 'WATCHER' // Auto tag
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_convertContent(content, fileName) {
|
||||
const trimmed = content.trim();
|
||||
const upper = trimmed.toUpperCase();
|
||||
|
||||
// Detect file type and choose converter
|
||||
if (upper.startsWith('UNA') || upper.startsWith('UNB')) {
|
||||
// EDIFACT inbound → VDA 4905
|
||||
return this._convertInbound(content, upper);
|
||||
} else if (/^[0-9]{3}/.test(trimmed) || /^[57][0-9]{2}/.test(trimmed)) {
|
||||
// VDA record (starts with 3-digit record type, e.g. 511, 711) → EDIFACT outbound
|
||||
return this._convertOutbound(content);
|
||||
} else {
|
||||
return { error: `Dateiformat nicht erkannt (weder EDIFACT noch VDA). Erste Zeichen: "${trimmed.substring(0, 20)}..."` };
|
||||
}
|
||||
}
|
||||
|
||||
_convertInbound(content, upper) {
|
||||
try {
|
||||
const ConfigParser = window.EDIBridge.ConfigParser;
|
||||
const DelforParser = window.EDIBridge.DelforParser;
|
||||
let preferredMode = null;
|
||||
|
||||
if (ConfigParser && DelforParser) {
|
||||
const customerId = DelforParser.getCustomerId(content);
|
||||
console.log("Watcher detected Inbound Customer ID:", customerId);
|
||||
if (customerId) {
|
||||
preferredMode = ConfigParser.lookupCustomerMode(this.config, customerId);
|
||||
console.log("Watcher mapped Inbound mode for ID", customerId, ":", preferredMode);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Prioritize mapped mode
|
||||
if (preferredMode === 'inbound-ifm') {
|
||||
const converter = window.EDIBridge.DelforToVDA4905Converter;
|
||||
if (converter) {
|
||||
const result = converter.convert(content);
|
||||
return { output: result, type: 'IFM DELFOR → VDA 4905', format: 'vda' };
|
||||
}
|
||||
} else if (preferredMode === 'inbound-bosch') {
|
||||
const parser = window.EDIBridge.DelforParser;
|
||||
const generator = window.EDIBridge.VDA4905Generator;
|
||||
if (parser && generator) {
|
||||
const parsed = parser.parse(content);
|
||||
const vda4905 = generator.generate(parsed);
|
||||
return { output: vda4905, type: 'DELFOR → VDA 4905', format: 'vda' };
|
||||
}
|
||||
} else if (preferredMode === 'inbound-invrpt') {
|
||||
const converter = window.EDIBridge.InvrptToVDA4913;
|
||||
if (converter) {
|
||||
const vda4913 = converter.convert(content);
|
||||
return { output: vda4913, type: 'INVRPT → VDA 4913', format: 'vda' };
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback to hardcoded logic if no mapping
|
||||
if (upper.includes('INVRPT')) {
|
||||
const converter = window.EDIBridge.InvrptToVDA4913;
|
||||
if (converter) {
|
||||
const vda4913 = converter.convert(content);
|
||||
return { output: vda4913, type: 'INVRPT → VDA 4913', format: 'vda' };
|
||||
}
|
||||
}
|
||||
|
||||
if (upper.includes('DELFOR') && upper.includes('D:04A')) {
|
||||
const converter = window.EDIBridge.DelforToVDA4905Converter;
|
||||
if (converter) {
|
||||
const result = converter.convert(content);
|
||||
return { output: result, type: 'IFM DELFOR D:04A → VDA 4905', format: 'vda' };
|
||||
}
|
||||
}
|
||||
|
||||
const parser = window.EDIBridge.DelforParser;
|
||||
const generator = window.EDIBridge.VDA4905Generator;
|
||||
if (parser && generator) {
|
||||
const parsed = parser.parse(content);
|
||||
const vda4905 = generator.generate(parsed);
|
||||
return { output: vda4905, type: 'DELFOR → VDA 4905', format: 'vda' };
|
||||
}
|
||||
|
||||
return { error: 'Passendes Inbound-Modul nicht geladen oder Typ unbekannt.' };
|
||||
} catch (e) {
|
||||
return { error: `Inbound-Konvertierung fehlgeschlagen: ${e.message}` };
|
||||
}
|
||||
}
|
||||
|
||||
_convertOutbound(content) {
|
||||
try {
|
||||
const VDAParser = window.EDIBridge.VDAParser;
|
||||
if (!VDAParser) return { error: 'VDA Parser nicht geladen.' };
|
||||
|
||||
const vda = VDAParser.parse(content);
|
||||
if (!vda.interchanges || vda.interchanges.length === 0) {
|
||||
return { error: 'Keine gültigen VDA 4913 Daten gefunden.' };
|
||||
}
|
||||
|
||||
// Check for customer mapping in config
|
||||
const ConfigParser = window.EDIBridge.ConfigParser;
|
||||
const customerId = VDAParser.getCustomerId(content);
|
||||
console.log("Watcher detected Customer ID:", customerId);
|
||||
let preferredMode = null;
|
||||
|
||||
if (ConfigParser && customerId) {
|
||||
preferredMode = ConfigParser.lookupCustomerMode(this.config, customerId);
|
||||
console.log("Watcher mapped mode for ID", customerId, ":", preferredMode);
|
||||
}
|
||||
|
||||
if (preferredMode === 'outbound-ifm') {
|
||||
const delvry = window.EDIBridge.VDA4913ToDELVRY03;
|
||||
if (delvry) {
|
||||
const idocs = delvry.convert(vda, this.config || {});
|
||||
const output = delvry.toXML(idocs);
|
||||
return { output, type: 'VDA 4913 → IFM DELVRY03', format: 'xml' };
|
||||
}
|
||||
} else if (preferredMode === 'outbound-zf') {
|
||||
const desadvZf = window.EDIBridge.VDA4913ToDESADVZF;
|
||||
if (desadvZf) {
|
||||
const output = desadvZf.convert(content, {}, { linWeights: {}, pacWeights: {}, pacDims: {} }, this.config);
|
||||
return { output, type: 'VDA 4913 → ZF DESADV', format: 'zf-edi', targetFormat: 'ZF DESADV' };
|
||||
}
|
||||
} else if (preferredMode === 'outbound-bosch') {
|
||||
const desadv = window.EDIBridge.VDA4913ToDESADV;
|
||||
if (desadv) {
|
||||
const output = desadv.convert(content, {}, { linWeights: {}, pacWeights: {}, pacDims: {} }, this.config);
|
||||
return { output, type: 'VDA 4913 → Bosch DESADV', format: 'edi' };
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to original logic if no mapping or mapped converter missing
|
||||
// Try IFM DELVRY03 first
|
||||
const delvry = window.EDIBridge.VDA4913ToDELVRY03;
|
||||
if (delvry) {
|
||||
const idocs = delvry.convert(vda, this.config || {});
|
||||
const output = delvry.toXML(idocs);
|
||||
return { output, type: 'VDA 4913 → IFM DELVRY03', format: 'xml' };
|
||||
}
|
||||
|
||||
// Fallback to Bosch DESADV
|
||||
const desadv = window.EDIBridge.VDA4913ToDESADV;
|
||||
if (desadv) {
|
||||
const output = desadv.convert(content, {}, { linWeights: {}, pacWeights: {}, pacDims: {} }, this.config);
|
||||
return { output, type: 'VDA 4913 → Bosch DESADV', format: 'edi' };
|
||||
}
|
||||
|
||||
return { error: 'Kein Outbound-Converter geladen.' };
|
||||
} catch (e) {
|
||||
return { error: `Outbound-Konvertierung fehlgeschlagen: ${e.message}` };
|
||||
}
|
||||
}
|
||||
|
||||
_addLog(level, fileName, message) {
|
||||
const entry = {
|
||||
time: new Date().toLocaleTimeString('de-DE'),
|
||||
level,
|
||||
fileName,
|
||||
message
|
||||
};
|
||||
this.log.unshift(entry);
|
||||
if (this.log.length > this.maxLogEntries) this.log.pop();
|
||||
if (this.onLogEntry) this.onLogEntry(entry);
|
||||
}
|
||||
|
||||
async start(inputDir, outputDir) {
|
||||
if (!window.electronAPI) {
|
||||
return { error: 'Electron API nicht verfügbar. App läuft im Browser-Modus.' };
|
||||
}
|
||||
const result = await window.electronAPI.startWatcher({ inputDir, outputDir });
|
||||
if (result.success) {
|
||||
this.status = 'running';
|
||||
this._addLog('info', '', `Watcher gestartet. Überwache: ${inputDir}`);
|
||||
if (this.onStatusChange) this.onStatusChange(this.status);
|
||||
} else if (result.error) {
|
||||
this._addLog('error', '', result.error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async stop() {
|
||||
if (!window.electronAPI) return;
|
||||
const result = await window.electronAPI.stopWatcher();
|
||||
if (result.success) {
|
||||
this.status = 'stopped';
|
||||
this._addLog('info', '', 'Watcher gestoppt.');
|
||||
if (this.onStatusChange) this.onStatusChange(this.status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async pause() {
|
||||
if (!window.electronAPI) return;
|
||||
const result = await window.electronAPI.pauseWatcher();
|
||||
if (result.success) {
|
||||
this.status = 'paused';
|
||||
this._addLog('info', '', 'Watcher pausiert.');
|
||||
if (this.onStatusChange) this.onStatusChange(this.status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async resume() {
|
||||
if (!window.electronAPI) return;
|
||||
const result = await window.electronAPI.resumeWatcher();
|
||||
if (result.success) {
|
||||
this.status = 'running';
|
||||
this._addLog('info', '', 'Watcher fortgesetzt.');
|
||||
if (this.onStatusChange) this.onStatusChange(this.status);
|
||||
} else if (result.error) {
|
||||
this._addLog('error', '', result.error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
if (!window.electronAPI) return { inputDir: '', outputDir: '', mode: 'auto' };
|
||||
return await window.electronAPI.loadSettings();
|
||||
}
|
||||
|
||||
async saveSettings(settings) {
|
||||
if (!window.electronAPI) return;
|
||||
return await window.electronAPI.saveSettings(settings);
|
||||
}
|
||||
|
||||
async selectFolder() {
|
||||
if (!window.electronAPI) return null;
|
||||
return await window.electronAPI.selectFolder();
|
||||
}
|
||||
|
||||
isElectron() {
|
||||
return !!window.electronAPI;
|
||||
}
|
||||
}
|
||||
|
||||
window.EDIBridge.WatcherBridge = WatcherBridge;
|
||||
Reference in New Issue
Block a user