kvmobile: Unterschied zwischen den Versionen

Aus kvwmap
Wechseln zu: Navigation, Suche
(Background-Layer)
(Ablauf der Synchronisation)
 
(38 dazwischenliegende Versionen des gleichen Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
= Aktueller Stand =
+
= Übersicht =
kvmobile funktioniert zur Zeit für die Erfassung von Punktobjekten mit Bildern und Position. Die aktuellste Version kann man im Github finden unter [https://github.com/pkorduan/kvmobile/tree/develop/platforms/android/build/outputs/apk].
+
Für die Nutzung der Daten von kvwmap auf kleineren Endgeräten wie Handy's oder Tablets und auch ohne ständige Internet-Verbindung wurde die APP kvmobile entwickelt.
Für die Verwendung mit kvwmap-Server müssen einige Voraussetzungen erfüllt werden, die [[Plugins#Mobile|hier]] beschrieben sind.
+
Gerade sind wir dabei die Anwendung für die Bearbeitung von Linien und Flächen weiterzuentwickeln. Dabei setzen wir auf die Leaflet Draw API siehe [https://leaflet.github.io/Leaflet.draw/docs/leaflet-draw-latest.html] und [https://leaflet.github.io/Leaflet.draw/docs/examples/full.html Demo].
+
  
= Ankündigung =
+
Die APP wird als HTML-CSS-JavaScript Anwendung ständig weiterentwickelt. Der Source Code ist auf [https://github.com/pkorduan/kvmobile github] verfügbar.
kvwmap bekommt einen kleinen Ableger zur Erfassung Geodaten mit mobilen Endgeräten. Wir haben dieses Jahr gestartet eine entsprechende App zu programmieren, mit der Haltestellen erfasst werden können. Die App soll offline funktionieren und die Daten mit der Datenbank des Web-GIS kvwmap synchronisieren können. Der Zugriff auf die Datenbank erfolgt über das Web-GIS kvwmap. Nutzer der App müssen sich also an einer Stelle in kvwmap authotisieren und können dann Daten eines in der Stelle zugeordneten Layers austauschen. Eine Hintergrundkarte kann für das Bearbeitungsgebiet vor der Arbeit im freien Einsatz gecached werden. Wie das passiert ist aber noch nicht genau festgelegt. Denkbar wäre das cachen von Rasterkarten aber auch die Verwendung von Vektor-Tiles, die dann aber natürlich in der App gerändert werden müssten. Da viele sicher das Luftbild im Hintergrund benötigen, wird man wohl um das cachen von Rasterkacheln in der mobilen App nicht drum herum kommen. kvmobile unterscheidet sich jedenfalls von dem ursprünglichen Ansatz einer mobilen Nutzung von kvwmap dadurch, dass kein Server mehr auf dem mobilen Endgerät notwendig ist. Die Ressourcen des Endgerätes (Handy, Tablet, etc.) werden über die [http://cordova.apache.org Plattform Cordova] hergestellt. Cordova stellt eine API zur Verfügung mit der man mit JavaScript auf mobile Ressourcen, z.B. die Camera, GPS, Speicher, Internet, etc. zugreifen kann. Wobei hier nicht der Browser mit JavaScript auf die Ressourcen zugreift, sondern man programmiert das in seiner Entwicklungsumgebung nur so als wäre es eine Web-Anwendung. Cordava erzeugt dann in einem build Prozess aus dieser Web-Anwendung eine App für die jeweilige Zielplattform. (zum Test zunächst Android 25)
+
  
kvmobile ist eine App, die zunächst für Android entwickelt wird. Da kvmobile auf der Platform cordova entwickelt wird, können später aber auch Apps für andere Betriebssysteme wie iOS oder BlackBerry kompiliert werden.
+
Das Changelog befindet sich in der Readme die auf github einsehbar ist.
  
Es ist geplant die Anwendung in 2017 für Tests dem Auftraggeber zur Verfügung zu stellen, damit sie in 2018 erstmalig in der Praxis eingesetzt werden kann.
+
Die Bugfixes werden [[Bug_kvmobile | hier]] beschrieben.
Ist die Einführung erfolgreich, werden weitere Funktionen hinzugefügt. Das Ziel ist die Erfassung beliebiger Daten von Layern, die in kvwmap als mobile Layer freigegeben wurden. Dabei sollen die Erfassungsformulare entsprechend der Definition im Attribut-Editor auch generisch auf der App erscheinen.
+
 
Ein weiterer Schritt wäre die wahlweise Erfassung von verschiedenen Layern in einer einzelnen App. Derzeit kann in der App erstmal nur ein Layer erfasst werden.
+
Die APP wird über Cordova für das Android Betriebssystem gebaut und verschiedene Versionen als [https://gdi-service.de/public/kvmobile/ APK-Datei zum Download] angeboten.
 +
 
 +
Serverseitig ist das [[Plugins#Mobile|Plugin mobile]] erforderlich. Das Plugin ermöglicht das Arbeiten im Offline-Modus in kvmobile sowie die Synchronisierung von Geodaten und Fotos zwischem dem Server und dem mobilen Endgerät.
 +
 
 +
Die APP kvmobile verbindet sich über Benutzername und Passwort mit einer Stelle in einem kvwmap und stellt dem Nutzer die in der Stelle verfügbaren Layer mobile zur Verfügung. Ohne kvwmap funktioniert die APP also derzeit nicht. kvmobile muss sich erst mindestens ein mal mit dem Server verbunden haben um die dortigen Layer auf das Endgerät zu holen. Anschließend kann der Nutzer offline arbeiten. Erst wenn wieder Internet vorhanden ist können die Daten mit dem Server synchronisiert werden.
 +
 
 +
Für die Synchronisierung wird ein Versionierungskonzept mit uuid's und Deltas verwendet. Alle Daten haben eine eindeutige uuid und eine Versionsnummer pro Layer. Wenn kvmobile sich die Daten erstmalig holt, werden diese so wie sie sind in der sqlite Datenbank in kvmobile gespeichert. Werden Änderungen durchgeführt, egal ob auf Server oder Client werden die dazugehörigen SQL-Requests als sogenannte Deltas mit hochgezählter Versionsnummer gespeichert. Bei der Synchronisierung schickt kvmobile seine Deltas zum Server, die dort ausgeführt werden, und holt sich die Deltas vom Server die vom Zeitpunkt der letzten Synchronisierung bis jetzt angelegt wurden und führt diese auf dem Client aus. In der APP kvmobile wird die neue Version gesetzt, damit sie weiß bis zu welcher Änderungen die Deltas schon ausgeführt wurden.
 +
 
 +
Werden die selben Objekte von verschiedenen kvmobil APP's gleichzeitig bearbeitet, überschreibt immer die letzte Synchronisierung vorherige. Wenn die Abstände zwischen den Synchronisierungen größer sind, z.B. weil schlechtes Internet verfügbar ist, ist es also ratsam die Daten die bearbeitet werden sollen vorher in Bereiche für verschiedene Mitarbeiter aufzuteilen.
 +
 
 +
Auf dieser Seite werden nur die Funktionen der APP beschrieben. Die APP wird ständig weiterentwickelt. Es ist möglich, dass sich Bilder und Inhalte dieser Dokumentation vom aktuellen Stand der APP unterscheidet.
  
 
= Installation auf Android =
 
= Installation auf Android =
Zeile 53: Zeile 60:
 
Unter dem Menüpunkt Einstellungen [[Datei:Button_Einstellungen.png|25px|||Button Einstellungen]] können die Farben für die Marker, Linien und Polygone angegeben werden, die mit dem Attribut Status klassifiziert sind. Im Abschnitt Farben [[Datei:Einstellungen_Farben.png||150px||Einstellungen_Farben.png]] kann man auf das Kästchen klicken und die Farbe aus einem Color-Picker [[Datei:Farbauswahl.png||150px||Farbauswahl.png]] wählen und übernehmen.
 
Unter dem Menüpunkt Einstellungen [[Datei:Button_Einstellungen.png|25px|||Button Einstellungen]] können die Farben für die Marker, Linien und Polygone angegeben werden, die mit dem Attribut Status klassifiziert sind. Im Abschnitt Farben [[Datei:Einstellungen_Farben.png||150px||Einstellungen_Farben.png]] kann man auf das Kästchen klicken und die Farbe aus einem Color-Picker [[Datei:Farbauswahl.png||150px||Farbauswahl.png]] wählen und übernehmen.
  
= Changemanagement und Historisierung =
+
= Changemanagement ab Version 1.18.0 =
 +
Seit der Version 1.17.0 werden die Änderungen (deltas) nicht mehr für jeden Layer in separaten deltas-Tabellen gespeichert, sondern nur noch in einer und zwar in der Reihenfolge in der die Änderungen auf dem Client oder dem Server ausgeführt werden. Wenn die Deltas dann auch in der Reihenfolge wie sie angelegt wurden auf anderen Geräten ausgeführt werden, wird die referenzielle Integrität eingehalten.
 +
 
 +
== Tabllen auf dem Server ==
 +
Auf dem Server gibt es die Tabelle public.syncs_all, in der registriert wird welche Synchronisierungen stattgefunden haben.
 +
* id serial - Eine fortlaufende Nummer der stattgefundenen Synchronisierungen.
 +
* client_id character varying - Eindeutiger Identifikator vom Client. Änderungen vom Server haben hier keinen Eintrag.
 +
* username character varying - Vor und Nachname des angemeldeten Nutzers.
 +
* client_time timestamp without time zone - Zeitstempel wann die Synchronisierung vom Client aus angestoßen worden ist.
 +
* pull_from_version integer - Version des Deltas ab dem Deltas für den Client gelesen wurden.
 +
* pull_to_version integer - Höchste Versionsnummer der Deltas die für den Client gelesen wurden.
 +
 
 +
pull_to_version kennzeichnet für den Client bis zu welchen Änderung er Deltas erhalten hat und damit den letzten Stand der Änderungen auf dem Server. Bei der nächsten Synchronisierung holt er sich nur Änderungen mit Versionsnummern die größer sind als diese Version.
 +
 
 +
Die Deltas-Tabelle public.deltas_all, die auf dem Server vorhanden ist, enthält folgende Attribute:
 +
* uuid integer - Eindeutige Nummer des Datensatzes den das Delta betrifft.
 +
* client_id character varying - Eindeutiger Identifikator vom Client. Änderungen vom Server haben hier keinen Eintrag.
 +
* sql text - Das Sql-Statement der Änderung in der Tabelle
 +
* schema_name character varying - Der Name des Schemas in der die Tabelle liegt
 +
* table_name character varying - Der Name der Tabelle
 +
* version integer - Die Version des Deltas. Gibt die Reihenfolge an, in der die Änderungen ausgeführt wurden und auf andern Geräten ausgeführt werden müssen.
 +
* action character varying - [insert | update | delete] Die Aktion die im SQL ausgeführt wurden, bzw. werden soll. Bei type = image steht hier nur insert oder delete, weil Bilder nicht geändert werden können, nur gelöscht und neu angelegt.
 +
* action_time timezone without time zone Die Zeit zu der die Aktion ausgeführt wurde.
 +
Die Deltas-Tabelle auf dem Client hat zusätzlich die Spalte:
 +
* type character varying - [sql | image] Ob es sich um eine Änderung des Datensatzes handelt oder um das Erzeugen oder Löschen von Bildern
 +
und dafür keine Spalte client_id
 +
 
 +
Im Folgenden wird beschrieben wie die Synchronisierung zwischen Server und Clients abläuft:
 +
 
 +
== Ablauf der Synchronisation ==
 +
1) Der Client fragt als erstes die Stellen beim Server ab
 +
* go string = mobile_get_stellen
 +
* passwort string
 +
* login_name string
 +
Als Antwort bekommt der Client eine Liste der Stellen zurück:
 +
  {
 +
    "success": true,
 +
    "user_id": "2",
 +
    "user_name": "Peter Korduan",
 +
    "stellen": [{
 +
      "ID": "54",
 +
      "Bezeichnung": "Administration Pet Dev",
 +
      "dbname": "kvwmapsp_pet_dev",
 +
      "west":6.98487,
 +
      "south":36.77034,
 +
      "east":17.69165,
 +
      "north":68.35926,
 +
      "startCenterLat":52.5648,
 +
      "startCenterLon":12.33826,
 +
      "layer_params": {
 +
        "jahr": {
 +
          "id": "1",
 +
          "key": "jahr",
 +
          "alias":"Jahr",
 +
          "default_value":"15",
 +
          "options_sql": "SELECT series.jahr AS value, '20' || series.jahr AS output FROM generate_series(15,40) AS series(jahr)",
 +
          "options": [{
 +
            "value":"15",
 +
            "output":"2015"
 +
          }, {
 +
            "value":"16",
 +
            "output":"2016"
 +
          }, {
 +
            "value": "17",
 +
            "output":"2017"
 +
          }
 +
        }
 +
      },
 +
      { Nächste Stelle }
 +
    ]
 +
  }
 +
 
 +
2) Nach dem Laden der Stellen lädt die App die Metadaten der Layer mit dem Case go=mobile_get_layers. Die Anfrage erfolgt mit folgenden Parametern:
 +
* go string = mobile_get_layers
 +
* Stelle_ID int
 +
* passwort string
 +
* login_name string
 +
Als Antwort bekommt der Client ein Array von Layerdefinitionen und die höchste Version der Deltas. Die Layerdefinition enthalten die Attribute und Klassendefinitionen:
 +
Result:
 +
  {
 +
    "success": true,
 +
    "layers": [ {
 +
      "id": "831",
 +
      "title":"Baumkataster Baum",
 +
      "alias":null,
 +
      "id_attribute":"uuid",
 +
      "name_attribute":"baumnummer",
 +
      "classitem" : "id_nr",
 +
      "transparency" : 80,
 +
      "geometry_attribute" : "the_geom",
 +
      "geometry_type" : 0,
 +
      "table_name" : "baum",
 +
      "schema_name" : "kob",
 +
      "query": "SELECT ... ",
 +
      "filter":"",
 +
      "document_path":"\/var\/www\/data\/bilder\/baumkataster\/","vector_tile_url":"",
 +
      "privileg":"2",
 +
      "drawingorder":"91000",
 +
      "legendorder":null,
 +
      "sync":"1",
 +
      "version":"1.0.0",
 +
      "attributes": [ Liste der Attribute des Layers ],
 +
      "tables" : [ Liste der vom Layer verwendeten Tabellen],
 +
      "classes" : [ Liste der vom Layer verwendeten Klassen]
 +
    }, {
 +
      ...
 +
    }],
 +
    "last_delta_version" : 109
 +
  }
 +
 
 +
Die Version des Layers kennzeichnet den Stand des Datenmodells. Später kommen bei mobile_sync_all alle Versionen der Layer zurück und können im Client abgeglichen werden. Wenn sich eine Layerversion geändert hat, müssen alle Layerdefinitionen und Daten neu geladen werden.
 +
 
 +
3) Im nächsten Schritt werden die Daten der Layer abgefragt. Bei der Abfrage der Daten wird last_delta_version als Filter angewendet.
 +
  version < last_delta_version
 +
Die Daten werden mit dem Case go=mobile_get_data abgefragt.
 +
* go string = mobile_get_data
 +
* Stelle_ID int
 +
* passwort string
 +
* login_name string
 +
* selected_layer_id int $layer_id,
 +
* last_delta_version int $last_delta_version
 +
 
 +
4) Die nächste Synchronisierung des Clients mit dem Server erfolgt ebenfalls mit last_delta_version. Dadurch bekommt der Client nur die Deltas von Änderungen geschickt, die er noch nicht kennt. Bei der Synchronisierung bekommt der Client erneut die höchste abgefragte Version vom Server mitgeteilt und weiß somit wieder bis wo er bereits gepullt hat.
 +
 
 +
Für die Synchronisierung sammelt der Client alle Deltas aus seiner eigenen deltas_all Tabelle ein die vom type sql sind und sendet eine Anfrage an den Server mit folgenden Parametern:
 +
* go string = mobile_sync_all - Anwendungsfall auf dem Server.
 +
* login_name string - Longin-Name des Nutzers.
 +
* passwort string - Das Passwort des Nutzers.
 +
* Stelle_ID integer - Die ID der Stelle in der die Synchronisierung statt finden soll.
 +
* client_id string - Die eindeutige Nummer des Gerätes. Wird von kvmobile mit cordova device plugin abgefragt.
 +
* client_time timestamp without time zone - Zeitstempel der Zeit wenn der Client die Anfrage schickt. Wird auf dem Client erzeugt und im Server in der sync_all Tabelle zum Vorgang abgelegt.
 +
* last_delta_version - Die Version bis die der Client die Daten schon hat. Vom Server werden alle abgefragt die älter sind als last_delta_version.
 +
* client_deltas - Ein Objekt mit einem Array von Deltas (rows) und einem Array der letzten Delta-Versionen (lastDeltaVersions). Beschreibung der Datenstuktur der Deltas siehe unten oder in der Dokumentation zur Tabelle deltas_all. Die Datenstruktur der Deltas die zum Server geschickt werden sieht wie folgt aus:
 +
  {
 +
    "rows" : [
 +
      {
 +
        "uuid" : "003de0b8-2185-4380-a547-f4c1634ff1sd",
 +
        "sql" : "INSERT INTO test.punkte (uuid, bilder, bilder_updated_at, updated_at_client, updated_at_server, user_name, status, name, is_active, created_at, created_from, user_id, stelle_id, geom , version) VALUES ('003de0b8-2185-4380-a547-f4c1634ff1sd', null, null, '2024-03-01 10:35:12', '2024-02-01 09:39:42', 'Peter Korduan', '0', 'Testfläche 4', 'f', '2024-02-01 07:12:32', 'Peter Korduan', '2', '54', '010100004FED29DFF3D28404AD6D20E610000054FED29DF4FED29DFF3D28404AD6DF3D28404AD6D4FED29DFF3D28404AD6D2AC68094B40', 3)",
 +
        "schema_name" : "test",
 +
        "table_name" : "polygone",
 +
        "action" : "update",
 +
        "action_time" : "2024-03-01 10:35:12",
 +
        "version" : 15
 +
      }, {
 +
        "uuid" : "003de0a8-2185-4380-a547-f4c1634ff9bd",
 +
        "sql" : "INSERT INTO test.punkte (uuid, bilder, bilder_updated_at, updated_at_client, updated_at_server, user_name, status, name, is_active, created_at, created_from, user_id, stelle_id, version, geom , version) VALUES ('4992613c-7a0a-463d-bb40-99a1345486bd', '{"/var/www/data/kvwmap_pet_dev/punkte/1650531377364.jpg&original_name=1650531377364.jpg","/var/www/data/kvwmap_pet_dev/punkte/1650531386496.jpg&original_name=1650531386496.jpg"}', '2024-03-01 10:39:05', '2024-03-01 10:39:05', '2024-03-01 10:39:05', 'Peter Korduan', '0', 'Test 4', 'f', '2024-03-01 10:39:05', 'Peter Korduan', '2', '54', '0101000020E610000054FED29DFF3D28404AD6D2AC68094B40' , 4)",
 +
        "schema_name" : "test",
 +
        "table_name" : "punkte",
 +
        "action" : "insert",
 +
        "action_time" : "2024-03-01T10:39:05",
 +
        "version" : 16
 +
      }, {
 +
        ... ein weiteres Deltas Objekt
 +
      }
 +
    ]
 +
  }
 +
 
 +
5) Zunächst werden in der Funktion mobile_sync_all_parameter_valide die Parameter geprüft.
 +
* Die Attribute login_name und passwort werden schon bei der Authentifizierung geprüft.
 +
* Das Attribute go und Stelle_Id werden bei der Authorisierung geprüft.
 +
Im Anwendungsfall go = mobile_sync_all wird geprüft:
 +
* ob client_time existiert und nicht leer ist.
 +
* ob client_id übergeben wurde und nicht leer ist.
 +
* ob last_delta_version übergeben wurde und nicht leer ist.
 +
* ob client_deltas übergeben wurden
 +
Bis hierhin werden die fehlenden Eigenschaften eingesammelt. Danach wird geprüft:
 +
* es sich um ein Objekt handelt
 +
* ob es eine Eigenschaft rows hat
 +
* Wenn es mindestens eine Zeile gibt ob in der ersten Zeile:
 +
** version übergeben wurde und nicht leer ist
 +
** sql übergeben wurde und nicht leer ist
 +
** schema_name übergeben wurde und nicht leer ist
 +
** table_name übergeben wurde und nicht leer ist
 +
 
 +
Die Validierung des Objektes schlägt bei der ersten Bedingung die nicht zutrifft fehl und bricht mit dem setzen einer Fehlermeldung ab.
 +
Zum Schluss der Prüfung wird wenn mindestens ein Fehler aufgetreten ist der Synchronisierungsvorgang abgebrochen und ein JSON-Objekt mit success: false und einer Meldung zurück an den Client geschickt:
 +
  {
 +
    "success" : false,
 +
    "err_msg" : "Eine entsprechende Fehlermeldung, die benennt was gefehlt hat."
 +
  }
 +
 
 +
6) Falls der Client Deltas mitgeschickt hat, werden diese, falls die Berechtigung dazu besteht, nacheinander auf dem Server ausgeführt. Hat der Nutzer keine Berechtigung die Deltas auf dem Server auszuführen, werden diese als failed_deltas an den Client zurückgeschickt.
 +
 
 +
Bei Update-Statements wird vorher abgefragt ob es in der Tabelle die geupdated werden soll einen Datensatz mit der gleichen uuid gibt, mit jüngeren updated_at_client oder updated_at. Wenn ja, werden die Deltas mit jüngerem Datum in action_time abgefragt um sie nach der Ausführung des Delta vom Client auszuführen.
 +
 
 +
7) Wenn es jüngere Deltas gab werden sie nach dem Ausführen des Delta vom Client ausgeführt.
 +
 
 +
8) Unabhängig davon ob Deltas vom Client auf dem Server ausgeführt wurden oder nicht, werden alle Deltas abgefragt
 +
* dessen Versionsnummer größer als die last_delta_version vom Client ist
 +
* keine insert Deltas mit der client_id vom Client
 +
Die gefundenen Deltas werden sortiert nach Version zum Client zurückgeschickt und ebenso die neue last_client_version.
 +
 
 +
Die Datenstruktur, die an den Client im JSON-Format zurückgeschickt wird, sieht wie folgt aus:
 +
* success boolean - True bei Erfolgt und false bei Misserfolg. Unabhängig ob failed deltas zurückkommen.
 +
* last_delta_version - Die Version bis zu der die Änderungen abgefragt wurden.
 +
* layer_versions - Die Versionen der Layer.
 +
* deltas Delta[] - Array mit Objekten der Struktur Delta, siehe unten
 +
* failedDeltas Delta[] - Array mit Objekten der Struktur Delta, bei denen Fehler auftraten. Zusätzlich haben diese Deltas die Eigenschaft err_msg.
 +
* log string - Ein Text der beschreibt was auf dem Server abgelaufen ist.
 +
 
 +
Ein Beispiel für die Datenstruktur des Response:
 +
  {
 +
    "success" : false,
 +
    "last_delta_version" : 18,
 +
    "deltas" : [...],
 +
    "failedDeltas" : [...],
 +
    "log" : "Es konnte keine Verbindung zur Datenbank hergestellt werden."
 +
  }
 +
 
 +
 
 +
Die Datenstruktur vom Delta im Response sieht so aus wie die Struktur der Tabelle deltas_all, außer dass bei failed_deltas die Eigenschaft err_msg vom Type text dazu kommt. Hier ein Beispiel:
 +
  {
 +
    "uuid" : "003de0a8-2185-4380-a547-f4c1634ff9bd",
 +
    "client_id": "a4a46b6fcfadb2c2",
 +
    "sql" : "INSERT INTO test.punkte (uuid, bilder, bilder_updated_at, updated_at_client, updated_at_server, user_name, status, name, is_active, created_at, created_from, user_id, stelle_id, version, geom , version) VALUES ('4992613c-7a0a-463d-bb40-99a1345486bd', '{"/var/www/data/kvwmap_pet_dev/punkte/1650531377364.jpg&original_name=1650531377364.jpg","/var/www/data/kvwmap_pet_dev/punkte/1650531386496.jpg&original_name=1650531386496.jpg"}', '2024-03-01 10:39:05', '2024-03-01 10:39:05', '2024-03-01 10:39:05', 'Peter Korduan', '0', 'Test 4', 'f', '2024-03-01 10:39:05', 'Peter Korduan', '2', '54', '20', '0101000020E610000054FED29DFF3D28404AD6D2AC68094B40' , 2)",
 +
    "schema_name" : "test",
 +
    "table_name" : "punkte",
 +
    "version" : 17,
 +
    "action" : "insert",
 +
    "action_time" : "2024-03-01 10:39:05",
 +
    "err_msg?" : "Fehler beim Eintragen des Datensatzes mit der ID: 003de0a8-2185-4380-a547-f4c1634ff9bd in die Tabelle test.punkte. Meldung: ..."
 +
  }
 +
 
 +
= Changemanagement und Historisierung vor der Version 1.18.0 =
 
In kvmobile werden Änderungen der Reihe nach als SQL-Statements in die Deltas Tabelle geschrieben. Das führt zu zwei Problemen:
 
In kvmobile werden Änderungen der Reihe nach als SQL-Statements in die Deltas Tabelle geschrieben. Das führt zu zwei Problemen:
 
# Wenn bei einer Synchronisierung der Daten mit dem Server ein Delta-Statement fehl schlägt, z.B. weil ein Serverseitiges Constraint nicht erfüllt wird, müssen alle folgenden Deltas abgebrochen werden, da sonst inkonsistente Datenbestände entstehen können. Das gilt besonders wenn verknüpfte Tabellen verwendet werden.
 
# Wenn bei einer Synchronisierung der Daten mit dem Server ein Delta-Statement fehl schlägt, z.B. weil ein Serverseitiges Constraint nicht erfüllt wird, müssen alle folgenden Deltas abgebrochen werden, da sonst inkonsistente Datenbestände entstehen können. Das gilt besonders wenn verknüpfte Tabellen verwendet werden.
 
# Wenn ein Datensatz gelöscht wird, müssen vorherige Änderungen nicht ausgeführt werden und im Client erzeugte neue Datensätze können aus dem Delta raus und es muss dazu kein INSERT Delta angelegt werden.
 
# Wenn ein Datensatz gelöscht wird, müssen vorherige Änderungen nicht ausgeführt werden und im Client erzeugte neue Datensätze können aus dem Delta raus und es muss dazu kein INSERT Delta angelegt werden.
Um diese Probleme löschen zu können, wird eine einfache Historisierung eingeführt, die im folgenden ausführlich beschrieben wird.
+
Um diese Probleme lösen zu können, wird eine einfache Historisierung eingeführt, die im folgenden ausführlich beschrieben wird.
 
Im Client bekommen die Datentabellen alle eine zusätzliche Spalte endet, die nicht mit synchronisiert wird. Wenn dort ein Zeitstempel drin steht ist der Datensatz untergegangen, sonst nicht.
 
Im Client bekommen die Datentabellen alle eine zusätzliche Spalte endet, die nicht mit synchronisiert wird. Wenn dort ein Zeitstempel drin steht ist der Datensatz untergegangen, sonst nicht.
 
Der Ausgangszustand nach einer Synchronisation ist eine leere Deltas-Tabelle und alle Datensätze haben im Attribut endet = NULL. Bei Änderungen im Client passiert folgendes:
 
Der Ausgangszustand nach einer Synchronisation ist eine leere Deltas-Tabelle und alle Datensätze haben im Attribut endet = NULL. Bei Änderungen im Client passiert folgendes:

Aktuelle Version vom 13. September 2024, 12:03 Uhr

Übersicht

Für die Nutzung der Daten von kvwmap auf kleineren Endgeräten wie Handy's oder Tablets und auch ohne ständige Internet-Verbindung wurde die APP kvmobile entwickelt.

Die APP wird als HTML-CSS-JavaScript Anwendung ständig weiterentwickelt. Der Source Code ist auf github verfügbar.

Das Changelog befindet sich in der Readme die auf github einsehbar ist.

Die Bugfixes werden hier beschrieben.

Die APP wird über Cordova für das Android Betriebssystem gebaut und verschiedene Versionen als APK-Datei zum Download angeboten.

Serverseitig ist das Plugin mobile erforderlich. Das Plugin ermöglicht das Arbeiten im Offline-Modus in kvmobile sowie die Synchronisierung von Geodaten und Fotos zwischem dem Server und dem mobilen Endgerät.

Die APP kvmobile verbindet sich über Benutzername und Passwort mit einer Stelle in einem kvwmap und stellt dem Nutzer die in der Stelle verfügbaren Layer mobile zur Verfügung. Ohne kvwmap funktioniert die APP also derzeit nicht. kvmobile muss sich erst mindestens ein mal mit dem Server verbunden haben um die dortigen Layer auf das Endgerät zu holen. Anschließend kann der Nutzer offline arbeiten. Erst wenn wieder Internet vorhanden ist können die Daten mit dem Server synchronisiert werden.

Für die Synchronisierung wird ein Versionierungskonzept mit uuid's und Deltas verwendet. Alle Daten haben eine eindeutige uuid und eine Versionsnummer pro Layer. Wenn kvmobile sich die Daten erstmalig holt, werden diese so wie sie sind in der sqlite Datenbank in kvmobile gespeichert. Werden Änderungen durchgeführt, egal ob auf Server oder Client werden die dazugehörigen SQL-Requests als sogenannte Deltas mit hochgezählter Versionsnummer gespeichert. Bei der Synchronisierung schickt kvmobile seine Deltas zum Server, die dort ausgeführt werden, und holt sich die Deltas vom Server die vom Zeitpunkt der letzten Synchronisierung bis jetzt angelegt wurden und führt diese auf dem Client aus. In der APP kvmobile wird die neue Version gesetzt, damit sie weiß bis zu welcher Änderungen die Deltas schon ausgeführt wurden.

Werden die selben Objekte von verschiedenen kvmobil APP's gleichzeitig bearbeitet, überschreibt immer die letzte Synchronisierung vorherige. Wenn die Abstände zwischen den Synchronisierungen größer sind, z.B. weil schlechtes Internet verfügbar ist, ist es also ratsam die Daten die bearbeitet werden sollen vorher in Bereiche für verschiedene Mitarbeiter aufzuteilen.

Auf dieser Seite werden nur die Funktionen der APP beschrieben. Die APP wird ständig weiterentwickelt. Es ist möglich, dass sich Bilder und Inhalte dieser Dokumentation vom aktuellen Stand der APP unterscheidet.

Installation auf Android

  • Aktuelle Version der apk-Datei von [1] auf dem mobilen Endgerät über einen Browser runterladen und lokal auf dem Gerät speichern, z.B. unter Downloads.
  • Heruntergeladene Daten öffnen.
  • Bestätigen, dass kvmobile installiert werden soll.
  • App wurde installiert. Öffnen
  • Authentifizierung mit Fingerabdruck, Muster oder Code. Je nachdem was auf dem Gerät eingestellt ist.
  • Ok zur Meldung "Wählen Sie eine Konfiguration aus und Stellen die Zugangsdaten zum Server ein"
  • Nach unten scrollen und Einstellung Konfiguration öffen und die gewünschte Konfiguration auswählen.

Konfiguration auswählen

  • Die App startet neu. Noch mal authentifizieren und dann Hinweis bestätigen.
  • In Einstellungen unter Server den Nutzernamen und das Passwort eintragen.

Benutzername und Passwort eingeben für Authentifizierung am Server

  • Button "Stellen abfragen" anklicken
  • Stelle auswählen und Button "Einstellungen Speichern" klicken

Stelle auswählen und Einstellungen speichern

  • Unter Layer Button "Layer abfragen" anklicken"

Layer abfragen

  • Warten bis alle Layer geladen sind. Die Liste der Layer wird angezeigt.
  • Einen Layer zur Bearbeitung auswählen

Layer auswählen

  • Oben Listen- oder Kartenansicht auswählen.

Karte anzeigen Liste anzeigen

Offline Daten

Layer

kvmobile ist so konzipiert, dass Daten, die in kvwmap als PostGiS-Layer definiert sind und bei denen die sync-Option in den Layereinstellungen ausgewählt sind, nach dem Laden in der App offline bearbeitet werden können. Derartige Daten werden in kvmobile als Layer bezeichnet. Jeder Layer in kvmobile entspricht einem Layer in kvwmap. Zur Verarbeitung in kvmobile werden die Daten von der App in eine sqlite Datenbank-Tabelle geschrieben, die lokal auf dem Endgerät liegt und die gleiche Datenstruktur wie auf dem Server hat. Alle Änderungen und neue Daten werden zunächst in diese sqlite Datenbank geschrieben. Alle dafür verwendeten SQL-Statement (Deltas) werden ebenfalls in der Datenbank gespeichert. Hat der kvmobile Nutzer eine Internetverbindung, kann er eine Syncronisation aufrufen. Dabei werden die Deltas an den Server übermittelt dort ausgeführt und die auf dem Server hinterlegten Deltas an den Client gesendet, der diese wiederum auch ausführt.

Bilder werden beim Syncronisieren nicht automatisch mit übertragen, da dies sehr viele sein können. Sind in einem Layer viele Bilder vorhanden und ist im Projektgebiet schlechter Internet-Empfang, Empfiehlt es sich die Bilder vor dem offline-arbeiten über eine USB-Verbindung auf das Endgerät zu übertragen. Ansonsten werden die einzelnen Bilder einmalig nachgeladen wenn man ein Formular zur Ansicht aufmacht in dem ein oder mehrere Bilder zum Datensatz gehören. Die Bilder stehen dann lokal auf dem Endgerät zur Verfügung. Macht man selber neue Bilder, werden diese auch zunächst lokal auf dem Endgerät gespeichert und dazu Image-Deltas angelegt. Hat der Nutzer Internet, kann er die Funktion Bild-Upload aufrufen und neuen lokalen Bilder werden zum Server gesendet. Dazu sollte man eine gute Internetverbindung z.B. mit WLAN haben.

Overlay

In kvmobile werden auch die Layer aus kvwmap geladen, die den Status sync nicht gesetzt haben. Um sie von den Layern zu unterscheiden heißen sie in kvmobile Overlays. Die Overlays können in kvmobile nur in der Karte angezeigt werden. Derzeig in Version 1.7.13 können die Sachdaten der Overlays noch nicht abgefragt oder angezeigt werden. Das ist noch in Entwicklung.

Background-Layer

Als Hintergrundkarten werden Kacheln verwendet. Standardmäßig wird hier die ORKA von MV eingesetzt. Wird die App aufgerufen mit Inernetverbindung ist die Hintergrundkarte überall zu sehen. Will man jedoch offline Arbeiten und die Hintergrundkarten auch nutzen sollten man sich die Kacheln von seinem Projektgebiet vorher herunterladen und auch per USB auf das Endgerät laden. Für einige Gebiete sind vorgefertigte Kacheln in tar.gz Dateien gepackt worden in Zukunft wäre jedoch ein entsprechender Kacheldownloader sindvoll. Eine entsprechende Anfrage für so ein Kachel-Download-Tool wurde an HRO gestellt. Es wurde auch ein Dienst auf gdi-service.de eingerichtet über den Kacheln herunter geladen werden können. Der Konfigurationsdatei configurations.js sind für jeden Landkreis in dem bisher kvmobile genutzt wird verschiedene Hintergrundkarten konfiguriert. Der Layer Vektorkacheln offline z.B. läd die Kacheln für die Ausdehnung der Anwendung und cached diese auf dem mobilen Endgerät. Dazu ist die Funktion Einstellungen > Offline-Karten > herunterladen anzuwenden.

Einstellungen

Unter dem Menüpunkt Einstellungen Button Einstellungen können die Farben für die Marker, Linien und Polygone angegeben werden, die mit dem Attribut Status klassifiziert sind. Im Abschnitt Farben Einstellungen_Farben.png kann man auf das Kästchen klicken und die Farbe aus einem Color-Picker Farbauswahl.png wählen und übernehmen.

Changemanagement ab Version 1.18.0

Seit der Version 1.17.0 werden die Änderungen (deltas) nicht mehr für jeden Layer in separaten deltas-Tabellen gespeichert, sondern nur noch in einer und zwar in der Reihenfolge in der die Änderungen auf dem Client oder dem Server ausgeführt werden. Wenn die Deltas dann auch in der Reihenfolge wie sie angelegt wurden auf anderen Geräten ausgeführt werden, wird die referenzielle Integrität eingehalten.

Tabllen auf dem Server

Auf dem Server gibt es die Tabelle public.syncs_all, in der registriert wird welche Synchronisierungen stattgefunden haben.

  • id serial - Eine fortlaufende Nummer der stattgefundenen Synchronisierungen.
  • client_id character varying - Eindeutiger Identifikator vom Client. Änderungen vom Server haben hier keinen Eintrag.
  • username character varying - Vor und Nachname des angemeldeten Nutzers.
  • client_time timestamp without time zone - Zeitstempel wann die Synchronisierung vom Client aus angestoßen worden ist.
  • pull_from_version integer - Version des Deltas ab dem Deltas für den Client gelesen wurden.
  • pull_to_version integer - Höchste Versionsnummer der Deltas die für den Client gelesen wurden.

pull_to_version kennzeichnet für den Client bis zu welchen Änderung er Deltas erhalten hat und damit den letzten Stand der Änderungen auf dem Server. Bei der nächsten Synchronisierung holt er sich nur Änderungen mit Versionsnummern die größer sind als diese Version.

Die Deltas-Tabelle public.deltas_all, die auf dem Server vorhanden ist, enthält folgende Attribute:

  • uuid integer - Eindeutige Nummer des Datensatzes den das Delta betrifft.
  • client_id character varying - Eindeutiger Identifikator vom Client. Änderungen vom Server haben hier keinen Eintrag.
  • sql text - Das Sql-Statement der Änderung in der Tabelle
  • schema_name character varying - Der Name des Schemas in der die Tabelle liegt
  • table_name character varying - Der Name der Tabelle
  • version integer - Die Version des Deltas. Gibt die Reihenfolge an, in der die Änderungen ausgeführt wurden und auf andern Geräten ausgeführt werden müssen.
  • action character varying - [insert | update | delete] Die Aktion die im SQL ausgeführt wurden, bzw. werden soll. Bei type = image steht hier nur insert oder delete, weil Bilder nicht geändert werden können, nur gelöscht und neu angelegt.
  • action_time timezone without time zone Die Zeit zu der die Aktion ausgeführt wurde.

Die Deltas-Tabelle auf dem Client hat zusätzlich die Spalte:

  • type character varying - [sql | image] Ob es sich um eine Änderung des Datensatzes handelt oder um das Erzeugen oder Löschen von Bildern

und dafür keine Spalte client_id

Im Folgenden wird beschrieben wie die Synchronisierung zwischen Server und Clients abläuft:

Ablauf der Synchronisation

1) Der Client fragt als erstes die Stellen beim Server ab

  • go string = mobile_get_stellen
  • passwort string
  • login_name string

Als Antwort bekommt der Client eine Liste der Stellen zurück:

 {
   "success": true,
   "user_id": "2",
   "user_name": "Peter Korduan",
   "stellen": [{
     "ID": "54",
     "Bezeichnung": "Administration Pet Dev",
     "dbname": "kvwmapsp_pet_dev",
     "west":6.98487,
     "south":36.77034,
     "east":17.69165,
     "north":68.35926,
     "startCenterLat":52.5648,
     "startCenterLon":12.33826,
     "layer_params": {
       "jahr": {
         "id": "1",
         "key": "jahr",
         "alias":"Jahr",
         "default_value":"15",
         "options_sql": "SELECT series.jahr AS value, '20' || series.jahr AS output FROM generate_series(15,40) AS series(jahr)",
         "options": [{
           "value":"15",
           "output":"2015"
         }, {
           "value":"16",
           "output":"2016"
         }, {
           "value": "17",
           "output":"2017"
         }
       }
     },
     { Nächste Stelle }
   ]
 }

2) Nach dem Laden der Stellen lädt die App die Metadaten der Layer mit dem Case go=mobile_get_layers. Die Anfrage erfolgt mit folgenden Parametern:

  • go string = mobile_get_layers
  • Stelle_ID int
  • passwort string
  • login_name string

Als Antwort bekommt der Client ein Array von Layerdefinitionen und die höchste Version der Deltas. Die Layerdefinition enthalten die Attribute und Klassendefinitionen: Result:

 {
   "success": true,
   "layers": [ {
     "id": "831",
     "title":"Baumkataster Baum",
     "alias":null,
     "id_attribute":"uuid",
     "name_attribute":"baumnummer",
     "classitem" : "id_nr",
     "transparency" : 80,
     "geometry_attribute" : "the_geom",
     "geometry_type" : 0,
     "table_name" : "baum",
     "schema_name" : "kob",
     "query": "SELECT ... ",
     "filter":"",
     "document_path":"\/var\/www\/data\/bilder\/baumkataster\/","vector_tile_url":"",
     "privileg":"2",
     "drawingorder":"91000",
     "legendorder":null,
     "sync":"1",
     "version":"1.0.0",
     "attributes": [ Liste der Attribute des Layers ],
     "tables" : [ Liste der vom Layer verwendeten Tabellen],
     "classes" : [ Liste der vom Layer verwendeten Klassen]
   }, {
     ...
   }],
   "last_delta_version" : 109
 }

Die Version des Layers kennzeichnet den Stand des Datenmodells. Später kommen bei mobile_sync_all alle Versionen der Layer zurück und können im Client abgeglichen werden. Wenn sich eine Layerversion geändert hat, müssen alle Layerdefinitionen und Daten neu geladen werden.

3) Im nächsten Schritt werden die Daten der Layer abgefragt. Bei der Abfrage der Daten wird last_delta_version als Filter angewendet.

 version < last_delta_version

Die Daten werden mit dem Case go=mobile_get_data abgefragt.

  • go string = mobile_get_data
  • Stelle_ID int
  • passwort string
  • login_name string
  • selected_layer_id int $layer_id,
  • last_delta_version int $last_delta_version

4) Die nächste Synchronisierung des Clients mit dem Server erfolgt ebenfalls mit last_delta_version. Dadurch bekommt der Client nur die Deltas von Änderungen geschickt, die er noch nicht kennt. Bei der Synchronisierung bekommt der Client erneut die höchste abgefragte Version vom Server mitgeteilt und weiß somit wieder bis wo er bereits gepullt hat.

Für die Synchronisierung sammelt der Client alle Deltas aus seiner eigenen deltas_all Tabelle ein die vom type sql sind und sendet eine Anfrage an den Server mit folgenden Parametern:

  • go string = mobile_sync_all - Anwendungsfall auf dem Server.
  • login_name string - Longin-Name des Nutzers.
  • passwort string - Das Passwort des Nutzers.
  • Stelle_ID integer - Die ID der Stelle in der die Synchronisierung statt finden soll.
  • client_id string - Die eindeutige Nummer des Gerätes. Wird von kvmobile mit cordova device plugin abgefragt.
  • client_time timestamp without time zone - Zeitstempel der Zeit wenn der Client die Anfrage schickt. Wird auf dem Client erzeugt und im Server in der sync_all Tabelle zum Vorgang abgelegt.
  • last_delta_version - Die Version bis die der Client die Daten schon hat. Vom Server werden alle abgefragt die älter sind als last_delta_version.
  • client_deltas - Ein Objekt mit einem Array von Deltas (rows) und einem Array der letzten Delta-Versionen (lastDeltaVersions). Beschreibung der Datenstuktur der Deltas siehe unten oder in der Dokumentation zur Tabelle deltas_all. Die Datenstruktur der Deltas die zum Server geschickt werden sieht wie folgt aus:
 {
   "rows" : [
     {
       "uuid" : "003de0b8-2185-4380-a547-f4c1634ff1sd",
       "sql" : "INSERT INTO test.punkte (uuid, bilder, bilder_updated_at, updated_at_client, updated_at_server, user_name, status, name, is_active, created_at, created_from, user_id, stelle_id, geom , version) VALUES ('003de0b8-2185-4380-a547-f4c1634ff1sd', null, null, '2024-03-01 10:35:12', '2024-02-01 09:39:42', 'Peter Korduan', '0', 'Testfläche 4', 'f', '2024-02-01 07:12:32', 'Peter Korduan', '2', '54', '010100004FED29DFF3D28404AD6D20E610000054FED29DF4FED29DFF3D28404AD6DF3D28404AD6D4FED29DFF3D28404AD6D2AC68094B40', 3)",
       "schema_name" : "test",
       "table_name" : "polygone",
       "action" : "update",
       "action_time" : "2024-03-01 10:35:12",
       "version" : 15
     }, {
       "uuid" : "003de0a8-2185-4380-a547-f4c1634ff9bd",
       "sql" : "INSERT INTO test.punkte (uuid, bilder, bilder_updated_at, updated_at_client, updated_at_server, user_name, status, name, is_active, created_at, created_from, user_id, stelle_id, version, geom , version) VALUES ('4992613c-7a0a-463d-bb40-99a1345486bd', '{"/var/www/data/kvwmap_pet_dev/punkte/1650531377364.jpg&original_name=1650531377364.jpg","/var/www/data/kvwmap_pet_dev/punkte/1650531386496.jpg&original_name=1650531386496.jpg"}', '2024-03-01 10:39:05', '2024-03-01 10:39:05', '2024-03-01 10:39:05', 'Peter Korduan', '0', 'Test 4', 'f', '2024-03-01 10:39:05', 'Peter Korduan', '2', '54', '0101000020E610000054FED29DFF3D28404AD6D2AC68094B40' , 4)",
       "schema_name" : "test",
       "table_name" : "punkte",
       "action" : "insert",
       "action_time" : "2024-03-01T10:39:05",
       "version" : 16
     }, {
       ... ein weiteres Deltas Objekt
     }
   ]
 }

5) Zunächst werden in der Funktion mobile_sync_all_parameter_valide die Parameter geprüft.

  • Die Attribute login_name und passwort werden schon bei der Authentifizierung geprüft.
  • Das Attribute go und Stelle_Id werden bei der Authorisierung geprüft.

Im Anwendungsfall go = mobile_sync_all wird geprüft:

  • ob client_time existiert und nicht leer ist.
  • ob client_id übergeben wurde und nicht leer ist.
  • ob last_delta_version übergeben wurde und nicht leer ist.
  • ob client_deltas übergeben wurden

Bis hierhin werden die fehlenden Eigenschaften eingesammelt. Danach wird geprüft:

  • es sich um ein Objekt handelt
  • ob es eine Eigenschaft rows hat
  • Wenn es mindestens eine Zeile gibt ob in der ersten Zeile:
    • version übergeben wurde und nicht leer ist
    • sql übergeben wurde und nicht leer ist
    • schema_name übergeben wurde und nicht leer ist
    • table_name übergeben wurde und nicht leer ist

Die Validierung des Objektes schlägt bei der ersten Bedingung die nicht zutrifft fehl und bricht mit dem setzen einer Fehlermeldung ab. Zum Schluss der Prüfung wird wenn mindestens ein Fehler aufgetreten ist der Synchronisierungsvorgang abgebrochen und ein JSON-Objekt mit success: false und einer Meldung zurück an den Client geschickt:

 {
   "success" : false,
   "err_msg" : "Eine entsprechende Fehlermeldung, die benennt was gefehlt hat."
 }

6) Falls der Client Deltas mitgeschickt hat, werden diese, falls die Berechtigung dazu besteht, nacheinander auf dem Server ausgeführt. Hat der Nutzer keine Berechtigung die Deltas auf dem Server auszuführen, werden diese als failed_deltas an den Client zurückgeschickt.

Bei Update-Statements wird vorher abgefragt ob es in der Tabelle die geupdated werden soll einen Datensatz mit der gleichen uuid gibt, mit jüngeren updated_at_client oder updated_at. Wenn ja, werden die Deltas mit jüngerem Datum in action_time abgefragt um sie nach der Ausführung des Delta vom Client auszuführen.

7) Wenn es jüngere Deltas gab werden sie nach dem Ausführen des Delta vom Client ausgeführt.

8) Unabhängig davon ob Deltas vom Client auf dem Server ausgeführt wurden oder nicht, werden alle Deltas abgefragt

  • dessen Versionsnummer größer als die last_delta_version vom Client ist
  • keine insert Deltas mit der client_id vom Client

Die gefundenen Deltas werden sortiert nach Version zum Client zurückgeschickt und ebenso die neue last_client_version.

Die Datenstruktur, die an den Client im JSON-Format zurückgeschickt wird, sieht wie folgt aus:

  • success boolean - True bei Erfolgt und false bei Misserfolg. Unabhängig ob failed deltas zurückkommen.
  • last_delta_version - Die Version bis zu der die Änderungen abgefragt wurden.
  • layer_versions - Die Versionen der Layer.
  • deltas Delta[] - Array mit Objekten der Struktur Delta, siehe unten
  • failedDeltas Delta[] - Array mit Objekten der Struktur Delta, bei denen Fehler auftraten. Zusätzlich haben diese Deltas die Eigenschaft err_msg.
  • log string - Ein Text der beschreibt was auf dem Server abgelaufen ist.

Ein Beispiel für die Datenstruktur des Response:

 {
   "success" : false,
   "last_delta_version" : 18,
   "deltas" : [...],
   "failedDeltas" : [...],
   "log" : "Es konnte keine Verbindung zur Datenbank hergestellt werden."
 }


Die Datenstruktur vom Delta im Response sieht so aus wie die Struktur der Tabelle deltas_all, außer dass bei failed_deltas die Eigenschaft err_msg vom Type text dazu kommt. Hier ein Beispiel:

 {
   "uuid" : "003de0a8-2185-4380-a547-f4c1634ff9bd",
   "client_id": "a4a46b6fcfadb2c2",
   "sql" : "INSERT INTO test.punkte (uuid, bilder, bilder_updated_at, updated_at_client, updated_at_server, user_name, status, name, is_active, created_at, created_from, user_id, stelle_id, version, geom , version) VALUES ('4992613c-7a0a-463d-bb40-99a1345486bd', '{"/var/www/data/kvwmap_pet_dev/punkte/1650531377364.jpg&original_name=1650531377364.jpg","/var/www/data/kvwmap_pet_dev/punkte/1650531386496.jpg&original_name=1650531386496.jpg"}', '2024-03-01 10:39:05', '2024-03-01 10:39:05', '2024-03-01 10:39:05', 'Peter Korduan', '0', 'Test 4', 'f', '2024-03-01 10:39:05', 'Peter Korduan', '2', '54', '20', '0101000020E610000054FED29DFF3D28404AD6D2AC68094B40' , 2)",
   "schema_name" : "test",
   "table_name" : "punkte",
   "version" : 17,
   "action" : "insert",
   "action_time" : "2024-03-01 10:39:05",
   "err_msg?" : "Fehler beim Eintragen des Datensatzes mit der ID: 003de0a8-2185-4380-a547-f4c1634ff9bd in die Tabelle test.punkte. Meldung: ..." 
 }

Changemanagement und Historisierung vor der Version 1.18.0

In kvmobile werden Änderungen der Reihe nach als SQL-Statements in die Deltas Tabelle geschrieben. Das führt zu zwei Problemen:

  1. Wenn bei einer Synchronisierung der Daten mit dem Server ein Delta-Statement fehl schlägt, z.B. weil ein Serverseitiges Constraint nicht erfüllt wird, müssen alle folgenden Deltas abgebrochen werden, da sonst inkonsistente Datenbestände entstehen können. Das gilt besonders wenn verknüpfte Tabellen verwendet werden.
  2. Wenn ein Datensatz gelöscht wird, müssen vorherige Änderungen nicht ausgeführt werden und im Client erzeugte neue Datensätze können aus dem Delta raus und es muss dazu kein INSERT Delta angelegt werden.

Um diese Probleme lösen zu können, wird eine einfache Historisierung eingeführt, die im folgenden ausführlich beschrieben wird. Im Client bekommen die Datentabellen alle eine zusätzliche Spalte endet, die nicht mit synchronisiert wird. Wenn dort ein Zeitstempel drin steht ist der Datensatz untergegangen, sonst nicht. Der Ausgangszustand nach einer Synchronisation ist eine leere Deltas-Tabelle und alle Datensätze haben im Attribut endet = NULL. Bei Änderungen im Client passiert folgendes:

Änderungen

INSERT

  • Ablauf
    • INSERT Delta erzeugen
    • INSERT Delta eintragen
    • INSERT Delta als SQL ausführen
  • Ergebnis
    • Neuer INSERT Datensatz in Deltatabelle
    • Neuer Datensatz in Datentabelle mit endet = NULL

1. UPDATE

(wenn es noch keinen Datensatz mit gleicher id und endet IS NOT NULL gibt)

  • Ablauf
    • UPDATE Delta erzeugen
    • UPDATE Delta eintragen
    • vorhandenen Datensatz als neuen Datensatz mit endet = Datum neu einfügen
    • UPDATE Delta als SQL ausführen auf Datensatz mit id und endet IS NULL
  • Ergebnis
    • Neuer UPDATE Datensatz in Deltatabelle
    • Kopie von Datensatz mit Zustand von vor der Änderung in Datentabelle mit endet = Datum
    • Aktueller Datensatz mit letzten Änderungen in Datentabelle

n. UPDATE

(wenn es schon einen Datensatz mit gleicher id und endet IS NOT NULL gibt)

  • Ablauf
    • UPDATE Delta erzeugen
    • UPDATE Delta eintragen
    • UPDATE Delta als SQL ausführen auf Datensatz mit id und endet IS NULL
  • Ergebnis
    • Neuer UPDATE Datensatz in Deltatabelle
    • Aktueller Datensatz mit letzten Änderungen in Datentabelle

zwischen dem UPDATE eines neuen und eines vorhandenen Datensatzes gibt es keine Unterschiede, außer, dass in der Deltastabelle bei einem neuen Datensatz nach vor UPDATE Deltas ein INSERT Delta steht und bei vorhandenen nur UPDATE Deltas.

DELETE eines vorhandenen Datensatzes

  • Ablauf
    • DELETE Delta erzeugen
    • Alle Deltas mit gleicher id löschen (können nur UPDATE Deltas sein, weil es kein INSERT gibt für schon vorhandene Datensätze und kein DELETE weil man einen Datensatz nicht zwei mal löschen kann.)
    • DELETE Delta eintragen
    • DELETE Delta als SQL ausführen auf Datensatz mit gleicher id und endet IS NULL
  • Ergebnis
    • Es gibt nur einen DELETE Delta von diesem Datensatz in der Deltatabelle
    • Datensatz mit endet IS NULL fehlt in Datentabelle
    • Datensatz mit endet = Datum existiert noch

DELETE eines neuen Datensatzes

(nach der Synchronisierung angelegter und auch ggf. geänderter Datensatz)

  • Ablauf
    • DELETE Delta erzeugen
    • Alle Deltas mit gleicher id löschen (können INSERT und UPDATE Statements sein, denn neue Datensätze haben ein INSERT und DELETE geht nur ein mal.)
    • kein! DELETE Delta eintragen, weil ein neuer der gelöscht wurde nicht synchronisiert werden muss.
    • DELETE Delta als SQL ausführen auf Datensatz mit gleicher id und endet IS NULL
  • Ergebnis
    • Der Datensatz taucht gar nicht mehr in der Deltatabelle auf.
    • Datensatz mit endet IS NULL fehlt in Datentabelle
    • Datensatz mit endet = Datum existiert noch

Deltas vor dem Synchronisieren

  • Neue Datensätze haben ein INSERT wenn nichts geändert wurde oder ein oder mehrere UPDATES
  • vorhandene Datensätze haben wenn nichts geändert wurde keine Deltas, ein oder mehrere UPDATES oder ein DELETE

Wiederherstellung

Um ein Delta, welches zu einem Fehler auf dem Server führt korrigieren oder aufheben zu können wurden Wiederherstellungen eingeführt. Bei einer Wiederherstellung werden folgende Schritte immer ausgeführt:

  • Ablauf
    • alle UPDATE und DELETE Deltas mit der id löschen
    • den Datensatz mit endet = NULL gelöscht
    • den historische Datensatz mit endet = Datum auf endet = NULL setzen.

Die Vorgehensweise bei den Deltas unterscheiden sich jedoch wie folgt:

Einen geänderten Datensatz wiederherstellen

  • Ablauf
    • Beim Löschen von Deltas sind nur UPDATE Deltas betroffen, weil der Datensatz ja nicht auf dem Client erzeugt oder gelöscht wurde.
  • Ergebnis
    • Es gibt genau einen Datensatz mit endet = NULL in der Version wie nach der letzten Synchronisation.
    • Der Datensatz hat keine Deltas.

Einen gelöschten Datensatz wiederherstellen

  • Ablauf
    • Beim Löschen der Deltas ist nur ein DELETE Delta betroffen, weil nach einem Delete keine UPDATE oder INSERT Deltas mehr da sind.
  • Ergebnis
    • Es gibt genau einen Datensatz mit endet = NULL in der Version wie nach der letzten Synchronisation.
    • Der Datensatz hat keine Deltas.

Einen neuen Datensatz, der zuvor geändert wurde wiederherstellen

  • Ablauf
    • Beim Löschen von Deltas sind nur UPDATE Deltas betroffen. Das INSERT Delta brauchen wir noch, weil der Datensatz ja neu auf dem Client ist.
  • Ergebnis
    • Es gibt genau einen Datensatz mit endet = NULL in der Version wie nach dem neuen Anlegen des Datensatzes.
    • Der Datensatz hat nur ein INSERT Delta.

Einen neuen Datensatz, der zuvor gelöscht wurde wiederherstellen

  • Ablauf
    • Beim Löschen von Deltas sind praktisch keine betroffen, weil der neue Datensatz ja gelöscht wurde und keine Deltas von ihm da sind.
    • Ein INSERT Delta erzeugen auf der Basis des wiederherzustellenden Datensatzes.
    • Das INSERT Delta eintragen.
  • Ergebnis
    • Es gibt genau einen Datensatz mit endet = NULL in der Version wie nach dem neuen Anlegen des Datensatzes.
    • Der Datensatz hat nur ein INSERT Delta.