/** * 📅 IHK NETZPLAN-TRAINER (DIN 69900) 📅 * ===================================== * Interaktiver Trainer zur Berechnung von: * - Vorwärtsrechnung: FAZ (Frühester Anfangszeitpunkt) & FEZ (Frühester Endzeitpunkt) * - Rückwärtsrechnung: SAZ (Spätester Anfangszeitpunkt) & SEZ (Spätester Endzeitpunkt) * - Pufferzeiten: GP (Gesamtpuffer = SAZ - FAZ) & FP (Freier Puffer = min(FAZ_Nachfolger) - FEZ) * - Kritischer Pfad (Vorgänge mit GP = 0) */ class NetzplanTrainer { constructor(containerId) { this.container = document.getElementById(containerId); this.scenarioIndex = 0; this.scenarios = [ { title: "Szenario 1: Rollout eines neuen Firmennetzwerks (PrintTop GmbH)", nodes: [ { id: "A", name: "Anforderungsanalyse", d: 3, preds: [], faz: 0, fez: 3, saz: 0, sez: 3, gp: 0, fp: 0 }, { id: "B", name: "Hardware-Beschaffung", d: 5, preds: ["A"], faz: 3, fez: 8, saz: 3, sez: 8, gp: 0, fp: 0 }, { id: "C", name: "Kabelverlegung (Cat 7)", d: 4, preds: ["A"], faz: 3, fez: 7, saz: 4, sez: 8, gp: 1, fp: 1 }, { id: "D", name: "Switch-/Router-Konfiguration", d: 4, preds: ["B"], faz: 8, fez: 12, saz: 8, sez: 12, gp: 0, fp: 0 }, { id: "E", name: "Patchfeld & Dosen auflegen", d: 3, preds: ["C"], faz: 7, fez: 10, saz: 9, sez: 12, gp: 2, fp: 2 }, { id: "F", name: "End-to-End Funktionstest", d: 2, preds: ["D", "E"], faz: 12, fez: 14, saz: 12, sez: 14, gp: 0, fp: 0 }, { id: "G", name: "Übergabe & Abnahme", d: 1, preds: ["F"], faz: 14, fez: 15, saz: 14, sez: 15, gp: 0, fp: 0 } ], criticalPath: ["A", "B", "D", "F", "G"] }, { title: "Szenario 2: Migration auf Microsoft Active Directory", nodes: [ { id: "A", name: "Ist-Aufnahme AD DS", d: 2, preds: [], faz: 0, fez: 2, saz: 0, sez: 2, gp: 0, fp: 0 }, { id: "B", name: "OU-Struktur & GPO-Design", d: 4, preds: ["A"], faz: 2, fez: 6, saz: 2, sez: 6, gp: 0, fp: 0 }, { id: "C", name: "Bereitstellung DC-Hardware", d: 3, preds: ["A"], faz: 2, fez: 5, saz: 3, sez: 6, gp: 1, fp: 1 }, { id: "D", name: "Benutzerdaten CSV-Export", d: 2, preds: ["A"], faz: 2, fez: 4, saz: 6, sez: 8, gp: 4, fp: 4 }, { id: "E", name: "Installation Domain Controller", d: 2, preds: ["B", "C"], faz: 6, fez: 8, saz: 6, sez: 8, gp: 0, fp: 0 }, { id: "F", name: "PowerShell Massenimport", d: 3, preds: ["D", "E"], faz: 8, fez: 11, saz: 8, sez: 11, gp: 0, fp: 0 }, { id: "G", name: "Client-Domain-Join & Tests", d: 2, preds: ["F"], faz: 11, fez: 13, saz: 11, sez: 13, gp: 0, fp: 0 } ], criticalPath: ["A", "B", "E", "F", "G"] } ]; this.init(); } init() { if (!this.container) return; this.render(); } render() { const sc = this.scenarios[this.scenarioIndex]; this.container.innerHTML = `

📊 DIN 69900 Netzplantechnik Trainer

${sc.title}

Aufgabe: Berechnen Sie für jeden Vorgang die Zeitpunkte der Vorwärtsrechnung (FAZ, FEZ), der Rückwärtsrechnung (SAZ, SEZ) sowie den Gesamtpuffer (GP) und Freien Puffer (FP). Identifizieren Sie anschließend alle Vorgänge auf dem Kritischen Pfad.

ℹ️ FEZ = FAZ + D ℹ️ FAZ = max(FEZ aller Vorgänger) ℹ️ SAZ = SEZ - D ℹ️ SEZ = min(SAZ aller Nachfolger) ℹ️ GP = SAZ - FAZ = SEZ - FEZ ℹ️ FP = min(FAZ Nachfolger) - FEZ
${sc.nodes.map(n => ` `).join("")}
ID Vorgang Dauer (D) Vorgänger FAZ FEZ SAZ SEZ GP FP Kritisch?
${n.id} ${n.name} ${n.d} ${n.preds.length > 0 ? n.preds.join(", ") : "-"}
`; this.attachEvents(); } attachEvents() { document.getElementById("np-check-btn").addEventListener("click", () => this.checkAnswers()); document.getElementById("np-solve-btn").addEventListener("click", () => this.solve()); document.getElementById("np-reset-btn").addEventListener("click", () => this.render()); document.getElementById("np-prev-scenario").addEventListener("click", () => { this.scenarioIndex = 0; this.render(); }); document.getElementById("np-next-scenario").addEventListener("click", () => { this.scenarioIndex = 1; this.render(); }); } checkAnswers() { const sc = this.scenarios[this.scenarioIndex]; let correctCount = 0; let totalFields = sc.nodes.length * 7; const errors = []; sc.nodes.forEach(n => { const row = document.getElementById(`row-node-${n.id}`); const valFaz = parseInt(document.getElementById(`in-faz-${n.id}`).value, 10); const valFez = parseInt(document.getElementById(`in-fez-${n.id}`).value, 10); const valSaz = parseInt(document.getElementById(`in-saz-${n.id}`).value, 10); const valSez = parseInt(document.getElementById(`in-sez-${n.id}`).value, 10); const valGp = parseInt(document.getElementById(`in-gp-${n.id}`).value, 10); const valFp = parseInt(document.getElementById(`in-fp-${n.id}`).value, 10); const valCrit = document.getElementById(`in-crit-${n.id}`).checked; const isActuallyCrit = sc.criticalPath.includes(n.id); let nodeOk = true; const checkField = (elemId, actual, expected, name) => { const el = document.getElementById(elemId); if (actual === expected) { el.style.border = "1px solid #22c55e"; el.style.background = "#064e3b"; correctCount++; } else { el.style.border = "1px solid #ef4444"; el.style.background = "#450a0a"; nodeOk = false; errors.push(`Vorgang ${n.id} (${n.name}): ${name} sollte ${expected} sein (Eingabe: ${isNaN(actual) ? 'leer' : actual}).`); } }; checkField(`in-faz-${n.id}`, valFaz, n.faz, "FAZ"); checkField(`in-fez-${n.id}`, valFez, n.fez, "FEZ"); checkField(`in-saz-${n.id}`, valSaz, n.saz, "SAZ"); checkField(`in-sez-${n.id}`, valSez, n.sez, "SEZ"); checkField(`in-gp-${n.id}`, valGp, n.gp, "GP"); checkField(`in-fp-${n.id}`, valFp, n.fp, "FP"); const critEl = document.getElementById(`in-crit-${n.id}`); if (valCrit === isActuallyCrit) { critEl.parentElement.style.background = "#064e3b"; correctCount++; } else { critEl.parentElement.style.background = "#450a0a"; nodeOk = false; errors.push(`Vorgang ${n.id} liegt ${isActuallyCrit ? 'AUF' : 'NICHT auf'} dem kritischen Pfad.`); } if (nodeOk) { row.style.background = "rgba(34, 197, 94, 0.1)"; } else { row.style.background = "rgba(239, 68, 68, 0.1)"; } }); const box = document.getElementById("np-feedback-box"); box.style.display = "block"; const percent = Math.round((correctCount / totalFields) * 100); if (errors.length === 0) { box.innerHTML = `

🎉 Perfekt! 100% Richtige Netzplanberechnung (${correctCount}/${totalFields} Felder)

Gesamtdauer des Projekts: ${sc.nodes[sc.nodes.length - 1].fez} Zeiteinheiten.
Der kritische Pfad verläuft über: ${sc.criticalPath.join(" → ")}. Alle Vorgänge hier haben Gesamtpuffer GP = 0.

`; } else { box.innerHTML = `

⚠️ ${errors.length} Fehler gefunden (${percent}% korrekt)

`; } } solve() { const sc = this.scenarios[this.scenarioIndex]; sc.nodes.forEach(n => { document.getElementById(`in-faz-${n.id}`).value = n.faz; document.getElementById(`in-fez-${n.id}`).value = n.fez; document.getElementById(`in-saz-${n.id}`).value = n.saz; document.getElementById(`in-sez-${n.id}`).value = n.sez; document.getElementById(`in-gp-${n.id}`).value = n.gp; document.getElementById(`in-fp-${n.id}`).value = n.fp; document.getElementById(`in-crit-${n.id}`).checked = sc.criticalPath.includes(n.id); }); this.checkAnswers(); } } document.addEventListener("DOMContentLoaded", () => { if (document.getElementById("netzplan-trainer-container")) { window.netzplanTrainer = new NetzplanTrainer("netzplan-trainer-container"); } });