← Zurück zu pelczer.de

Logikpläne & Systemarchitektur

Die iOS-App des baumops-Systems. Offline-First, Core Data, MVVM — vom Aufmaß über die Kalkulation bis zur Geländebrücke. Hier ist der komplette Bauplan.

~50SwiftUI Views
6Export-Formate
5–9Entwicklungs-Wellen
0externe Dependencies
Swift / SwiftUI Core Data Offline-First MVVM Kernel-Guards FastAPI-Backend

System-Übersicht

Construction Grid ist die iOS-App im baumops-System: Sie läuft offline auf dem Gerät und spricht bei Bedarf die Mops-Box im Baustellennetz an.

Plattform

iOS
Swift / SwiftUI · iPhone & iPad

Persistenz

Core Data
Offline-First · test25B.xcdatamodeld

Architektur

MVVM
+ Kernel-Schicht (TheBrain, Guards)

Dependencies

0
Keine externen SPM-Pakete im Frontend

baumops — das System, in dem die App lebt

graph TB subgraph DEVICE["iPad/iPhone auf der Baustelle"] APP["iMOPS Construction Grid
Swift / SwiftUI · Offline-First"] CD[("Core Data
lokal am Gerät")] APP --- CD end subgraph BOX["Mops-Box (lokaler Server im Baustellennetz)"] API["mops-api
FastAPI · Python"] MOPS["Mops
llama3.2:3b · Ollama"] PROF["Prof
Claude Sonnet · Eskalation"] RAG[("Qdrant RAG
Bau-Wissensbasis")] API --- MOPS API --- PROF API --- RAG end CLOUD["mops.baumops.com
Cloud"] APP -->|"WLAN, nur wenn vorhanden"| API APP -.->|"optional"| CLOUD style APP fill:#1f3a5f,stroke:#58a6ff,color:#fff style CD fill:#4f3a1f,stroke:#d29922,color:#fff style API fill:#3b1f5e,stroke:#bc8cff,color:#fff style MOPS fill:#1f4f3a,stroke:#56d364,color:#fff style PROF fill:#5f3a1f,stroke:#f0883e,color:#fff style RAG fill:#1f3a4f,stroke:#79c0ff,color:#fff style CLOUD fill:#30363d,stroke:#8b949e,color:#c9d1d9

App-Schichtenmodell

graph TB subgraph V["View-Schicht (SwiftUI, ~50 Views)"] direction LR EvList["EventListView"] EvDetail["EventDetailView
der Hub"] Kalk["LVKalkulationView"] CAD["CADImportView /
CADViewerView"] Aufmass["AufmassSheet"] end subgraph VM["ViewModel-Schicht (MVVM)"] direction LR AddJob["AddJobVM"] EventVM["EventListVM"] JobVM["Job-VMs"] BauWissen["BauWissenVM"] end subgraph SVC["Service-Schicht"] direction LR LVKalk["LVKalkulator"] Mapper["ExtractPlanMapper"] Gen["HouseProject/Generator"] Exp["Exporter"] end subgraph KERN["Kernel-Schicht"] direction LR Brain["TheBrain"] Guards["KernelGuards"] end subgraph PERS["Persistenz"] CoreData[("Core Data
test25B.xcdatamodeld")] end V --> VM VM --> SVC SVC --> KERN KERN --> PERS SVC --> PERS style V fill:#1f3a5f,stroke:#58a6ff,color:#fff style VM fill:#3b1f5e,stroke:#bc8cff,color:#fff style SVC fill:#1f4f3a,stroke:#56d364,color:#fff style KERN fill:#5f1f1f,stroke:#f85149,color:#fff style PERS fill:#4f3a1f,stroke:#d29922,color:#fff

System-Architektur

Die Ordnerstruktur des Repos spiegelt die Schichten: App, Models, Views, ViewModels, Service, Kernel, Resources.

Projektstruktur — Verantwortlichkeiten

graph LR ROOT["iMOPS Construction Grid"] ROOT --> App["App/
Einstieg, Szenen-Konfig"] ROOT --> Models["Models/
Core-Data-Entities"] ROOT --> Views["Views/
~50 SwiftUI-Views"] ROOT --> VMs["ViewModels/
MVVM"] ROOT --> Service["Service/
Domänen-Logik & Exporter"] ROOT --> Kernel["Kernel/
Guard- & Workflow-Engine"] Models --> M1["LVPosition"] Models --> M2["Aufmass"] Models --> M3["GelaendeResult"] Service --> S1["LVKalkulator"] Service --> S2["ExtractPlanMapper"] Service --> S3["HouseProject/Generator"] Kernel --> K1["TheBrain"] Kernel --> K2["KernelGuards"] style ROOT fill:#1f6feb,stroke:#58a6ff,color:#fff style Kernel fill:#5f1f1f,stroke:#f85149,color:#fff style Models fill:#4f3a1f,stroke:#d29922,color:#fff style Service fill:#1f4f3a,stroke:#56d364,color:#fff

Kernel-Guards — ein Schritt ist erst fertig, wenn er geprüft ist

flowchart TD Action["Aktion / Zustandsänderung
z.B. Position freigeben"] --> Guard{"KernelGuards
prüfen Regeln"} Guard -->|"Regel verletzt"| Block["Blockiert
Grund wird angezeigt"] Guard -->|"Alle Regeln erfüllt"| Brain["TheBrain
Zustand fortschreiben"] Brain --> Persist["Core Data
persistieren"] Block --> Action style Guard fill:#9e6a03,stroke:#d29922,color:#fff style Block fill:#da3633,stroke:#f85149,color:#fff style Brain fill:#3b1f5e,stroke:#bc8cff,color:#fff style Persist fill:#238636,stroke:#56d364,color:#fff

Datenmodell

Core-Data-Modell test25B.xcdatamodeld. Die Beziehungen unten sind mein Verständnis aus dem Code — bitte fachlich gegenprüfen.

Bitte prüfen: Die genauen Entity-Namen und Kardinalitäten (1:n / n:m) habe ich aus README und Ordnerstruktur abgeleitet. Korrigiere Namen, Felder und Beziehungen, wo sie vom echten Modell abweichen.

Kern-Entitäten & Beziehungen (Entwurf)

erDiagram EVENT ||--o{ LVPOSITION : "enthält" EVENT ||--o{ GELAENDERESULT : "hat Berechnung" EVENT ||--o{ BAUTAGESBERICHT : "dokumentiert" LVPOSITION ||--o{ AUFMASS : "wird gemessen durch" LVPOSITION { string bezeichnung decimal menge string einheit decimal einheitspreis decimal einkaufspreis string mengenQuelleRaw "gemessen | geschätzt" } AUFMASS { decimal sollMenge decimal istMenge date erfasstAm } EVENT { string name string ort date datum string status } GELAENDERESULT { decimal cutVolumen decimal fillVolumen string dxfQuelle } BAUTAGESBERICHT { date tag string wetter string leistung }

Fortschritt-Ableitung — gemessen verdrängt geschätzt (Welle 5.2.1)

flowchart LR Pos["LV-Position"] --> Q{"mengenQuelleRaw?"} Q -->|"geschätzt"| Est["Fortschritt aus
Schätzwert"] Q -->|"gemessen (Aufmaß)"| Meas["Fortschritt aus
Aufmaß-Istmenge"] Meas --> Win["Gemessen gewinnt
verdrängt die Schätzung"] Est -.->|"sobald Aufmaß da ist"| Win style Meas fill:#238636,stroke:#56d364,color:#fff style Win fill:#1f6feb,stroke:#58a6ff,color:#fff style Est fill:#30363d,stroke:#8b949e,color:#c9d1d9

Wellen-Lifecycle

Die App wächst in „Wellen". Jede Welle ist erst fertig, wenn Build grün ist und ein Test besteht — nicht wenn jemand „ich denke, das funktioniert" sagt.

Welle 5.1–5.2.1

Aufmaß
Soll/Ist, Fortschritt aus Messung · gemergt

Welle 6

Kalkulation
LV-Kalkulation, Live-Summe · gemergt

Welle 7

Geländebrücke
DXF, Cut/Fill, PDF · gemergt

Welle 9

Ampel
Voraussetzungs-Ampel · geplant

Von der Position zur Baufreigabe — der Datenfluss durch die Wellen

flowchart TD Import["Plan-Import
ExtractPlanMapper
mengenQuelleRaw"] --> LV["LV-Position
angelegt"] LV --> Aufmass["Welle 5: Aufmaß
Soll/Ist erfassen"] Aufmass --> Fortschritt["Welle 5.2.1:
Fortschritt aus Messung"] Fortschritt --> Kalk["Welle 6: Kalkulation
Live-Summe, Einkaufspreise"] Import --> Gelaende["Welle 7: Geländebrücke
DXF → Cut/Fill → LV"] Gelaende --> Kalk Kalk --> Ampel{"Welle 9:
Voraussetzungs-Ampel"} Ampel -->|"alles erfüllt"| Gruen["GRÜN
Baufrei"] Ampel -->|"offen"| Rot["ROT
nicht baufrei"] Gruen --> Freigabe["Polier-Freigabe"] style Aufmass fill:#1f6feb,stroke:#58a6ff,color:#fff style Kalk fill:#3b1f5e,stroke:#bc8cff,color:#fff style Gelaende fill:#1f4f3a,stroke:#56d364,color:#fff style Gruen fill:#238636,stroke:#56d364,color:#fff style Rot fill:#da3633,stroke:#f85149,color:#fff

Voraussetzungs-Ampel — Zustände (Welle 9, geplant)

stateDiagram-v2 [*] --> Rot Rot --> Gelb: erste Voraussetzungen erfüllt Gelb --> Gruen: alle Voraussetzungen erfüllt Gruen --> Freigegeben: Polier gibt frei Gelb --> Rot: Voraussetzung entfällt Gruen --> Gelb: Voraussetzung entfällt Freigegeben --> [*]

Geländebrücke (Welle 7)

Vom CAD-Plan zur Erdmassenberechnung: DXF-Upload, Höhen-Interpolation, Cut/Fill, PDF-Report — und die Mengen fließen zurück ins Leistungsverzeichnis.

DXF → Cut/Fill → LV — die Pipeline

flowchart LR DXF["DXF-Datei
CADImportView"] --> Parse["Geländepunkte
einlesen"] Parse --> IDW["IDW-Interpolation
Höhenmodell"] IDW --> CutFill["Cut/Fill
Erdmassen berechnen"] CutFill --> Result["GelaendeResult
Cut- & Fill-Volumen"] Result --> PDF["PDF-Report"] Result --> Bridge["Brücke ins LV
Mengen → LV-Position"] Bridge --> Kalk["fließt in
Welle-6-Kalkulation"] style IDW fill:#3b1f5e,stroke:#bc8cff,color:#fff style CutFill fill:#9e6a03,stroke:#d29922,color:#fff style Result fill:#1f4f3a,stroke:#56d364,color:#fff style Bridge fill:#1f6feb,stroke:#58a6ff,color:#fff

Wo gerechnet wird — App und Mops-Box teilen sich die Arbeit

sequenceDiagram participant U as Polier (iPad) participant A as Construction Grid participant B as Mops-Box (mops-api) U->>A: DXF hochladen A->>A: Plan parsen, Punkte extrahieren A->>B: POST /gelaendebruecke/calculate B->>B: IDW-Interpolation, Cut/Fill B-->>A: Cut-/Fill-Volumen A->>A: GelaendeResult speichern (Core Data) A->>U: PDF-Report + LV-Mengen Note over A,B: Ohne Mops-Box im Netz bleibt der Rest der App voll nutzbar

Mops-Box & Backend

Die Mops-Box ist ein einfacher Rechner im Baustellennetz (Ubuntu, CPU-only). Sie hostet mops-api: ein lokales RAG-Backend mit zwei „Köpfen" — Mops und Prof.

Hardware

i5 · 16 GB
Ubuntu 24.04 · CPU-only · :8080

Mops

llama3.2
lokal · Maurermeister-Persona

Prof

Sonnet
Claude · Eskalation via /prof

RAG

Qdrant
Hybrid Vector + Keyword-Boost

Frage von der Baustelle — Mops zuerst, Prof bei Bedarf

flowchart TD Q["Bau-Frage aus der App
POST /chat"] --> Pref{"/prof Prefix?"} Pref -->|"Nein"| RAG["Hybrid-RAG
Qdrant + Keyword-Boost"] RAG --> Mops["Mops antwortet
llama3.2, lokal"] Mops --> Ans["Antwort an die App"] Pref -->|"Ja"| Prof["Prof antwortet
Claude Sonnet"] Prof --> Echo["Antwort als pending Echo
in Qdrant"] Echo --> Admin{"Admin-UI
Freigabe?"} Admin -->|"approve"| Wissen["wandert ins
approved-Wissen"] Prof --> Ans style Mops fill:#1f4f3a,stroke:#56d364,color:#fff style Prof fill:#5f3a1f,stroke:#f0883e,color:#fff style RAG fill:#1f3a4f,stroke:#79c0ff,color:#fff style Wissen fill:#238636,stroke:#56d364,color:#fff

Hybrid-RAG — warum exakte Begriffe höher ranken

flowchart LR Query["Query"] --> Vec["MiniLM
384-dim Vector"] Vec --> Cand["Qdrant: top_k×4
Cosine-Kandidaten"] Query --> KW["Keywords
extrahieren"] Cand --> Boost["Score-Boost
final = vector + 0.15 × hits"] KW --> Boost Boost --> Rank["Re-Ranking
top_k zurück"] style Boost fill:#9e6a03,stroke:#d29922,color:#fff style Rank fill:#238636,stroke:#56d364,color:#fff

Export & Nachweise

Was im System steht, muss raus können — in den Formaten, die Bau-Büros und Behörden lesen. Sechs Exporter, ein gemeinsames Prinzip: belegbar.

Exporter-Pipeline

flowchart TD Data[("Core Data
Baustellen-Daten")] --> Export{"Exporter"} Export --> GAEB["GAEB
LV-Austausch"] Export --> XR["XRechnung
elektr. Rechnung"] Export --> LVPDF["LV-PDF"] Export --> LVCSV["LV-CSV"] Export --> Mangel["Mangel-PDF"] Export --> Tagesbericht["Bautagesbericht-PDF"] style Data fill:#4f3a1f,stroke:#d29922,color:#fff style Export fill:#1f6feb,stroke:#58a6ff,color:#fff style GAEB fill:#238636,stroke:#56d364,color:#fff style XR fill:#238636,stroke:#56d364,color:#fff
Git-Disziplin (The Mops Protocol): Keine direkten Pushes auf main (Hook-blockiert). Workflow: feature/… → Push → PR → Review → Merge. Ein Feature ist erst fertig, wenn der Build grün ist und ein Test besteht.