41 KiB
Aktuelle Systemarchitektur
Status und unterstützter Pfad
Der unterstützte Editor ist Circuit-First:
Project → DistributionBoard → CircuitList → CircuitSection → Circuit → CircuitDeviceRow
Ein Stromkreis ist nicht dasselbe wie eine Gerätezeile. BMK, Schutz- und Kabeldaten gehören zum Stromkreis; Last-, Raum- und Kategoriedaten gehören zur Gerätezeile.
Die frühere Consumer-Oberfläche, ihre API, Tabellen und Upgrade-Werkzeuge sind entfernt. Neue Funktionen bauen ausschließlich auf dem Circuit-First-Modell auf.
Laufzeit
Browser :3001
│
▼
Next.js App Router ── /api/* Rewrite ──▶ Express API :3000
│
▼
Repository / Transaktion
│
▼
data/leistungsbilanz.db (SQLite)
Im Docker-Entwicklungssetup laufen Frontend und API in getrennten Containern. Das
Frontend leitet /api/* über API_INTERNAL_URL an die API weiter. Die Datenbank
liegt über einen Host-Mount außerhalb des Containers.
Wichtige Einstiegspunkte
src/app/projects/page.tsx– Projektliste und globale Gerätebibliotheksrc/app/projects/[projectId]/page.tsx– Projektstammdaten, Verteilungen, Räume und Projektgerätesrc/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx– unterstützte Editorroutesrc/frontend/components/circuit-tree-editor.tsx– Editorzustand, Befehlsausführung, Drag-and-drop und der schrittweise Historien-Cutoversrc/frontend/components/circuit-grid-*.ts– reine Grid-Projektion, Zellbesitz, Einfügen und Sicherheitsregelnsrc/frontend/utils/api.ts– typisierte Frontend-API-Aufrufesrc/server/index.tsundsrc/server/routes/– API-Kompositionsrc/domain/services/– fachliche Command- und Synchronisierungsregelnsrc/server/composition/– Verdrahtung fachlicher Services mit konkreten SQLite-Repositoriessrc/db/repositories/– Abfragen, Persistenzmapper und Transaktionsadaptersrc/db/schema/undsrc/db/migrations/– SQLite-Schema und Migrationen
Daten- und Befehlsfluss
- Das Grid projiziert den geladenen Circuit-Tree in sichtbare Zeilen.
- Eine Benutzeraktion wird im Frontend validiert und als API-Befehl gesendet.
- Controller validieren Requestdaten mit Zod.
- Domain-Services prüfen fachliche Regeln wie BMK-Eindeutigkeit, Abschnittszuordnung und Reserveverhalten.
- Repositories schreiben Daten. Kritische Mehrfachschreibvorgänge besitzen explizite SQLite-Transaktionsadapter mit Commit-/Rollback-Integrationstests.
- Das Frontend lädt den Circuit-Tree neu und stellt Auswahl beziehungsweise Viewport soweit möglich wieder her.
Der Editor besitzt keinen sitzungslokalen Undo-/Redo-Stapel mehr. Beim initialen
Laden und nach jedem Tree-Reload liest er den persistenten History-Status und
gleicht dessen Revision mit currentRevision des Trees ab. Das Datenmodell
besitzt einen projektbezogenen Revisionszähler sowie getrennte Revision-/Change-Set-
Tabellen. Eine direkt getestete Persistence-Funktion schreibt diese
Historienmetadaten innerhalb der zentralen Command-Transaktion optimistisch und
atomar fort. Vorwärts- und Rückwärtskommandos besitzen einen
versionierten, JSON-sicheren Umschlag; Typ und Payload können dadurch nach
einem Neustart verlustfrei rekonstruiert werden. Alle aktuell unterstützten
Circuit-, Gerätezeilen-, Projektgeräte-, Projektstruktur- und
Projekteinstellungsänderungen
verwenden typisierte Command-Stores, die Fachänderung, automatisch erzeugtes
inverses Kommando, Revision und Historienstapel gemeinsam committen
beziehungsweise zurückrollen.
Gerätezeilen-Kommandos bewahren dabei auch lokale ProjectDevice-Overrides und
prüfen Projektzugehörigkeit von Verknüpfungen und Räumen. Projektweite,
persistente Undo-/Redo-Stacks verwalten die zulässige LIFO-Reihenfolge und
verwerfen den Redo-Zweig bei einem neuen Benutzerkommando. Ihr Status ist über
GET /api/projects/:projectId/history lesbar. Die unveränderliche,
absteigend paginierte Revisions-Timeline ist ohne Befehls-Payloads über
GET /api/projects/:projectId/history/revisions lesbar. Ein zentraler
Dispatcher führt die nachfolgend beschriebenen typisierten Kommandos über
öffentliche Command-, Undo- und Redo-Endpunkte aus.
Alle unterstützten Runtime-Project-Command-Stores einschließlich der
vollständigen Snapshot-Wiederherstellung verwenden dabei
project-command-transaction.persistence.ts als gemeinsame äußere
Transaktionsgrenze. Sie führt Fachänderung, Revisions-Append und
History-Transition in fester Reihenfolge innerhalb derselben SQLite-Transaktion
aus. Für CircuitDeviceRow-Feldänderungen übernimmt eine typisierte Variante den
während der Fachänderung um Override-Metadaten ergänzten Forward-Command. Die
fachlichen Validierungs-, Forward- und Inversenregeln bleiben im jeweiligen
Store.
Die Low-Level-Funktion appendProjectRevision bleibt ein internes Detail dieser
Persistenzgrenze und wird direkt mit einer realen SQLite-Transaktion getestet.
Ein eigenständiges Runtime-Revisions-Repository existiert nicht.
ProjectDeviceSyncService und CircuitNumberingService kennen nur schmale,
fachlich benannte Reader-Interfaces. Ihre SQLite-Repositories werden
ausschließlich in src/server/composition/ erzeugt und injiziert.
Auch die allgemeinen Projekt-, Geräte-, Raum-, Geschoss- und Circuit-
Repositories verlangen einen expliziten AppDatabase-Kontext. Ihre
Anwendungsinstanzen werden zentral in
src/server/composition/application-repositories.ts erzeugt; Controller
importieren weder den globalen SQLite-Client noch konkrete Repository-Klassen.
circuit-device-row.insert und circuit-device-row.delete sind atomare
Strukturkommandos. Beim Löschen wird die vollständige Zeile im inversen
Kommando gesichert, sodass Undo dieselbe UUID und alle Fachwerte wiederherstellt.
circuit.insert und circuit.delete behandeln einen
Stromkreis mit null, einer oder mehreren Gerätezeilen als vollständigen Block.
Undo bewahrt dabei sämtliche Circuit-/Row-UUIDs und ändert keine
Betriebsmittelkennzeichen. Bestehende Circuit- und Gerätezeilen-Zelländerungen
sowie eigenständiges Einfügen und Löschen verwendet das Grid bereits über die
öffentliche Command-Grenze. Neue Circuits und Gerätezeilen erhalten ihre stabile
UUID vor dem Command; Löschen und Undo bewahren diese Identität. Der Tree liefert
dazu currentRevision; Undo/Redo für diese Aktionen läuft über die projektweite
Serverhistorie. Direkte Feld-PATCH-, Struktur-POST-, Move-, Circuit- und
Gerätezeilen-DELETE-Endpunkte sind entfernt. Gerätezeilen-Moves,
Stromkreis-Reorders und die explizite Neunummerierung im Grid verwenden die
persistenten Kommandos. Die Toolbar leitet ihre Undo-/Redo-Verfügbarkeit direkt
aus den serverseitigen Stack-Tiefen ab, sodass ein Reload die Bedienbarkeit
nicht unterbricht.
Benannte logische Projektstände werden in project_snapshots getrennt von
Datenbank-Backups gespeichert. POST /api/projects/:projectId/snapshots
erzeugt bei passender erwarteter Revision transaktional einen vollständigen,
schema-versionierten Projektzustand mit SHA-256-Prüfwert. Enthalten sind
Projekteinstellungen, Verteiler, Stromkreislisten, Bereiche, Stromkreise und
Gerätezeilen sowie Projektgeräte, Geschosse und Räume. Globale Geräte sind nicht
Teil des Projekt-Snapshots.
Create/List verändern weder Projektrevision noch Undo-/Redo-Stapel.
kind unterscheidet benannte und automatische Stände. Die zentrale
Revisionspersistenz erzeugt nach jeweils 25 weiteren Projektänderungen
transaktional einen automatischen Stand. Pro Projekt bleiben die neuesten 12
automatischen Stände erhalten; ältere automatische Stände werden in derselben
Transaktion entfernt. Benannte Stände und die unveränderliche Revisionshistorie
sind von dieser Aufbewahrung ausdrücklich ausgeschlossen.
POST /api/projects/:projectId/snapshots/:snapshotId/restore prüft Payload,
Prüfsumme, erwartete Revision und den unmittelbar zuvor gelesenen
Projektzustand. Der Restore ersetzt alle unterstützten Projektdaten in einer
Transaktion und schreibt dabei eine neue Revision mit Quelle restore sowie
ein vollständiges inverses Kommando. Undo und Redo können deshalb auch einen
Restore nach einem Neustart exakt zurücknehmen oder wiederholen.
Die Projektseite bindet diese APIs in einem einklappbaren Bereich
„Versionen und Sicherungspunkte“ ein. Dort können Benutzer Sicherungspunkte
benennen, jeden aufgeführten benannten oder automatischen Stand nach
expliziter Bestätigung wiederherstellen und die paginierte Revisions-Timeline
mit deutschen Quellen- und Änderungsbezeichnungen lesen. Die Snapshot-Liste
ordnet jede positive sourceRevision serverseitig ihren unveränderlichen
Revisionsmetadaten zu. Dadurch zeigt auch ein älterer automatischer Stand die
auslösende Änderung, ohne eine zweite frei formulierte Beschreibung zu
speichern oder von den zuletzt paginiert geladenen Timeline-Einträgen
abzuhängen. Revision null wird als Projektstart dargestellt.
Projektweites Rückgängig/Wiederholen bleibt im Kopf dieses Bereichs auch im
eingeklappten Zustand erreichbar. Die Verfügbarkeit stammt direkt aus den
persistierten Server-Stacks; nach einer Historienaktion oder einem Restore lädt
die Seite sämtliche Projektdaten neu.
circuit-device-row.move verschiebt oder sortiert eine oder mehrere Zeilen
zwischen vorhandenen Stromkreisen derselben Liste. Erwartete und neue
Stromkreis-/Sortierpositionen machen Forward und Inverse deterministisch;
Reservewerte aller beteiligten Stromkreise werden atomar neu abgeleitet. Das
Kompositkommando circuit-device-row.move-with-new-circuit bildet auch das
Verschieben auf einen freien Platz ab: Ein Zielstromkreis mit stabiler UUID und
BMK wird zusammen mit allen Zeilenbewegungen erzeugt. Undo stellt die exakten
Quellpositionen wieder her und löscht den erzeugten Stromkreis nur, wenn dessen
Felder und vollständiger Zeilenbestand unverändert sind.
Der Editor erzeugt die vollständigen Move-Zuweisungen aus dem geladenen Tree,
vergibt für neue Ziele vor dem Kommando eine stabile UUID und führt das
Toolbar-Undo/Redo über die projektweite Historie aus.
Die Projektgerätepalette belegt keine permanente Layoutspalte mehr. Sie wird
über die Editor-Toolbar als überlagernder Drawer geöffnet, während das
Stromkreis-Grid standardmäßig die gesamte verfügbare Breite nutzt. Auswahl,
Schnelleinfügen und die vorhandenen Drag-and-drop-Payloads bleiben im Drawer
unverändert verfügbar; während eines aktiven Projektgeräte-Drags kann er nicht
geschlossen werden. Der Drawer liegt am rechten Fensterrand und verdeckt damit
nicht die führenden BMK- und Anzeigenamenspalten. Das Grid zeigt Geräte- und
Stromkreisleistungen in einer gemeinsamen Spalte Gesamtsumme: Gerätezeilen
verwenden rowTotalPower, Stromkreis-Sammelzeilen circuitTotalPower.
Zahlen werden ausschließlich für die Anzeige deutsch und begrenzt formatiert;
gespeicherte Werte und Bearbeitungsentwürfe behalten ihre volle Genauigkeit.
Jeder Abschnitt liefert und zeigt seine aufsummierte Stromkreisleistung. Die
Stromkreisliste zeigt außerdem die ungefilterte Gesamtleistung des Verteilers,
den am Verteiler gespeicherten Gleichzeitigkeitsfaktor und die daraus
abgeleitete Gesamtleistung unter Berücksichtigung dieses Faktors. Sortierung
und Filter verändern diese fachlichen Summen nicht.
Reihenfolge und Sichtbarkeit der Grid-Spalten sind reine UI-Präferenzen. Sie
werden im Browser unter einem projektspezifischen Schlüssel gespeichert und
deshalb beim Wechsel zwischen Verteilern desselben Projekts wiederverwendet,
ohne Projektrevisionen oder fachliche Snapshots zu erzeugen.
circuit.reorder-section speichert die erwartete und neue Sortierposition
jedes Stromkreises eines vollständigen Abschnitts. Forward, Undo und Redo
ändern ausschließlich sortOrder; Stromkreisblöcke, Gerätezeilen und BMKs
bleiben unverändert. circuit.renumber-section bildet die getrennte,
ausdrücklich ausgelöste Neunummerierung ab. Es speichert alle erwarteten und
neuen BMKs des Abschnitts, löst Tauschkollisionen über temporäre Werte und
ändert weder Sortierung noch Gerätezeilen.
Die Bereichsaktion erzeugt die vollständigen erwarteten und neuen BMKs aus
Präfix und aktueller Stromkreisreihenfolge und verwendet das persistente
Kommando für Toolbar-Undo/Redo. Die früheren direkten Renumber- und
Identifier-Restore-Endpunkte sowie ihr separater Transaktionsadapter sind
entfernt.
Drag-and-drop verwendet circuit.reorder-section. Die explizite Übernahme
einer sortierten Ansicht verwendet circuit.reorder-sections, damit alle
betroffenen Bereiche in einer Transaktion und als ein Undo-Schritt gespeichert
werden. Direkte Reorder-Endpunkte existieren nicht mehr.
project-device.sync-rows persistiert Synchronisierung, Trennen und erneutes
Verknüpfen als atomaren Mehrzeilen-Command. Jede betroffene Zeile enthält den
vollständigen erwarteten und neuen Stand aller synchronisierbaren Felder,
einschließlich ProjectDevice-Verknüpfung und lokaler Override-Metadaten. Damit
werden stille Überschreibungen veralteter Zeilen verhindert und Undo/Redo stellt
exakt die vorherigen lokalen Werte wieder her. Die Synchronisieren- und
Trennen-Endpunkte sowie die Projektseite verwenden diesen Command mit
optimistischer Revisionsprüfung. Rückgängig/Wiederholen erfolgt dort
einheitlich über die persistente projektweite Historie; der frühere
sitzungslokale Spezial-Undo und separate direkte Restore-/Reconnect-Schreibwege
existieren nicht mehr.
project-device.update versioniert Änderungen an den kanonischen
Projektgerätefeldern unabhängig davon. Der Store erzeugt die Inverse aus dem
gespeicherten Gerät und schreibt Geräteänderung, Revision und Historienstapel
atomar. Verknüpfte Stromkreiszeilen werden dabei bewusst nicht automatisch
synchronisiert. Der ProjectDevice-PUT-Endpunkt und die Projektseite verwenden
diesen Command mit optimistischer Revisionsprüfung.
project-device.insert und project-device.delete versionieren außerdem den
Lebenszyklus eines Projektgeräts mit stabiler UUID. Beim Löschen speichert das
inverse Insert den vollständigen Gerätestand sowie vollständige, nach dem
Löschen erwartete Snapshots aller zuvor verknüpften Gerätezeilen. Undo setzt
die Links nur zurück, wenn diese Zeilen weiterhin zum Projekt gehören,
unverknüpft und vollständig unverändert sind. Gerät, Linkänderungen, Revision
und Historienstapel teilen dieselbe Transaktion. Create, Import aus der globalen
Gerätebibliothek und Delete laufen über dieselbe Command-Grenze; ihre Antworten
liefern Gerät und aktualisierten Historienstand an die Projektseite zurück.
project.update-settings versioniert Projektname, interne und externe
Projektnummer, Bauherr, Beschreibung, die Kennzeichnung als öffentliches
Gebäude, beide Standardspannungen sowie die im Projekt freigeschalteten
Verteiler-Netzarten als eine atomare Änderung. Die Gebäudekennzeichnung ist
der spätere Eingang für die Auswahl halogenfreier Kabel und Leitungen. Der
Systemkatalog besteht aus AV, SV, EV, USV, MSR und SiBe; mindestens
eine Netzart muss aktiv bleiben und eine bereits von einer Verteilung
verwendete Netzart kann nicht deaktiviert werden. Die Projektseite bearbeitet diese Angaben in einem
beschrifteten Einstellungsmodal statt in einer permanenten Formularkarte. Der
Store leitet das inverse Kommando aus dem gespeicherten Projekt ab und schreibt
Werte, Revision und Historienstapel gemeinsam. PUT /api/projects/:projectId
verlangt deshalb expectedRevision, liefert Projekt plus aktualisierten
Historienstand und besitzt keinen separaten direkten Settings-Schreibweg mehr.
Kommando- und Snapshot-Payloads müssen dem aktuellen Baseline-Schema entsprechen.
Die Projektseite zeigt Verteilungen, Etagen und Räume als kompakte
Bestandsübersichten; ihre versionierten Erstellwege öffnen beschriftete Modals
statt dauerhafter Eingabezeilen. Projektgeräte werden als durchsuchbare,
fünfspaltige Übersicht dargestellt. Manuelle Anlage, Übernahme aus der globalen
Bibliothek und vollständige Bearbeitung erfolgen in einem gemeinsamen Modal.
Die anschließende Vorschau verknüpfter Stromkreiszeilen bleibt davon getrennt,
damit Änderungen weiterhin niemals still synchronisiert werden.
GET /api/projects/:projectId/export verpackt denselben vollständigen
Projektzustand in ein portables, format- und schema-versioniertes JSON-Dokument
mit SHA-256-Prüfsumme. POST /api/projects/:projectId/import prüft Format,
Snapshot-Relationen und Prüfsumme vor jedem Schreibzugriff. Der Modus replace
läuft über project.restore-state und ist dadurch eine atomare, dauerhaft
rückgängig machbare Projektrevision. Der Modus duplicate ordnet Projekt-,
Struktur-, Gruppen-, Verteilerkomponenten-, Raum-, Stromkreis- und
Gerätezeilen-UUIDs sowie Schutzgeräte-Referenzen vollständig neu zu und legt
die Kopie mit Revision 0 in einer Transaktion an. Fachliche Verknüpfungen
innerhalb des unterstützten Laufzeitmodells bleiben
erhalten. Die
Projektübersicht verwendet dafür den separaten Collection-Endpunkt
POST /api/projects/import, der ausschließlich eine neue Kopie anlegt und
deshalb weder eine bestehende Projekt-ID noch expectedRevision annimmt. Das
Ersetzen eines Projekts bleibt auf dessen Einstellungsmodal und den
projektgebundenen Endpunkt beschränkt.
distribution-board.insert versioniert die Anlage einer Verteilung mit
Command-Schema 1 als vollständigen Block aus Verteilung, Stromkreisliste, den
drei Gruppen Beleuchtung 1, 1-phasig 1, 3-phasig 1, Hauptschalter -Q0
und Überspannungsableiter -FA.
Alle UUIDs entstehen vor dem Command und bleiben über Undo/Redo stabil.
distribution-board.delete ist die persistierte Inverse und entfernt nur den
vollständig unveränderten, weiterhin stromkreislosen Block. Controller und
Projektseite übergeben die erwartete Projektrevision; der frühere direkte
Controller-Schreibweg ist entfernt. Verteilungen besitzen eine optionale
Etagenreferenz sowie eine Netzart aus dem Projektkatalog. Anlage und
nachträgliche Bearbeitung prüfen die Projektzugehörigkeit der Etage und die
Freigabe der Netzart in den Projekteinstellungen.
Bereits befüllte Verteilungen werden über
distribution-board.insert-subtree und distribution-board.delete-subtree
als vollständiger Unterbaum kopiert beziehungsweise gelöscht. Der Snapshot
umfasst Verteilung, Stromkreisliste, Gruppen, Stromkreise, Gerätezeilen,
Verteilerkomponenten und alle zugehörigen Schutzgeräte. Beim Kopieren werden
sämtliche besitzenden UUIDs neu vergeben, während fachliche Verknüpfungen zu
Projektgeräten und Räumen erhalten bleiben. Beide Aktionen sind atomare
Projektrevisionen und bleiben nach einem Neustart über Undo/Redo umkehrbar. Die
Projektseite bietet sie im Einstellungsmodal der jeweiligen Verteilung an;
Löschen verlangt dort eine ausdrückliche Bestätigung.
distribution-board-component.insert und
distribution-board-component.delete sowie
distribution-board-component.update versionieren Anlage, Entfernung,
Bearbeitung und Sortierung veränderlicher Gruppen-Schutzgeräte und zusätzlicher
Verteilergeräte. Die vollständigen Snapshots enthalten stabile
Komponenten-UUIDs und, für Gruppen-Schutzgeräte, die getrennte
1:1-Schutzgerätekonfiguration. Updates tauschen einen exakt erwarteten gegen
einen vollständigen Zielzustand aus; ID, Eigentum, Rolle und Platzierungszone
bleiben dabei unverändert. Projekt-, Listen- und Gruppenzugehörigkeit sowie
veraltete Zustände werden innerhalb derselben Transaktion geprüft. Die festen
Kopfkomponenten Hauptschalter und Überspannungsableiter sind von diesen
allgemeinen Commands ausgeschlossen. Veränderliche Gruppen- und Fußkomponenten
werden im Editor über eigene Modale und diese Commands verwaltet.
Leere Stromkreisgruppen werden über circuit-group.insert,
circuit-group.update und circuit-group.delete verwaltet. Ihr vollständiger
Snapshot koppelt Kategorie, positive Gruppennummer und daraus abgeleiteten
BMK-Präfix; allgemeine Updates ändern ausschließlich den Anzeigenamen.
Löschen verlangt einen exakt unveränderten Zustand ohne Stromkreise und ohne
Gruppenkomponenten. Befüllte Gruppen verwenden getrennt davon den vollständigen
circuit-group.delete-subtree-Command mit ausdrücklicher Warnung.
circuit-group.reorder sortiert Gruppen als vollständige Stromkreislisten-
Zuordnung. Jede vorhandene Gruppe muss mit erwarteter und neuer Position
enthalten sein. Der Command verändert ausschließlich sortOrder; Nummern,
Präfixe und Stromkreis-BMKs bleiben stabil. Die gesamte Sortierung bildet eine
Revision und einen Undo/Redo-Schritt.
src/domain/services/circuit-group-renumbering.ts plant explizite
Gruppennummerierungen deterministisch. Der reine Plan erhält
Stromkreis-Endnummern, leitet Gruppenpräfixe und optionale .0-Komponenten-BMKs
neu ab und prüft Nummerntausch sowie Kollisionen mit unveränderten Gruppen.
circuit-group.renumber führt diesen Plan über die gemeinsame
Projekt-Command-Transaktion aus. Vor dem Schreiben müssen alle betroffenen
Gruppen, Stromkreise und Gruppenkomponenten exakt dem erwarteten Zustand
entsprechen. Nummerntausch verwendet kollisionsfreie temporäre Präfixe und
BMKs über beide BMK-Tabellen; anschließend werden alle Zielwerte finalisiert.
Der vollständige inverse Plan ermöglicht dauerhaftes Undo/Redo.
src/domain/services/circuit-group-move-planning.ts plant den Wechsel eines
vollständigen Stromkreises zwischen zwei verschiedenen Gruppen derselben
Kategorie. Die Ziel-BMK verwendet die höchste vorhandene Stromkreis-Endnummer
der Zielgruppe plus eins; Lücken werden nicht gefüllt. Quell-/Zielgruppe,
Quell-/Zielposition und beide BMKs werden festgeschrieben, damit eine spätere
Wiederholung nichts neu berechnet.
circuit.move-group führt den geplanten Wechsel als eine Projekt-Revision aus.
Nur sectionId, equipmentIdentifier und sortOrder des Stromkreises ändern
sich. Gerätezeilen und das getrennte 1:1-Schutzgerät bleiben über die stabile
Stromkreis-UUID verbunden. Der inverse Wechsel speichert Quellgruppe, BMK und
Position vollständig für dauerhaftes Undo/Redo.
CircuitGroupSubtreeSnapshot ist der kanonische Vertrag für destruktive
Gruppenoperationen. Er umfasst Gruppe, optionale Gruppenkomponenten samt
1:1-Schutzdaten, vollständige Stromkreise samt 1:1-Schutzdaten und sämtliche
Gerätezeilen einschließlich Link- und Override-Metadaten. Seine
Warnzusammenfassung liefert Vorsicherung/FI sowie Stromkreis- und
Gerätezeilenanzahl.
circuit-group.delete-subtree nimmt den aktuellen Unterbaum unmittelbar vor
dem Löschen erneut auf und verlangt exakte Übereinstimmung mit dem bestätigten
Snapshot. circuit-group.restore-subtree schreibt ihn in FK-sicherer Reihenfolge
mit denselben UUIDs zurück. Beide Richtungen laufen als vollständige inverse
Projekt-Commands; späte Fehler hinterlassen weder Teillöschungen noch
Teilwiederherstellungen.
Der Circuit-Tree liefert zusätzlich headerComponents,
gruppenbezogene components und footerComponents. Abschnitte enthalten
optionale Gruppenkategorie und -nummer; Stromkreise und Gruppenkomponenten
enthalten ihre getrennte Schutzgerätekonfiguration. Die
1:1-Schutzgerätetabellen sind die einzige unterstützte Quelle dieser Daten.
Separate aktive Read-Repositories lesen Komponenten- und beide
Schutzgerätetabellen; Schreibzugriffe bleiben ausschließlich in Commands.
src/frontend/utils/circuit-structure-projection.ts projiziert diesen Tree
rein und deterministisch in die drei Zonen Kopf, vollständige Gruppen und Fuß.
Innerhalb einer Gruppe folgen auf die Überschrift optionale Schutzkomponenten,
vollständige Stromkreisblöcke und der freie Einfügeplatz. Leere Gruppen bleiben
sichtbar; Komponenten werden stabil nach sortOrder und ID sortiert.
circuit-grid-projection.ts normalisiert diese Struktur zusammen mit den
bisherigen kompakten und mehrzeiligen Stromkreisblöcken für den Editor. Kopf-,
Gruppen- und Fußkomponenten erscheinen als Zeilen über die volle Tabellenbreite
und nehmen nicht an Zellauswahl oder Tastatur-Einfügen teil. Hauptschalter und
Überspannungsableiter bleiben feste, schreibgeschützte Kopfkomponenten.
Optionale Gruppenvorsicherungen, Gruppen-FIs und zusätzliche Verteilergeräte
werden über beschriftete Modale angelegt, bearbeitet und nach Bestätigung
entfernt. Der Frontend-API-Adapter serialisiert dafür die vorhandenen
distribution-board-component.*-Commands; die verbindliche
geräteabhängige Validierung und atomare Revision bleiben serverseitig.
Neue Stromkreisgruppen erhalten im Editor die höchste vorhandene
Gruppennummer ihrer Kategorie plus eins und ein daraus abgeleitetes Präfix.
Das Bearbeiten ändert ausschließlich den Anzeigenamen. Nur vollständig leere
Gruppen können über circuit-group.delete entfernt werden; für befüllte
Gruppen bleibt die Aktion bis zum gesonderten Warn- und Unterbaumdialog
gesperrt.
circuit-protection.update bildet die eigene persistente Schreibgrenze für
die neue 1:1-Stromkreisschutztabelle. Der Command vergleicht den vollständigen
erwarteten Datensatz, validiert Typ und abhängige Felder, prüft die
Projektzugehörigkeit und schreibt Schutzgerät, Revision und Historienübergang
atomar. Benutzer können Schutzdaten anlegen oder ändern, aber nicht entfernen;
Undo einer erstmaligen Anlage darf den zuvor fehlenden Datensatz exakt
wiederherstellen.
Der vollständige CircuitSnapshot enthält den zugehörigen
protectionDevice-Datensatz, sofern einer vorhanden ist. Neue Stromkreise und
durch Geräteverschiebung erzeugte Zielstromkreise verwenden die vereinbarten
Kategorie-Standardwerte und schreiben Schutzgerät, Stromkreis sowie
Gerätezeilen atomar. Delete/Undo erfasst denselben Datensatz vollständig.
Im Editor sind die Schutzspalten deshalb eine
schreibgeschützte Projektion der 1:1-Daten; Änderungen erfolgen über ein
geräteabhängiges Schutzgeräte-Modal und circuit-protection.update.
Gruppen lassen sich im Editor schrittweise nur gegenüber einer benachbarten
Gruppe derselben Kategorie verschieben. Der Client sendet dabei immer die
vollständige erwartete und gewünschte Sortierreihenfolge über
circuit-group.reorder; Gruppennummer, Präfix und Kind-BMK bleiben unverändert.
Eine separate, ausdrücklich bestätigte Editoraktion erzeugt dagegen einen
vollständigen circuit-group.renumber-Plan für genau eine Kategorie. Sie
nummeriert deren Gruppen gemäß aktueller Reihenfolge ab eins und ändert
Gruppenpräfixe, optionale Gruppen-Schutz-BMK und Stromkreis-BMK gemeinsam;
die Stromkreisnummer hinter dem letzten Punkt bleibt erhalten.
Das vorhandene Stromkreis-Drag-Handle kann außerdem genau einen vollständigen
Stromkreis in eine andere Gruppe derselben Kategorie verschieben. Der atomare
circuit.move-group-Befehl erhält Gerätezeilen und Schutzdaten, setzt die
Zielposition und vergibt dort die höchste vorhandene Stromkreisnummer plus eins;
Lücken werden nicht automatisch gefüllt.
Befüllte Gruppen werden im Editor erst nach einer Warnung mit Anzahl der
enthaltenen Stromkreise, Gerätezeilen und Gruppenschutzgeräte gelöscht. Der
Client sendet dafür den vollständigen aktuellen Unterbaum an
circuit-group.delete-subtree; der Server vergleicht ihn innerhalb derselben
Transaktion mit dem Datenbankstand. Undo stellt Gruppe, Schutzgeräte,
Stromkreise, Gerätereihen und Verknüpfungs-/Override-Metadaten vollständig
wieder her.
distribution-board.update versioniert Etage, Netzart und den
verteilerweiten Gleichzeitigkeitsfaktor gemeinsam und stellt alle Werte über
dauerhaftes Undo/Redo wieder her. Der Faktor liegt zwischen 0 und 1 und
ist für bestehende sowie neu angelegte Verteilungen standardmäßig 1.
Snapshot-Schema 5 enthält zusätzlich den manuellen Mengenanteil jeder
Gerätezeile. Version 4 wird mit manualQuantity = quantity hochgestuft und
enthält bereits den vollständigen externen Modellzustand
aus Quelle, Batches, Raum-Mappings und Objekten. Es enthält außerdem die
projektweite Revit-CSV-Konfiguration, die Gebäudekennzeichnung sowie den Gleichzeitigkeitsfaktor,
Gruppenkategorie und -nummer, Verteilerkomponenten sowie die getrennten
1:1-Schutzgerätedaten. Capture,
benannte und automatische Snapshots, Wiederherstellung, Undo/Redo und beide
JSON-Importmodi verwenden denselben vollständigen Zustand. Die unterstützte
Version 3 wird mit leerem externem Modellzustand und Version 2 zusätzlich mit
leerer externer Konfiguration hochgestuft. Die unterstützte
Baseline-Version 1 wird zusätzlich mit isPublicBuilding = false hochgestuft; Formate vor
dieser Baseline werden nicht eingelesen.
Persistente Insert-, Update- und Delete-Commands versionieren Anlage,
Bearbeitung und Löschung von Geschossen und Räumen mit stabilen UUIDs und
exakten Vorher-/Nachher-Snapshots. Ein Geschoss wird nur
entfernt, solange ihm weder ein Raum noch eine Verteilung zugeordnet wurde. Ein
Raum wird nur entfernt, solange keine CircuitDeviceRow auf ihn verweist. Alle
Schreibendpunkte verlangen expectedRevision und liefern den aktualisierten
Historienstand.
Projektgeräte
Die Projektgerätekategorie lighting, single_phase oder three_phase ist die
fachliche Klassifikation und Zielgruppe. phaseType ist keine separate
Benutzereingabe, sondern wird daraus für Spannung, Verknüpfung und spätere
Dimensionierung abgeleitet.
Beim Einfügen entsteht eine verknüpfte CircuitDeviceRow. Der Anzeigename wird
kopiert, aber nicht still synchronisiert. Spätere Änderungen am Projektgerät
werden als Diff angezeigt und nur für ausdrücklich gewählte Felder und Zeilen
übernommen.
Die globale Gerätebibliothek ist ein einfacher, datenbankweiter Vorlagenbestand. Kopieren in ein Projekt erzeugt ein eigenständiges Projektgerät.
Persistenz und Migration
- SQLite ist die aktuell unterstützte Datenbank.
- Fremdschlüssel werden für jeden Datenbankkontext aktiviert.
npm run db:migraterichtet mit Baseline-Migration0000und den folgenden additiven Migrationen das aktuelle Schema ein.npm run db:verify:circuit-schemaprüft erforderliche und entfernte Spalten.npm run db:backuperzeugt ein konsistentes und verifiziertes Online-Backup.npm run typecheck:scriptsprüft die TypeScript-Wartungsskripte, ohne Code zu erzeugen.- Die Baseline ersetzt alle vorigen Entwicklungsmigrationen. Datenbanken, Snapshots und persistierte Commands aus Vor-Baseline-Ständen werden bewusst nicht unterstützt. Ab der ersten veröffentlichten Version sind angewendete Migrationen unveränderlich und Änderungen erfolgen additiv.
- Allgemeine Circuit-, Gerätezeilen-, CircuitList- und DistributionBoard- Repositories stellen im Anwendungspfad nur noch benötigte Leseabfragen bereit. Fachliche Schreibvorgänge liegen in den typisierten Command-Repositories.
- Die vollständige Verteilungs-Testfixture liegt unter
tests/support/distribution-board-fixture.tsund ist kein exportierter Produktions-Schreibweg. - Die Baseline bildet die relationale Grundlage für geschützte Stromkreisgruppen und Verteilerkomponenten. Separate 1:1-Tabellen halten Stromkreis- und Komponenten-Schutzgeräte. Die früheren flachen Stromkreis-Schutzfelder sind aus der Baseline entfernt. Ein triggergeführtes Register erzwingt bereits eine normalisierte, stromkreislistenweite BMK-Eindeutigkeit über Stromkreise und Verteilerkomponenten. Snapshot- und Transfer-Integration verwenden aktuell Snapshot-Schema 5. Persistente Insert/Delete/Update-Commands für veränderliche Verteilerkomponenten, Gruppen einschließlich befüllter Unterbäume sowie vollständige Gruppensortierung sind integriert. Der Editor zeigt die geschützte Struktur an und bearbeitet veränderliche Gruppen- und Fußkomponenten über dedizierte Command-Modale. Gruppenanlage, -umbenennung, -sortierung, explizite Neunummerierung, Same-Category-Stromkreiswechsel, geschütztes Unterbaumlöschen und Stromkreisschutz sind integriert.
PostgreSQL ist bewusst nicht implementiert. Die Domainregeln und Transaktionsgrenzen sollen portabel bleiben; Schema und Betriebsmodell benötigen bei einem späteren Wechsel trotzdem einen eigenen PostgreSQL-Adapter.
Revit-CSV-Grundlage
Der reine Adapter unter src/external-model/csv validiert die projektweite
Transport- und Mappingkonfiguration und parst UTF-8-CSV verlustarm in eine
klassifizierte Zellmatrix. Er erkennt Metadaten, Header, Passthrough-, Objekt-
und Verdachtszeilen, prüft IFCGUIDs und serialisiert unveränderte Revit-Exporte
einschließlich BOM, CRLF und Quotierung bytegleich zurück. Der lokale Befehl
npm run revit:verify-reference prüft die bereitgestellte Referenzdatei.
external_csv_configurations enthält höchstens eine versionierte Konfiguration
je Projekt. external-csv-configuration.update schreibt Anlage, Änderung oder
Entfernung zusammen mit Revision und persistentem Undo/Redo. Snapshot-Schema 5,
Restore und portabler Projekttransfer enthalten diesen Zustand; beim Duplizieren
werden Konfigurations-UUID und Projektlink remapped.
Migration 0003 ergänzt die relationale Grundlage für genau eine implizite
revit_csv-Quelle je Projekt, geordnete Importbatches, normalisierte
Quellraummappings und über (sourceId, ifcGuid) eindeutige externe Objekte.
Batches halten Konfigurationssnapshot, Originalbytes und die klassifizierte
Zellmatrix zusammen. Migration 0004 ergänzt die monotone projektweite
Konfigurationsversion am Batch, getrennt von der Schema-Version des
Konfigurationsformats. Quellwerte, lokale Planungswerte und Override-Metadaten
der Objekte sind getrennt; optionale Links auf Raum, Verteilung, ProjectDevice
und CircuitDeviceRow bleiben nullable. ExternalModelStateRepository liest den
vollständigen Zustand deterministisch und unterscheidet unbekannte von noch
nicht importierten Projekten. Der letzte bestätigte Batch wird aus der
Batchreihenfolge abgeleitet statt redundant an der Quelle gespeichert.
Snapshot/Transfer v5 erfasst zusätzlich manualQuantity; Version 4 wird mit
dem bisherigen Gesamtwert als manuellem Anteil hochgestuft. Originalbytes,
Matrix und alle internen Links werden weiterhin vollständig erfasst,
stellt sie in Fremdschlüsselreihenfolge wieder her und remappt beim
Projektduplikat jede interne UUID; IFCGUID und Quelltransport bleiben
unverändert. external-import.apply-initial ist die atomare Schreibgrenze für
einen bestätigten Erstimport. Vor dem Insert prüft sie den aktuellen
Konfigurationsstand, SHA-256 und Base64-Kanonizität, parst die Originalbytes
erneut, vergleicht Matrix, vollständige IFCGUID-Menge und Quellwerte und
validiert alle Raum-, Verteiler- und ProjectDevice-Links gegen das Projekt.
Nicht explizit als Override markierte Planungswerte müssen dem regelbasierten
Vorschlag entsprechen. Der Command erzeugt und verknüpft ausdrücklich keine
CircuitDeviceRow; Undo entfernt den vollständigen unveränderten Importzustand,
Redo verwendet dieselben UUIDs und Bytes. Bloße Vorschau und Importplanung
erzeugen weiterhin keine dieser Zeilen.
Migration 0005 trennt bei CircuitDeviceRow den manuellen Mengenanteil vom
weiterhin materialisierten Gesamtwert. Bestehende Daten werden mit
manualQuantity = quantity übernommen. Die Fachregel für spätere externe
Zuordnungs-Commands lautet quantity = manualQuantity + Summe der effectiveQuantity aller verknüpften ExternalModelObjects; Link und beide
Mengenwerte müssen innerhalb desselben Projekt-Commands wechseln.
external-object.update-row-assignment ist diese atomare Schreibgrenze für
bestehende Gerätezeilen. Sie weist mehrere externe Einzelobjekte zu, verschiebt
sie zwischen vorhandenen Rows oder löst ihre Links. Vor dem Schreiben werden
die vollständigen erwarteten Row- und Objektsnapshots, Projektzugehörigkeit,
Verteilung, Gruppenkategorie, Raum und Selektionskennzeichen geprüft.
Abweichende lokale Planungswerte benötigen eine explizite Konfliktbestätigung;
unterschiedliche Räume, Kategorien oder Selektionskennzeichen werden nicht
still zusammengeführt. Alle betroffenen materialisierten Mengen werden aus dem
unveränderten manuellen Anteil und sämtlichen Ziel-Objektlinks validiert. Die
Gegenrichtung enthält dieselben vollständigen Snapshots und funktioniert nach
Neustart; beim Lösen des letzten Objekts bleibt die Row bestehen.
external-object.assign-to-new-row fügt unzugeordnete Objekte atomar als neue
Gerätezeile in einen vorhandenen Stromkreis ein. Der manuelle Anteil dieser Row
ist null; Gesamtmenge, Raum, Verteilung, Kategorie, Selektionskennzeichen und
Planungswerte werden vor dem Schreiben geprüft. Undo löst exakt diese Objekte
und löscht die erzeugte Row nur, wenn sie und ihre Linkmenge unverändert sind.
Der Löschbefehl ist ausschließlich für die Projekthistorie zugelassen; eine
normale spätere Trennung lässt die Row gemäß der allgemeinen Zuordnungsregel
bestehen.
external-object.assign-to-new-circuit erzeugt einen neuen Stromkreis mit
vorab festgeschriebenem BMK, abgeleiteter Projektspannung, genau einer
externen Row und verpflichtendem Standardschutz zusammen mit den Objektlinks.
Verteilungs-, Gruppen-, Raum-, Mengen- und Planungsregeln werden vor dem Insert
gegen den aktuellen Projektstand geprüft. Der historische Gegenbefehl ist kein
allgemeiner Löschpfad: Er entfernt den vollständigen Circuit-Teilbaum nur bei
unverändertem Snapshot und unveränderter Objektlinkmenge. Redo verwendet exakt
dieselben IDs, dasselbe BMK und denselben Schutzsnapshot.
Die drei Zuordnungsadapter teilen sich
external-object-assignment.persistence.ts für Snapshotvergleich,
Kompatibilitätsregeln und link-sichere Updates.
GET /api/projects/:projectId/circuit-lists/:circuitListId/external-objects
liefert die verteilungsbezogene Read-Projektion für den späteren Editor-Drawer.
Die CircuitList bestimmt den Verteiler serverseitig; die Antwort enthält nur
dessen externe Objekte, sortiert unzugeordnet vor zugeordnet, sowie kompakte
Quell-, Raum-, Planungs- und Linkangaben. Originalbytes und vollständige
Command-Snapshots werden nicht an diese Oberfläche gegeben. Ohne optionalen
Revit-Import ist die Projektion leer.
GET und PUT /api/projects/:projectId/external-csv/configuration lesen oder
ändern die Konfiguration; der PUT plant Identität und nächsten
Konfigurationsstand serverseitig und verwendet den typisierten Command.
POST /api/projects/:projectId/external-csv/preview nimmt Dateiname und
Base64-kodierte CSV bis 18 MB entgegen. Der zustandsfreie Service liefert Hash,
Dialekt, Header, Klassifikationszahlen, Verdachtszeilen und gemappte
Objektquellwerte. Er schreibt weder Entwurf noch Projektdaten.
POST /api/projects/:projectId/external-csv/initial-import/plan verwendet
denselben begrenzten Transport, setzt eine gespeicherte Konfiguration und einen
noch leeren externen Projektzustand voraus und bleibt ebenfalls zustandsfrei.
Die Antwort gruppiert Quellräume, schlägt bei genau einer identischen
Raumnummer den vorhandenen Raum vor, gruppiert exakte Familie-und-Typ-Werte,
liefert regelbasierte Objektplanungswerte und Konfliktzahlen und enthält die
vorhandenen Ebenen, Räume, Verteilungen und ProjectDevices als Auswahlkataloge.
Unbekannte Familien, ungültige Leistungen/Mengen und Objekte ohne Raum bleiben
sichtbare Warnungen; es wird keine Entscheidung automatisch bestätigt.
POST /api/projects/:projectId/external-csv/initial-import/apply nimmt Datei,
erwarteten Hash, Konfigurationsversion und Projektrevision erneut entgegen.
Vollständige Entscheidungen ordnen jeden Quellraum optional einem vorhandenen
Raum und einer Standardverteilung sowie jede exakte Familiengruppe optional
einem vorhandenen ProjectDevice zu. Unklassifizierte Familien oder fehlende
Gruppenentscheidungen blockieren. Der Server parst und plant erneut, erzeugt
stabile interne UUIDs und übergibt den vollständigen Zustand an
external-import.apply-initial; CSV-Stromkreiswerte bleiben reine Quellwerte
und alle circuitDeviceRowId bleiben null.
Die Projektseite öffnet über „Revit-CSV“ ein deutsches Modal für Dialekt,
Spaltenzuordnung, zusätzliche Quellfelder und exakte Familie-und-Typ-Regeln.
Nach dem revisionierten Speichern kann eine lokale CSV gewählt und über den
zustandsfreien Endpunkt geprüft werden. Die Oberfläche zeigt Kennzahlen und nur
die ersten 25 erkannten Objekte; die vollständigen Objekte bleiben in der
Response verfügbar, ohne alle Zeilen in den DOM einzufügen. Der anschließende
Erstimport-Wizard gruppiert Quellräume und exakte Familie-und-Typ-Werte. Er
zeigt eindeutige, mehrdeutige und neue Raumtreffer, lässt vorhandene Räume,
Standardverteilungen und ProjectDevices explizit zuordnen und blockiert die
Übernahme bei unklassifizierten Familien. Erst nach einem Bestätigungsdialog
wird der vollständige Import als eine persistente, rückgängig machbare
Projektrevision angewendet. Dabei entstehen keine Stromkreise oder Gerätezeilen.
Fehlende Räume und ProjectDevices können direkt aus der jeweiligen Gruppe über
die regulären Projektformulare angelegt werden. Revit-Werte befüllen diese
Formulare vor, werden aber vor dem Speichern sichtbar vom Nutzer bestätigt.
Die Kataloganlage ist jeweils ein eigener Projekt-Command; der Wizard übernimmt
den neuen Datensatz anschließend in seinen lokalen Auswahlkatalog und markiert
ihn für die betroffene Gruppe.
Noch nicht unterstützt
- Mehrbenutzerbetrieb und Konfliktauflösung
- Editor-Oberfläche für die vorhandene Zuordnungslogik, Folgeimport und strukturtreuer Revit-Rückexport
- vollständige elektrische Dimensionierung
- Produktionsdeployment
Details: Bekannte Einschränkungen und Zukunftsarchitektur.