Eine Velobox Applikation erstellen

Worum geht's ?

Eine Velobox Applikation (kurz Vlbapp) ist eine auf der Velobox als Applikationsserver laufende, in den Stationsdaemon integrierte Anwendung. (Der Stationsdaemon (vlbstationd.sh) ist die zentrale, als Daemon laufende Softwarekomponente der Velobox.) Jede Vlbapp definiert ihr eigenes API, mit dem die Client-Anwendung (gewöhnlich eine GUI) über eine Websocket-Verbindung kommuniziert.
Hier wird beschrieben, wie eine solche Applikation auszusehen hat und in den Stationsdaemon eingebunden wird.

[Historie]
10.03.2023Siegmar MüllerBegonnen
11.03.2023Siegmar MüllerGrobe Übersicht fertig

Einführung

Der Stationsdaemon ist in der Programmiersprache TCL geschrieben, in der auch die Apps erstellt werden müssen und deren Kenntnis im Weiteren vorausgesetzt wird. Eine Reihe von TCL- (und anderen) Dateien befinden sich innerhalb einer einzigen, vorgegebenen Verzeichnisstruktur. Funktionsblöcke sind darüber hinaus in namespaces organisiert.

Ein von einer App entgegengenommener API-Befehl hat die Form einer Kommandozeile mit definierter Syntax. Die App sendet ihrerseits JSON-Objektstrings an die verbundenen Clients, die als Ereignismeldungen zu interpretieren sind. Ein Ereignis (event) kann die Antwort auf einen API-Befehl sein, aber auch anderweitig durch eine von der App überwachte Komponente asynkron ausgelöst worden sein, z.B. das Eintreffen von Sensordaten.

Jede Vlbapp hat ihren eindeutigen Kodenamen <vlbapp>. Sie ist aufgeteilt in ein Kommunikationsmodul custom/wsserver/wsdomains/apps/<vlbapp>.tcl und das eigentliche Applikationsmodul custom/apps/<vlbapp>.tcl.

Anmerkung: Das Konzept der Velobox3 bringt Vereinfachungen, die aus der Nutzung des Apple-Bonjour Protokolls resultieren (Linux avahi). Damit kann jede Velobox im LAN ohne irgendeine Registrierung mit ihrem Hostnamen als <velobox-hostname>.local erreicht werden.

Websocket Domänen

Der Stationsdaemon besitzt einen eigenen HTTP/Websocketserver (lib/wssserver.tcl). Beim Hochfahren lädt dieser alle TCL-Dateien aus dem Verzeichnis custom/wsserver/wsdomains, einschließlich aller Unterverzeichnisse. Es handelt sich um die Module zum Bearbeiten von Websocketrequests, die die erforderliche Handlerprozedur für Websocketrequests bereitstellen müssen. Es gelten die folgenden Konventionen:

Bei Eintreffen einer Websocket Verbindungsanforderung ws://<velobox_name>.local/<wsdomain> wird die Prozedur <wsdomain>::handleClientMessage mit der weiteren Behandlung der Websocket Kommunikation betraut (s. man 3tcl websocket).

Das Kommunikationsmodul

Das Kommunikationsmodul einer Vlbapp namens <vlbapp> ist eine Websocketdomäne apps/<vlbapp> deren Handler die Schnittstelle zur eigentlichen Vlbapp darstellt. Es behandelt insbesondere folgende Ereignisse:

Kernelmodule im Stationsdaemon

Der Stationsdaemon besitzt einige Module, die der Kommunikation mit angeschlossener Hardware, aber auch mit externen Diensten im Netzwerk dienen. Eine Vlbapp kann dort spezielle Prozeduren aufrufen und eigene Callback Prozeduren hinterlegen, mit denen sie über spezielle Ereignisse informiert werden kann. Details dazu werden weiter unten beschrieben.

Das Applikationsmodul

Applikationsmodule werden beim Hochfahren des Stationsdaemon am Schluß, nach dem Laden aller Bibliotheks- und Kernelmodule aus dem Verzeichnis custom/apps geladen. Jedes Applikationsmodul enthält die Kommandos namespace eval <vlbapp> ... und set app_loaded <vlbapp>. Sofern die Vlbapp die Prozedur init besitzt, wird diese nach dem Laden aufgerufen. Weiterhin sollten die folgenden Prozeduren verfügbar sein:

Die start- und stop-Prozeduren dienen vor allem dem Einrichten bzw. Entfernen von Callbacks zu den benötigten Kernelmodulen.

Zum Verteilen von JSON-Ereignismeldungen an ihre angemeldeten Clients steht dem Applikationsmodul im Websocket Server die Prozedur
::WSServer::disposeServerMessage {apps/<vlbapp> text <json-objektstring>}
zur Verfügung.

HTTP Domänen

(Sinn und Zweck derselben)

Verwaltung von Kundendaten

(Datenbank vlbclients)

Verwalten von Einstellungen

(Datenbank vlbsettings)

Schichtenmodell der Velobox

Browser
(LAN)Externe
Applikation
Htdocs
(APIs über Websocket)
Vlbapps
(Prozeduraufrufe)
(Callbacks)
Kernelmodule
(2 Schichten: Basis- und
App-Kernelmodule)
↘↓
Bibliotheksmodule
(Hardware)
(LAN)

Bibliotheksmodule (Übersicht)

Allgemeine Bibliotheksmodule (lib/)
Binäre Module
Reines TCL: dbld2img piepser wsserver

::DBLD2IMG

TODO Hier Übersicht, Details s. "Generierung von JPEG-Druckbildern".

Hochfahren des Stationsdaemon

Allgemeine Bibliotheksmodule (lib/) werden vor den Kernelmodulen geladen.
Basis-Kernelmodule (lib/kernel) werden vor den App-Kernelmodulen (custom/kernel/) geladen.
Sofern das geladenen Kernelmodul eine Prozedur init {} besitzt, wird diese nach dem Laden aller Module derselben Ebene (Basis- bzw. App-) aufgerufen. App-Kernelmodule haben so die Möglichkeit, sich mit Callback-Prozeduren bei Basis-Kernelmodulen anzumelden.

Details zu den Kernelmodulen

Zugriff auf die lokalen Datenbanken

db.tcl

Druckdatenübernahme von den Sensoren

USB-Anschluß

watchttyacm.tcl

TTY*** Module hinterlegen bei watchTTYACM Callback Prozeduren, die aufgerufen werden, sobald eine über USB angeschlossene Druckmatte des entsprechenden Typs erkannt wurde. Diese Callback Prozeduren starten die Datenübernahme mit den ermittelten Parametern. Die Callback Prozeduren werden auch aufgerufen, wenn eine Matte entfernt wurde.

Bluetooth

btsattel.tcl

Generierung von JPEG-Druckbildern

Um alle CPU-Kerne in diesen rechenintensiven Prozeß einzubeziehen, werden die interpolierten JPEG-Druckbilder in Hintergrundthreads generiert. Im Bibliotheksmodul ::DBLD2IMG stehen dafür 5 Threads in Wartestellung. Eine größere Zahl wäre angesichts von max. 4 vorhandenen CPU-Kernen eher kontraproduktiv. Die Prozedur ::DBLD2IMG::startJPEG {...}(Details s. unten) sucht dazu den nächsten freien Thread, der das fertige Bild im shared Memory hinterlegt und den Aufrufer mittels der von ihm übergebenen Callback-Prozedur über die Fertigstellung des Bildes informiert. Der muß nun das fertige Bild abholen, bevor das nächste Bild generiert ist. Wenn Bilder zu unterschiedlichen Zwecken kurz hintereinander erzeugt werden, ist i.a. nicht klar, welches der Bilder gerade fertig geworden ist. (Die parallel laufenden Threads können sich ggf. überholen.) Zur Lösung dieses Problems wird zwischen Bildtypen (imagetype) unterschieden, deren Übergabe in verschiedenen Speicherbereichen erfolgt.

Wenn gerade alle Threads belegt sind, wird kein Bild erzeugt und die als Callbacks hinterlegten "overload handler" werden aufgerufen. Wenn von einer Bildfolge kein Bild verloren gehen darf, darf die Generierung eines Folgebildes erst dann angestoßen werden, nachdem die Fertigstellung des Vorgängers gemeldet wurde.

::DBLD2IMG

Globale Optionen: bgcolor colorcontrast
Sonstige Bildoptionen: defaults/Abweichungen
::DBLD2IMG::startJPEG {cbproc druckbild n_rows n_cols {imagetype jpegimage} {alt_jpegoptions {} }}
Startet die Bilderstellung im nächsten freien Thread.
Sollte kein Thread frei sein, geht eine Meldung an alle registrierten Overload Handler. Das Bild wird nicht generiert.
Das fertige Bild wird als Thread shared variable unter images $imagetype hinterlegt und es erfolgt ein Aufruf der übergeben Callback Prozedur mit $imagetype, die das fertige Bild dort abholen kann.

JPEG*** Module

JPEGSattel JPEGHocker

Von den Druckdaten zum JPEG-Bild

... am Beispiel des Hockerbildes in der Vlbapp "sitzknochenabstand".