4
doc/Software
Tobias Hopp edited this page 2025-02-24 10:30:38 +00:00

<- Inhaltsverzeichnis

Programmierung / Software

Die Software wurde von Tobias Hopp entwickelt.
Die Software unterliegt dem privaten Copyright Schutz § 69a Abs. 3 Satz 1 UrhG.
Jegliche publikation, bearbeitung, manipulation und verbreiten wird rechtlich verfolgt.
Die Rechte an den folgenden Inhalten hat alleine die natürliche Person Tobias Hopp, geb. 02.05.2003.


Programmiersprache & Bibliotheken

Als Programmiersprache haben wir TypeScript verwendet.
TypeScript ist eine Abwandlung von JavaScript, welche aber strikte Typen hat.
Typen sind in Programmiersprachen eigenschaften von Variablen. Eine Variable kann eine Zeichenkette (sogenannten String) enthalten, aber auch eine Nummer (integer) sein.
Wenn Typengleichheit besteht, kann eine Variable (beispielsweise x) nur einmal definiert werden und behält dann den Typ bei. Wenn man beispielsweise x = 5 beschreibt, dann kann x nicht mehr eine Zeichenkette oder gar nichts werden.
Typensicherheit ist gerade in größeren Projekten von nöten, da so garantiert werden kann, welcher Entwickler wo und was bei einer sogenannten Methode (wie eine Funktion/Formel) zurückgibt.
Da JavaScript, von haus aus keine Typensicherheit bietet, verabscheuen es einige.
TypeScript vereint nun die Typensicherheit mit der eigentlich sehr schönen Sprache JavaScript.

TypeScript hat den Vorteil, dass es sehr anpassbar und dynamisch ist. Es gibt bereits viele Bibliotheken um LEDs und GPIO-Pins über den Raspberry Pi anzusteuern. Außerdem lässt sich in wenigen schritten ein kleiner Webserver einrichten, welcher nun quasi das Hauptstück des iTenders ist.
Da es eben so anpassbar ist, konnten wir den kleinen Webserver sehr erweitern und ihn genau zu unseren zwecken Programmieren.
Die Sprache ist außerdem eine Kompilierungs und Interpretierungssprache, ähnlich wie Java, weshalb sie sehr schnell ist und viele Asynchrone-Tasks (Aufgaben im Hintergrund) ausführen kann.

Genau das war uns wichtig, dass der Nutzer nicht darauf warten muss, dass das Programm fertig berechnet hat, sondern direkt weiter die Bedienung fortführen kann.

Für den Server selber haben wir nicht nur natives Type- bzw. JavaScript verwendet, sondern auch Node.
NodeJs ist die Serverseitige programmierung, des eigentlich für das Web entwickelten Javascripts.
Hier lassen sich die eben besagten Bibliotheken installieren und schnell zu einer Lösung programmieren.

Für Bibliotheken (Programmschnipsel anderer Programmierer) benutzen wir den Package Manager Yarn.
Über ihn lassen sich einfach Bibliotheken installieren, kompilieren und verwenden.
Für das kompilieren selber benutzen wir den TSC (Typescript compiler), für das verpacken für die Benutzeroberfläche Webpack.

Für die Datenbank, welche alles Speichert, haben wir uns für MongoDB entschieden.
MongoDB als No-SQL Datenbank arbeitet im Gegensatz zu Datenbanksystemen wie MySQL mit Dokumenten anstatt mit richtigen Tabellen.
Es bestehen keine festen Relationen zu anderen Dokumenten, es gibt auch keine wirklichen Tabellen.
Jegliche Datensätze sind dynamisch, das heißt, es besteht keine feste Datenstruktur.
Das klingt erstmal schlecht, ist aber mit dem richtigen Programm hilfreich.

Ein MongoDB Server verwaltet mehrere logische Datenbanken, die wiederum einen oder mehrere logische Namensräume enthalten, die sogenannten Collections. In einer Collection werden die einzelnen Datensätze, Dokumente genannt, verwaltet.

Durch die Benutzung von BSON als Dokument, lässt sich ohne großen Aufwand ein ganzes selbst erstelltes Paket in der Datebank abspeichern.
MongoDB ist durch die einfachheit auch deutlich schneller und ermöglicht es größere Datensätze mehr im Programmierkontext zu erfassen.
Mit der Bibliothek Mongoose ist es auch möglich das einzige Problem der Typensicherheit in den Griff zu bekommen.
So ist MongoDB nun schnell, Typensicher und bereit für große Mengen an Daten.

Versionen:

  • Yarn: 1.22.19
  • NodeJS: 18.9.1
  • TypeScript: 4.8.4
  • WebPack: 5.74.0
  • Mongodb/Mongoose: 5.11.97

Statistiken

Stand 17.01.2023:

# Programmiercode
23217 Zeilen
# Zeitaufwand
92 Stunden und 35 Minuten
# Importierte Bibliotheken
29 Stück
# Dateien (Klassen und Objekte)
~90 Dateien

Aufbau

Aufbau

Das Programm des iTenders ist getrennt in 3 Teile

  • iTender Basis
    • Die Basis besteht aus der Kommunikation zwischen den Pumpen, LEDs und jeglicher Hardware
    • Außerdem gibt es Timer, automatische Events, Prüfung und aktualisierung von Getränken etc.
    • Sie übernimmt z.B. das Starten vom Getränke-Füllen, stoppen sowie Berechnen von den Zutaten für ein Getränk
    • Dieser Teil arbeitet seh eng mit dem Websocket-Server zusammen, um so auf Events vom Endnutzer zu reagieren.
  • iTender Webserver
    • Der Webserver ist einfach ein statischer Webserver welche Dateien "serviert", die dann von der Oberfläche geladen werden können.
    • Der Client-Browser oder iTender-Display lädt dann diese Seite, erstmal passiert dann noch nichts
  • iTender Websocket-Server
    • Der Websocket-Server dient zur eigentlichen Live-Kommunikation zwischen Client und Gerät.
    • Der Server und die Webseite (Endgerät), bauen eine Ende-zu-Ende Verbindung auf
    • Die Kommunikation zwischen Client und Websocket-Server basiert auf JSON (Javascript-Objekt-Notation).
    • Da der Websocket in früheren Client-Versionen anfällig für Fehler beim Übertragen von nicht alphabetischen Zeichen war, wird der gesendete Inhalt noch in Base64-Kodiert.
    • Base64 dient dazu, die Daten binär zu kodieren, um sie auf der Gegenstelle wieder zu entkodieren.
    • Außerdem wird beim übertragen eine Checksumme mitgegeben, um bei Fehlerhaften Paketen ein neues anzufragen.

Programm & Umsetzung

Das Programm ist aufgeteilt in eine Client und eine Server Seite.
Die Serverseite ist wieder geteilt in "Application" und "WebSocket".
Die Application ist der eigentliche iTender. Sie übernimmt die ganzen Aufgaben wie das füllen der Getränke, ansteuern der Pumpen und LEDs.
Der sogenannte WebSocket ist für die Kommunikation mit dem Client (Benutzer-Seite) da.
Er kommuniziert mit der Benutzeroberfläche (UI für User Interface) und sendet bzw. empfängt Pakete welche dann die einzelnen Aktionen auf beiden Seiten ausführen.

Alle Kommunikation zwischen dem WebSocket und der UI basieren auf JSON (Javascript Objekt Notation).
Dieses Datenformat ist sehr robust und ermöglicht das einfache verpacken und senden von Datentypen wie Zeichenketten, Wahr/Falsch Werten (Boolische Werte) sowie Nummern, Objekten und Listen.
Damit die Kommunikation ausfallssicher ist, wird das JSON in Base64 umgewandelt.
Base64 ist eine Kodierung welche nur im gesamten wieder umgewandelt werden kann. Mit einzelnen Teilstücken dieser Enkodierung lässt sich nicht auslesen was dort einmal war.
Das ist zum einen eine kleine Sicherheit, aber auch unsere Möglichkeit zu prüfen, ob die Kommunkation gestört ist.
Sollte die Gegenstelle das jeweilige Paket nicht wieder in JSON kodieren können, fordert es das Paket erneut an.
Falls dann immer noch ein Problem besteht wird ein Alarm erstellt, welcher im Fehlerspeicher des iTenders gesichert wird.
Dort kann er vom Support-Team ausgelesen werden bzw. behandelt werden.

Da JSON eine Objekt Notation ist, müssen wir uns hier noch auf ein gemeinsames Protokoll einigen.
Unser Protokoll ist ein selbst gewähltes Event -> Data Prinzip.
Jedes Paket hat somit ein Event an das es geknüpft ist, die Gegenstelle weiß dann damit richtig umzugehen.
Beispiele hierfür sind das Event STATUS oder REQUEST.
Ein STATUS hat wieder einzelne sub-Status, welche den Status des iTenders wiedergeben.
Ein Status ist beispielsweise READY (iTender ist bereit und wartet auf Benutzerinteraktion) oder DOWNLOADING (iTender lädt neue Getränke herunter).
Der Status ist derzeit noch oben links im Rohformat in der UI sichtbar, soll aber früher oder später anders dargestellt werden.
Eine REQUEST hingehen ist nicht wie die anderen "Protokolle" nur senden und der andere empfängt, sondern ein die Oberfläche kann hier gezielt Daten anfragen und verarbeiten.
Ein Beispiel hierfür ist die Konfiguration welche im Setup-Menü angezeigt wird.
Damit alle Felder bereits ausgefüllt sind, wie der Benutzer sie eben konfiguriert hat, wird eine REQUEST mit dem Typen "CONFIG" an den WebSocket (iTender Basis) gesendet. Er antwortet dann mit der Konfiguration und die UI kann die Felder ausfüllen.

Um alle Getränke, Zutaten, Jobs und Vorgänge zu speichern, hat der iTender eine Datenbank.
Bei der Datenbank haben wir uns für MongoDB entschieden.

Alle 10 Minuten sowie vor und nach einem Job (Auftrag fürs Mischen) wird ein sogenannter Health-Check durchgeführt.
In ihm wird überprüft ob ein vorheriger Job fehlgschlagen ist und vielleicht fest steckt. Ob der iTender irgendwo Probleme erkennt und ob alle Behälter ausreichend gefüllt sind (Falls vorhanden mit Sensoren).

Bei einer Behälter-Aktualisierung werden erst einmal alle Behälter von der Datenbank abgefragt, welche einen Inhalt gesetzt haben (also eine Zutat für einen Cocktail).
Danach werden alle Getränke abgefragt. Für jeden Cocktail wird nun geprüft ob alle Zutaten in den Behältern zu verfügung stehen.
Wenn dies der Fall ist, wird der Cocktail einer Liste hinzugefügt, welche unter Laufzeit aufrufbar ist und über eine Request von der UI abgefragt werden kann.

Für die Aktualisierung der Getränke aus der Cloud wird auch JSON benutzt.
Hier wird eine Abfrage an den Cloud-Server gestellt, falls eine Internetverbindung gefunden wurde.
Der Cloudserver antwortet hier, falls erfolgreich, mit einer Liste aller Zutaten und Getränke.
Der iTender geht dann die lokalen Getränke und Zutaten durch und schaut ob er den Eintrag auch auf der Server-Version finden kann. Falls das nicht der Fall sein sollte wird das Element aus dem lokalen Speicher entfernt.

Danach geht die Applikation die Cloud-Elemente durch. Jeweils wird geprüft ob das Element bereits existiert und Änderungen vorliegen.
Für das Prüfen von Änderungen am Thumbnail (Vorschaubild des Getränks) sorgt eine Prüfsumme (Checksum).
Wir benutzen hier den HASH-256 Algorithmus, welcher einen eindeutigen Prüfschlüssel für einen bestimmten Inhalt erstellt. Passt dieser nicht, fordert der iTender das Thumbnail erneut von der Cloud an und speichert dieses lokal.

Da wir andere Schriftarten verwenden, müssen diese auch irgendwie geladen werden.
Anfangs haben wir die Schriftarten über den von Tobias H. bereitgestellten Font-Server geladen.
Da im späteren Anwendungsfall aber nicht immer eine Internetverbindung bereitsteht, können wir keine Schriftarten von Cloud-Services laden. Somit sind alle Schriftarten nun lokal heruntergeladen und werden auch dementsprechend lokal geladen.

Damit es später auch möglich ist, den iTender von einem anderen Gerät aus zu steuern, ist der Webserver auch über andere Geräte im Netzwerk erreichbar.
Das lässt sich im Setup anfangs über die "Remote-Zugriff" Option erlauben.
Falls aktiviert kann einfach auf die IP-Adresse des Raspberry Pis zugegriffen werden.
Die Verbindung wird dann allerdings der Haupt-Instanz getrennt.
Diese Vorkehrung haben wir getroffen um jegliche Fehler mit Doppel-Events zu vermeiden.
Es wäre theoretisch möglich alles auf mehreren Endgeräten anzuzeigen, ist aber nicht das was wir haben wollen.

Für LEDs nutzen wir eine Bibliothek aus NPM (Node Package Manager), welche das "schreiben" auf die LEDs sehr einfach macht. Hier wird die Farbe je nach Status des iTenders angepasst. Im Ruhezustand (READY) ist sie auf der Ambiente-Farbe, welche man ebenfalls im Setup festlegen kann.

Kommentar des Autors

Das Projekt hat nun ingesamt einiges meiner Zeit in Anspruch genommen.
Jegliche Kopie oder Verbreitung meines Sourcecodes würde nur mir selbst schaden.
Das Projekt ist in seinem selbst Uhrheberrechtlich geschützt und wird es auch bleiben.
Die Idee es zu einem Marktfähigen Produkt zu machen besteht, ist aber nicht ganz ausgedacht.

Da die Programmierung eben so viel Mühen gekostet hat, bzw. kostet, wäre mir wichtig Respektvoll damit umzugehen.