From d78ed8e2b31e0567e601856ad80b647d410ce4fc Mon Sep 17 00:00:00 2001 From: Tobias Hopp Date: Wed, 30 Nov 2022 16:39:36 +0100 Subject: [PATCH] =?UTF-8?q?=E2=80=9Edoc/Software=E2=80=9C=20=C3=A4ndern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc%2FSoftware.md | 170 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 doc%2FSoftware.md diff --git a/doc%2FSoftware.md b/doc%2FSoftware.md new file mode 100644 index 0000000..847939e --- /dev/null +++ b/doc%2FSoftware.md @@ -0,0 +1,170 @@ +[<- Inhaltsverzeichnis](https://git.gaminggeneration.de/tobiash/itender/wiki/doc%2FHome) +# 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: 17.1 +- TypeScript: 4.8.4 +- WebPack: 5.74.0 +- Mongodb/Mongoose: 5.11.97 + + +## Statistiken +Stand 30.11.2022: +```md +# Programmiercode +15863 Zeilen +# Zeitaufwand +79 Stunden und 32 Minuten +# Importierte Bibliotheken +16 Stück +# Dateien (Klassen und Objekte) +52 Dateien +``` + +## Aufbau & 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. + + +## Erklärung aller Dateien + +| Context | Datei | Beschreibung | +|--------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| iTender | main | Die Hauptdatei, sie ist der Einstiegspunkt des Programms, von hier aus wird die Datenbank verbunden und der Webserver gestartet | +| iTender | MyGPIO | Die eigene GPIO Library von Tobias Hopp. Sie dient zum Steuern von GPIO-Pins am Raspberry Pi | +| iTender | Utils | Hier befinden sich einige schnelle Funktionen, welche des öfteren von mehreren Methoden im Programm genutzt werden | +| iTender | RejectReason, SensorType, RequestType | Ein paar Klassen welche Enums (sogenannte feste Platzhalter für Variablen) bereitstellen | +| iTender | SensorType | Die Art des Sensors, aktuell sind Ultraschallsensoren und Wäge sensoren unterstützt | +| Webserver | App | Die App ist die Instanz des Webservers, welcher für die Oberfläche genutzt wird, dieser wird vom iTender selbst, sowie etwaigen anderen Geräten aufgerufen | +| Webserver | WebsocketApp | Ähnlich wie die normale App, nur spezifisch für die Direktverbindung zwischen Oberfläche und Server | +| Webserver | WebSocketHandler | Die Server-Seitige WebSocket-Verbindung. Hier werden die Nachrichten an die Oberfläche gesendet und verarbeitet | +| iTender | LEDHandler | Übernimmt die Steuerung der WS2812b LEDs | +| iTender | Category | Kategorie des Getränks | +| iTender | Settings | Die Einstellungen des iTenders, werden gespeichert in der config.json | +| Weboberfläche | error.pug, index.pug, layout.pug | Die statische Webseite für den iTender, diese Oberfläche liest die JavaScript Dateien ein und verbindet sich dann mit dem WebSocket (mit dem iTender) | +| Compiler | dist/ Ordner | Hier sind alle kompilierten Dateien zu finden | +| Webserver | web/main | Der Einstiegspunkt für die Weboberfläche, von hier aus wird mit dem WebSocket verbunden | +| iTender | HX711 | Die Klasse um den HX711 Wäge sensor abzufragen | +| Webserver, Weboberfläche | WebsocketPayload | Das ist die Payload (zu Deutsch Datenpaket), welche sowohl vom Webserver als auch von der Weboberfläche zum kommunizieren genutzt wird | +| Datenbank | database/Container | Hier werden die jeweiligen Container (wo die Inhalte für den Cocktail rein gefüllt werden) gespeichert | +| Datenbank | database/Database | Die Hauptklasse der Datenbank im iTender, sie sorgt für eine statische Verbindung mit der Datenbank und ermöglicht die Abfragen / Speicherungen von Getränken, Behältern und Zutaten | +| Datenbank | database/Drink | Hier werden die Getränke gespeichert, welche vom Server heruntergeladen werden | +| Datenbank | database/Job | Diese Klasse stellt den aktuellen Job des iTenders da, er wird erstellt wenn der Benutzer einen Drink haben möchte. In diesem Job-Objekt sind dann Werte wie die Kalkulation der Dauer, die Zutaten und Mengen, die jeweiligen Pumpen sowie ein Start und Endzeitpunkt festgelegt | +| Datenbank | database/Ingredient | Hier sind die Zutaten für die jeweiligen Drinks gespeichert. Es werden zu allen Drinks referenzen zu diesen Objekten erstellt | +| Weboberfläche | web/Fill | In dieser Klasse werden die Fill-Events erwartet. Sollte der Benutzer ein Getränk haben wollen, wechselt der iTender auf den Status FILLING. Diese Klasse greift das Event auf und zeigt dementsprechend dem Benutzer den aktuellen Status seines Auftrags an | +| Weboberfläche | web/Modal | Diese Klasse ist eine Art Herzstück geworden. Sie ermöglicht das schnelle und einfache Darstellen von Popups für den Benutzer. Beispiele sind Fehler oder Bestätigungsfenster. | +| Weboberfläche | web/Setup | Das Setup erwartet wie die Fill-Klasse ein Event. Hierbei das SETUP-Event. Sollte es "gefeuert" werden, werden hier die nötigen Maßnahmen getroffen um dem Benutzer das Setup anzuzeigen.
| +| Weboberfläche | web/WebHandler | Der WebHandler übernimmt das öffnen von Panels (welches Menü gerade offen ist) sowie dem korrekten Anzeigen aller Drinks welche vom iTender bereitgestellt werden | +| Weboberfläche | web/WebWebSocketHandler | Das Gegenstück zum WebSocketHandler, nur auf Web-Seite. Daher auch das doppelte Web im Namen. Es übernimmt das Verbinden mit dem WebSocket, welcher die Kommunikation zwischen iTender und Oberfläche ermöglicht | +| Weboberfläche | | | + + + +## 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. \ No newline at end of file