Samstag, 10. November 2018

Umstrukturierung SmartHome-System

Hallo zusammen,

heute möchte ich über die Umgestaltung meines IoT-Systems sprechen. Dazu möchte ich zunächst auf die bisherige Struktur eingehen.

Alte Struktur


Bei der Entwicklung der Struktur 2015 bin ich von einer einfachen EVA-Struktur ausgegangen. Das heißt es gibt die Schritte Eingabe, Verarbeitung und Ausgabe. Für die Ein- und Ausgabe hatte ich physische Geräte vorgesehen, welche sich mit einem Server verbinden und sich dort registrieren. Dabei teilen sie diesem ihre Ein- und Ausgänge (IOs) mit, welche vom Server gespeichert werden. Zur Verbindung dieser virtuellen IOs waren Logikstrukturen gedacht. Diese enthalten auch wieder Ein- und Ausgänge, sowie Logikelemente, welche vom Benutzer über einen Editor zusammengestellt werden können. Sämtliche Elemente, also Eingänge, Ausgänge und Logikelemente können schließlich über den Editor logisch verbunden werden. Die dadurch entstandenen Strukturen stellen Vorlagen dar, von welchen nun Instanzen erzeugt werden können. Diese enthalten nun die definierten IOs, welche mit den IOs der Geräte verbunden werden können.

Das ganze System wurde mit der Zeit Schritt für Schritt erweitert. Eine Erweiterung stellt die Möglichkeit dar, instanzabhängige Variablen einzufügen. Während es schon von Beginn an möglich war Konstanten in die Struktur zu integrieren, welche für alle Instanzen einer Struktur gleich sind, so ist es damit möglich Konstanten zu definieren, welche für jede Instanz einzeln einen Wert erhalten.

Eine andere Erweiterung ist die Möglichkeit, eigene Logikelemente programmieren und als Plug-In integrieren zu können. Beispiele dafür sind Plug-Ins für Alexa, sowie ein Plug-In für globale Variablen. Damit ist es möglich Konstanten zu definieren, welche Struktur- und Instanzübergreifend verwendet werden können.

Mit der stetigen Erweiterung des Systems wurde für jede Möglichkeit die verschiedenen IOs zu verbinden jeweils eigener Code und passende Nutzerinterfaces erstellt. Daher existieren mehrere Quellcodebereiche, die praktisch die gleichen Aufgaben erfüllen, jedoch alle leicht unterschiedlich sind. Dabei ist jedoch immernoch die starre Struktur Eingang-Logik-Ausgang vorhanden.

Alte Systemstruktur: Alle Module sind direkt verbunden
Das Ziel der Überarbeitung ist es daher, den gesamten Prozess des Verbindens zu vereinheitlichen. Dadurch soll die Menge des Quellcodes reduziert und der Prozess flexibler gestaltet werden.

Neue Struktur


Als Lösung verwende ich einheitliche Datenpunkte „DataNodes“. Jeder Ein- und Ausgang im System erstellt nun für sich ein DataNode. Die DataNodes werden global verwaltet und können über ein einheitliches Interface verbunden werden. Daher bezeichne ich diese Systemstruktur als „Unified Node Architecture“ („UNA“).

Der Vorteil von UNA besteht darin, dass die verschiedenen Module (Logikmodul, Gerätemodul), welche bisher hart über den Quellcode verbunden wurden, nun als Plug-Ins ausgelegt werden können, welche ihre IOs flexibel registrieren. Weiterhin können einfach zusätzliche Plugins hinzugefügt werden, welche mit den bestehenden Plug-Ins verbunden werden können. Zudem entfällt die starre Eingang-Logik-Augang-Struktur, da nun auch beispielsweise Geräte direkt ohne Logik verknüpft werden können, oder auch Ausgänge von Logikstrukturen als Eingang von weiteren Strukturen genutzt werden können.

Neue Systemstruktur: Die Module sind als Plug-Ins (blau) ausgelegt, die Verbindung erfolgt universell.

Nutzerinterface


Damit die neuen UNA-Nodes auch verbunden werden können, wurde ein neues Form erstellt. Das Verbinden von IOs war bisher ein vergleichsweise unübersichtlicher Prozess. Das verwendete Form war für die vier Kombinationen von Ein- und Ausgängen aus Logik- und Gerätesicht ausgelegt.

-    Eingang aus Logiksicht
-    Eingang aus Gerätesicht
-    Ausgang aus Logiksicht
-    Ausgang aus Gerätesicht

Für jede Variante war eigener Code vorhanden, welcher je Anwendung ausgewählt wurde. Das führte leider zu viel und zu unübersichtlichen Code, sowie zu einem unübersichtlichen Form.

Altes Verbindungsform:
Anzeige vorhandener Verbindungen
Altes Verbindungsform:
Auswahl möglicher Geräte
Altes Verbindungsform:
Erstellen einer Verbindung
Aus diesem Grund bin ich froh, dieses Verbindungsform durch ein neues ersetzen zu können. Da es nun nur noch DataNodes gibt, gestaltet sich das neue Form übersichtlicher. Es besteht aus zwei (TreeView-)Listen, welche alle existierenden Nodes anzeigen. Dabei wird unterschieden, ob ein Node zum Lesen oder zum Schreiben gedacht ist. Generell können alle Schreib-Nodes auch gelesen werden, jedoch nicht anders herum. Entsprechend dieser Information erscheint das Node auf der linken und/oder der rechten Seite.

Auf beiden Seiten können Nodes ausgewählt werden (türkis im Bild). Entsprechend wird die jeweils andere Liste nach Kompatibilität mit dem gewählten Node gefiltert. Gleichzeitig werden bereits verbundene Nodes farblich (grün im Bild) hervorgehoben. Ist auf beiden Seiten ein Node ausgewählt, so können diese über Buttons verbunden oder getrennt werden.

Neues Verbindungsform für UNA-Nodes
Möglicherweise ist dem ein oder anderen beim Betrachten des Bildes aufgefallen, dass sich dieses im Design von allen anderen Forms unterscheidet, die ich bisher in diesem und in anderen Einträgen gepostet habe. Dies ist darin begründet, dass ich dieses Form erstmals mit WPF („Windows-Presentation-Foundation“) erstellt und programmiert habe, während es sich bei allen anderen Forms um Windows-Forms handelt. Darauf möchte ich an dieser Stelle jedoch nicht näher eingehen.

Nutzerinterface-PlugIns


Neben der Neuerstellung des Verbindungs-Forms ist es durch die Umarbeitung des Geräte-Handlers und des Logik-Handlers zu serverseitigen Plug-Ins parallel nötig, die jeweiligen Bereiche in der Bedienoberfläche zu überarbeiten. Diese Module waren bisher fest in die Software integriert und konnten über einen Kommunikations-Kern (UI-Core) entsprechende Funktionen auf dem Server auslösen und Daten abrufen.

Alte Kommunikationsstruktur mit direkten Verbindungen
Da die Module serverseitig nun Plug-Ins sind, kann der Connection-Handler auf Serverseite eintreffende Requests nicht mehr einfach an die Module weitergeben. Daher habe ich die Module auf Client-Seite ebenfalls in Plug-Ins ausgelagert. Zur Kommunikation können diese nun Requests an einen Plugin-Handler weiterreichen. Dieser verpackt die Anfrage und schickt sie an den Server. Der Connection-Handler kann die Anfrage nun an den Plugin-Handler übergeben. Dieser entpackt die Anfrage wieder und sucht anhand einer angehängten Plug-In-ID  ein passendes Plug-In. Diesem übergibt er die Anfrage, welche dort verarbeitet wird.

Neue Kommunikationsstruktur mit Plug-Ins (blau)
Da die Umstrukturierung nun abgeschlossen ist, überlege ich aktuell, an welchen Stellen das System weiter ausgebaut und verbessert werden kann. Gleichzeitig sind bei mir weitere Geräte eingetroffen, die ich gern in das System einbinden möchte. Weiterhin prüfe ich derzeit, ob das System bereits weit genug entwickelt ist, um es als Anwendung oder als Quellcode auf GitHub zu veröffentlichen.

Über all diese Themen werde ich jedoch hier berichten, wenn die Zeit gekommen ist. Bis dahin bleibt mir nur, mich fürs Lesen zu bedanken und mich für heute zu verabschieden.

Vielen Dank!