# 🧠 Architekturübersicht – NodeMap Dieses Dokument beschreibt die technische Gesamtarchitektur des Projekts **NodeMap**, einer kartenbasierten Webanwendung zur Anzeige, Bearbeitung und Verwaltung von GIS-Daten, POIs und Gerätestatus. --- ## ⚙️ Technologie-Stack | Komponente | Beschreibung | | --------------------- | ---------------------------------------------------------------------- | | **Frontend** | React 18 + Next.js (App Router) | | **State-Management** | Redux Toolkit mit zentralem Store, Thunks & Slices | | **UI** | Tailwind CSS + Leaflet + React-Icons | | **Backend-Anbindung** | Webservices via `WebServiceMap.asmx` (IIS) + lokale Next.js API für DB | | **Datenbank** | MySQL (Produktiv & Entwicklung, z. T. via Docker) | | **Deployment** | Windows Server (IIS), optional per `nssm` als Dienst | --- ## 🗺️ Architekturüberblick ``` +------------------+ +------------------+ +------------------+ | Leaflet Map | <---> | Redux Store | <---> | Webservices | | (Interaktivität) | | (Status & Data) | | (IIS, .asmx) | +------------------+ +------------------+ +------------------+ ^ | v +------------------+ +------------------+ +-------------------+ | POI-Komponenten | <---> | Redux Slices | <---> | Next.js API-Routen| | (Add/Edit) | | (z. B. poiSlice) | | (Datenbank) | +------------------+ +------------------+ +-------------------+ ``` --- ## 🔁 Datenfluss (Beispiel: POI anzeigen) 1. Leaflet-Karte lädt bei `MapComponent` Mounting 2. Redux-Thunk `fetchPoiMarkersThunk` wird ausgelöst 3. Thunk ruft `fetchPoiDataService.js` (DB) oder Webservice (IIS) auf 4. Ergebnisse werden im Slice `readPoiMarkersStoreSlice` gespeichert 5. Komponenten lesen POI-Daten über `useSelector(...)` aus dem Store 6. POIs werden als Marker in Leaflet gesetzt --- ## 📁 Schlüsselfunktionen & Module | Bereich | Datei/Modul | Aufgabe | | ------------- | ----------------------------------------------- | ---------------------------------------- | | Kartenlogik | `MapComponent.js` | Zentrale Initialisierung und Layer-Logik | | Webservices | `services/webservice/` | Kommunikation mit TALAS V5 Webservice | | Datenbank | `services/database/` | Zugriff auf lokale Next.js-API & DB | | POIs | `AddPOIModal.js`, `PoiUpdateModal.js` | UI für POI-Erstellung & -Bearbeitung | | Redux | `redux/slices/`, `redux/thunks/`, `redux/store` | Globaler State, API-Steuerung | | Konfiguration | `.env.local`, `config.js`, dynamic URLs | IP, basePath, Ports | --- ## 🧩 Besonderheiten - **Konfigurierbarer basePath:** Pfad wie `/talas5` ist optional und kann per `NEXT_PUBLIC_BASE_PATH` in `.env.local` gesetzt werden. - **Rechteabhängige UI:** Funktionen (z. B. POI bearbeiten) basieren auf Benutzerrechten (`IdRight`) vom Server. - **Zentrale Komponentensteuerung:** Komponenten wie `MapLayersControlPanel` oder `CoordinatePopup` kontrollieren Layer & Interaktion. - **Kontextmenü-Logik:** Marker & Polylinien besitzen eigene Kontextmenüs – dynamisch zusammengesetzt und verwaltet. --- ## 📦 Versionierung & Builds - Version ist in `appVersion.js` definiert → wird über `NEXT_PUBLIC_APP_VERSION` eingeblendet - Build erfolgt via `npm run build`, Auslieferung über `.next/` - Nicht benötigte Dateien wie `__tests__`, `docs/`, `scripts/` etc. werden nicht in den Build aufgenommen --- ## 📚 Weiterführende Dokumentation - [`build-and-deploy.md`](./build-and-deploy.md) - [`env.local.schema.md`](./env.local.schema.md) - [`redux/slices/`](./redux/slices/) - [`services/webservice/`](./services/webservice/) --- ## Dynamische Layer-Verwaltung mit Redux ```mermaid flowchart TD %% Webservice subgraph Webservice A1[GisSystemStatic API] A2[GisStationsStaticDistrict API] end %% Redux subgraph Redux B1[fetchGisSystemStaticThunk] B2[fetchGisStationsStaticDistrictThunk] C1["gisSystemStaticSlice → selectGisSystemStatic"] C2["gisStationsStaticDistrictSlice → selectGisStationsStaticDistrict"] C3["mapLayersSlice → mapLayersVisibility"] end %% React subgraph React-Komponente D1[MapComponent.js] D2["useEffect: dynamische Layer"] D3["createAndSetDevices"] D4["layerRefs (useRef)"] D5["markerStates (useState)"] D6["useEffect: Sichtbarkeit prüfen"] D7["Map aktualisieren / add/remove"] D8["checkOverlappingMarkers"] end %% Datenfluss A1 --> B1 --> C1 --> D2 A2 --> B2 --> C2 --> D3 C3 --> D6 D2 --> D3 D3 --> D4 D3 --> D5 D5 --> D6 D6 --> D7 D6 --> D8 D7 --> D1 ``` --- Jetzt (dynamisch & Redux-basiert): MapComponent.js ruft folgenden Hook auf: js Copy Edit const { markerStates, layerRefs } = useDynamicDeviceLayers(map, GisSystemStatic, mapLayersVisibility, priorityConfig, oms); useDynamicDeviceLayers.js verarbeitet die GisSystemStatic-Liste: Jedes System (z. B. "TALAS", "ECI", "Cisco") bekommt einen eigenen Marker-Layer. Die Marker werden erstellt durch: js Copy Edit createAndSetDevices(...) // Systemweise Marker erzeugen createAndSetDevices.js: Filtert alle Stations aus staticDistrictData, deren System === IdSystem. Erstellt Marker für jedes Gerät. Bindet Popup, Kontextmenü, Styling, Bounce usw. Ruft setMarkersFunction(markers) auf → Übergibt die Marker zurück an den Hook. Der Hook speichert: js Copy Edit setMarkerStates((prev) => ({ ...prev, [Name]: newMarkers })); MapComponent.js hat dann: Zugriff auf alle Marker dynamisch über markerStates (ein Objekt mit Schlüssel = Systemname) Sichtbarkeit und OverlappingMarkerSpiderfier werden damit verarbeitet. --- 🔁 Die Geräte-Marker sind nicht mehr fest codiert, sondern werden dynamisch erzeugt anhand der Webservice-Daten GisSystemStatic. 🔄 Sichtbarkeit (Checkbox im Control Panel) löst ein Event visibilityChanged aus → MapComponent reagiert und rendert Marker neu. 🕷️ Überlappende Marker werden mit checkOverlappingMarkers + PlusRoundIcon verarbeitet. ```mermaid flowchart TD A1[MapComponent] --> B1[useDynamicDeviceLayers] B1 --> C1[loop über GisSystemStatic] C1 --> D1[createAndSetDevices] D1 --> E1[Filter stations aus Redux] E1 --> F1[erstelle Marker /Leaflet] F1 --> G1[Marker in LayerGroup einfügen] G1 --> H1[setMarkerStates im Hook] H1 --> A2[markerStates zurück nach MapComponent] A2 --> I1[Map aktualisiert Marker] A2 --> I2[checkOverlappingMarkers mit OMS] ``` --- 10.06.2025 # Datenfluss-Konzept: WebSocket ↔ Redux ↔ UI Dieses Dokument beschreibt den technischen Ablauf des Live-Datenflusses im NodeMap-Projekt, um neue Entwickler\:innen beim Onboarding zu unterstützen. ## ᵀᵃᵗᵉᵖᵏᴼᵏᴼᵉ: Architekturübersicht ```mermaid sequenceDiagram autonumber participant WS_Server as WebSocket Server participant WebService as TALAS WebService participant Browser participant ReduxStore as Redux Store participant UI as React Leaflet UI loop Alle 5 Sekunden WS_Server->>WebService: fetch endpointX alt Daten haben sich geändert WS_Server-->>Browser: emit 'endpointXUpdated' Browser->>ReduxStore: dispatch(fetchEndpointXThunk()) ReduxStore->>WebService: fetch endpointX WebService-->>ReduxStore: JSON response ReduxStore-->>UI: update Slice UI-->>User: re-render Markers else Keine Änderung WS_Server-->>WS_Server: keine Aktion end end ``` ## Beteiligte Komponenten ### WebSocket Server (`server.js`) - Ruft regelmäßig (`setInterval`) die Webservice-Endpunkte auf. - Erkennt Änderungen durch JSON-Vergleich (`JSON.stringify`). - Sendet WebSocket-Events nur bei echten Änderungen. ### Client: MapComponent - Hört auf `socket.on("endpointXUpdated")`. - Ruft dann gezielt den passenden Redux-Thunk auf (z. B. `fetchGisLinesStatusThunk`). ### Redux Store & Thunks - Jeder Endpunkt besitzt: - einen `Service` (API-Fetch) - einen `Thunk` (Redux-Logik) - einen `Slice` (State-Verwaltung) ### React UI (Leaflet Map) - Beobachtet relevante Redux-Slices via `useSelector()`. - Aktualisiert Marker, Tooltip und Popup über `createAndSetDevices()` und `useDynamicDeviceLayers()`. ## Beispiel-Endpunkte | Endpunktname | WebSocket Event | Redux Thunk | | --------------------------- | ---------------------------------- | --------------------------------------- | | `GisLinesStatus` | `GisLinesStatusUpdated` | `fetchGisLinesStatusThunk()` | | `GisStationsMeasurements` | `GisStationsMeasurementsUpdated` | `fetchGisStationsMeasurementsThunk()` | | `GisStationsStaticDistrict` | `GisStationsStaticDistrictUpdated` | `fetchGisStationsStaticDistrictThunk()` | | `GisStationsStatusDistrict` | `GisStationsStatusDistrictUpdated` | `fetchGisStationsStatusDistrictThunk()` | ## Vorteile - UI aktualisiert sich nur bei echten Datenänderungen → weniger Re-Renders. - Live-Synchronisation zwischen Datenquelle und Anzeige. - Skalierbar für beliebige Endpunkte. ## ToDo/Erweiterungen - Automatische Reconnect-Logik für WebSocket. - Anzeige des letzten Update-Zeitpunkts in UI. - Logging-UI für WebSocket-Messages zur Diagnose. --- > Letzte Änderung: `{{heutiges Datum}}` von Ismail Ali