314 lines
10 KiB
Markdown
314 lines
10 KiB
Markdown
<!-- /docs/architecture.md -->
|
||
|
||
# 🧠 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 |
|
||
|
||
---
|
||
|
||
## 🔄 Systemübersicht (Ablauf)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Browser
|
||
participant TALASweb
|
||
participant NodeMap
|
||
participant MySQL
|
||
|
||
Browser->>TALASweb: mapTypC.aspx?m=12&u=484
|
||
TALASweb-->>Browser: iFrame lädt NodeMap
|
||
Browser->>NodeMap: Liest m & u aus URL
|
||
NodeMap->>TALASweb: WebService-Requests (5 APIs)
|
||
NodeMap->>MySQL: API-Anfragen zu POIs, Geräten, Linien
|
||
NodeMap-->>Browser: Interaktive Karte anzeigen
|
||
|
||
```
|
||
|
||
---
|
||
|
||
## 🗺️ 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.development`,`.env.production`, `config.js`, dynamic URLs | IP, basePath, Ports |
|
||
|
||
---
|
||
|
||
## 🧩 Besonderheiten
|
||
|
||
- **Konfigurierbarer basePath:**
|
||
- **Konfigurierbarer basePath:**
|
||
Pfad wie `/talas5` ist optional und wird jetzt in `public/config.json` als `basePath` gepflegt
|
||
werden.
|
||
Die Konfiguration erfolgt je nach Umgebung über:
|
||
|
||
- `.env.development` für lokale Entwicklung
|
||
- `.env.production` für produktiven Einsatz
|
||
|
||
- **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 wird mit Husky Bibliothek automatisch erhöhert in `scripts/bumpVersion.js`
|
||
|
||
---
|
||
|
||
## 📚 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
|
||
|
||
---
|
||
|
||
[Zurück zur Übersicht](README.md)
|