Dieses Template Projekt wird für die Programmierausbildung an der Fachhochschule Nordwestschweiz (FHNW) eingesetzt.
Natürlich ist es auch für Programmierprojekte ausserhalb der FHNW geeignet. Einige Beschreibungen sind jedoch FHNW-spezifisch.
Sie sollten für Ihr Programmier-Projekt auf keinen Fall direkt auf diesem Repository arbeiten. Stattdessen sollten Sie ein eigenes Repository anlegen, entweder unter Ihrem FHNW GitLab- oder GitHub-Account und dieses Template-Respository als "Quelle" importieren.
Auf Ihrem GitLab-Account via
New Project -> Import Project -> Repo by URL
das Formular ausfüllen, mit Git repository URL
:
https://github.com/DieterHolz/RaspPiFX-Template-Project.git
Use this template
-Button dieses Repositories verwenden.
Verwenden Sie das vom Pi4J-Team vorbereitete CrowPi-Image.
- Download CrowPi Image
- zip-File auspacken, mit Raspberry Pi Imager eine SD-Card bespielen und damit den Raspberry Pi starten
Das CrowPi-Image enthält bereits alle notwendige Installationen für JavaFX/Pi4J-Applikationen.
-
Java 17. Im CrowPi-Image ist JDK17 installiert. Daher verwenden wir diesen JDK auch auf dem Laptop. Hinweis für Mac-Benutzer: Die Verwendung von SDKMAN (s.u.) für die Installation und die Verwaltung von JDKs ist sehr empfehlenswert.
-
IntelliJ IDEA 2021.3. Es ist wichtig, diese neueste Version zu verwenden. Am besten via JetBrains Toolbox installieren. Empfehlenswert ist die Verwendung der Ultimate Edition. Studierende erhalten, nach Anmeldung, eine kostenlose Lizenz. Registrieren Sie sich unter https://www.jetbrains.com/student/ mit Ihrer FHNW E-Mail-Adresse. Für die Community-Edition benötigt man keine Lizenz.
-
Git. Als Sourcecode-Repository verwenden wir git.
-
GitLab oder GitHub-Account. Verwenden Sie ihren FHNW Gitlab oder GitHub Account.
-
ssh
. Die Verbindung zum Raspberry Pi wird mitssh
hergestellt. Ist normalerweise auf allen Laptops vorinstalliert. -
VNC Viewer. Ermöglicht ein komfortables Arbeiten auf dem Raspberry Pi vom Laptop aus. Dadurch spart man sich das Anschliessen von Monitor, Tastatur und Maus direkt am Raspberry Pi.
Für MacOs und Linux gibt es ein sehr empfehlenswertes Tool zur Verwaltung unterschiedlicher Software Development Kits: SDKMAN
Insbesondere wenn, wie üblich, mehrere Java JDKs verwendet werden, hilft SDKMAN.
Folgenden Befehl in einem Terminal eingeben:
export SDKMAN_DIR="$HOME/sdkman" && curl -s "https://get.sdkman.io" | bash
Falls Sie SDKMAN bereits früher installiert haben, müssen Sie SDKMAN auf den neuesten Stand bringen:
sdk update
In einem neuen Terminal-Window diesen Befehl eingeben:
sdk install java 17.0.1-tem
Danach liegt der JDK in ihrer Home-Directory im Folder sdkman/candidates/java
. Von dort können Sie es dann in IntelliJ als neues SDK anlegen und im Projekt verwenden.
Mit:
sdk ls java
können Sie sich auflisten lassen, welche anderen JDKs zur Installation zur Verfügung stehen.
In einem Terminal-Window eingeben
java -version
Das sollte diese Ausgabe erzeugen
openjdk version "17.0.1" 2021-10-19
OpenJDK Runtime Environment Temurin-17.0.1+12 (build 17.0.1+12)
OpenJDK 64-Bit Server VM Temurin-17.0.1+12 (build 17.0.1+12, mixed mode, sharing)
Falls das nicht der Fall ist, muss der Default-JDK umgestellt werden. Mit SDKMAN geht das einfach:
sdk default java 17.0.1-tem
Der Laptop und der Raspberry Pi müssen das gleiche WLAN verwenden.
Eine einfache Variante, dies sicherzustellen, ist das Aufsetzen eines Hotspots auf einem Smartphone, idealerweise mit diesen Parametern:
- ssid:
Pi4J-Spot
- password:
MayTheSourceBeWithYou!
Auf diesen Hotspot connected sich der RaspPi mit dem CrowPi-Image automatisch und zeigt die IP-Nummer im Hintergrundbild an.
Den Laptop ebenfalls mit dem Pi4J-Spot verbinden.
In einem Terminal-Window des Laptops:
ssh pi@<ip.number>
Passwort: 'crowpi'
z.B.
ssh [email protected]
Passwort: 'crowpi'
Mit derselben IP-Nummer kann auch via VNC auf den RaspPi zugegriffen werden. Man erhält auf dem Laptop ein Fenster, das den kompletten Desktop des Raspberry Pis anzeigt.
Das Ganze sieht dann so aus (mit der gestarteten ExampleApp)
Dieses Projekt verwendet Maven, um die Applikationen zu bauen und entweder lokal auf dem Laptop oder auf dem Raspberry Pi auszuführen.
Die Artefakte werden dabei auf dem Laptop gebaut, anschliessend auf den Raspberry Pi kopiert und dort gestartet. Die Entwicklung direkt auf dem Raspberry Pi ist zwar ebenfalls möglich, wird aber nicht empfohlen. Besser ist es, die Applikation auf dem Laptop zu entwickeln und sie auf dem Raspberry Pi lediglich ausführen zu lassen.
Dazu müssen nur wenige Konfigurationen verändert werden.
launcher.class
(required): gibt an, welche Applikation gestartet werden soll. Impom.xml
ist bereits eine Liste von Kandidaten enthalten. Man muss nur bei der jeweils gewünschte Applikation die Kommentare entfernen.pi.ipnumber
(optional): Die aktuelle IP-Nummer des Raspberry Pi, z.B.192.168.1.2
, wird für SCP/SSH benötigt.
Mit diesen Einstellungen kann die Applikation mittels Maven-Befehl auf dem Raspberry Pi gestartet werden. Besser ist es jedoch, die Run-Konfigurationen von IntelliJ zu verwenden.
Im Projekt sind insgesamt vier Run-Konfigurationen vordefiniert, zwei zum Starten der Applikation, zwei zum Debuggen der Applikation auf dem RaspPi.
Run Local
startet das Programm, das inlauncher.class
eingestellt wurde, auf dem Laptop. Wird vor allem während der GUI-Entwicklung gebraucht (also noch ohne die Verwendung von an den Raspberry Pi angeschlossenen Sensoren und Aktuatoren).Run on Pi
startet das Programm auf dem Raspberry Pi.Debug on Pi
startet das Programm auf dem Raspberry Pi im Debug-Modus.Attach to Pi Debugger
verbindet die IDE des Laptops mit dem viaDebug on Pi
gestarteten Programm.
In Run on Pi
und Debug on Pi
muss die IP-Adresse des RaspPi eingestellt werden. Dazu Edit Configurations
wählen.
Für beide Run Konfigurationen unter Properties
die pi.ipnumber
auf die IP-Adresse des RaspPis setzen.
Falls der RaspPi als einziger im ausgewählten WLAN angemeldet ist, kann anstatt der IP-Nummer auch crowpi.local
eingetragen werden.
Analog in Attach to Pi Debugger
unter Host
ebenfalls die IP-Nummer des RaspPis eintragen.
Dient ausschliesslich der Überprüfung der JavaFX-Basis-Installation. Auf keinen Fall als Vorlage für die eigenen JavaFX-Applikationen verwenden.
Zum Starten:
launcher.class
impom.xml
auswählen<launcher.class>com.pi4j.mvc/com.pi4j.setup.HelloFX</launcher.class>
- mit
Run local
auf dem Laptop starten - mit
Run on Pi
auf dem RaspPi starten
Sobald der JavaFX-Setup überprüft ist, kann HelloFX gelöscht werden.
Die beiden anderen Beispielprogramme verwenden eine LED und einen Button. Diese müssen folgendermassen verdrahtet werden:
Ist eine reine Pi4J-Applikation ohne GUI. Auch sie dient ausschliesslich der Überprüfung des Setups.
Zum Starten:
launcher.class
impom.xml
auswählen<launcher.class>com.pi4j.mvc/com.pi4j.setup.MinimalPi4J</launcher.class>
Run local
macht für dieses Beispiel keinen Sinn. An den Laptop sind weder Button noch LED angeschlossen- mit
Run on Pi
auf dem RaspPi starten
Wenn der Button gedrückt wird, wird eine entsprechende Meldung auf dem Bildschirm ausgegeben.
Sobald der Pi4J-Setup überprüft ist, kann MinimalPi4J gelöscht werden.
Sie zeigt das Zusammenspiel eines JavaFX-basiertes Graphical-User-Interfaces (GUI) mit an den RaspPi angeschlossenen Sensoren und Aktuatoren, dem Physical-User-Interface (PUI).
Es dient als Vorlage für Ihre eigene Applikation. Das umfasst auch die enthaltenen TestCases.
Sie sollten zunächst das Beispiel kennenlernen und verstehen. Für Ihre eigene Applikation sollten Sie anschliessend die `TemplateApp kopieren und entsprechend abändern, ohne dabei die Grundregeln des MVC-Konzepts zu verletzen (s.u.).
Zum Starten:
launcher.class
impom.xml
auswählen<launcher.class>com.pi4j.mvc/com.pi4j.mvc.templateapp.AppStarter</launcher.class>
- mit
Run local
(oder direkt aus der IDE heraus) auf dem Laptop starten. Sinnvoll für die GUI-Entwicklung. Das PUI steht auf dem Laptop nicht zur Verfügung. Das GUI kann jedoch weitgehend ohne Einsatz des RaspPis entwickelt werden- in
AppStarter
kann zusätzlich noch ein rudimentärer PuiEmulator gestartet werden, so dass das Zusammenspiel zwischen GUI und PUI auch auf dem Laptop überprüft werden kann.
- in
- mit
Run on Pi
auf dem RaspPi starten (jetzt natürlich inklusive PUI)
Das MVC-Konzept sollte auch für Applikationen ohne GUI verwendet werden.
Falls Sie eine reine PUI-Applikation entwickeln oder erst später ein GUI hinzufügen wollen, sollten Sie die TemplatePUIApp
als Vorlage nehmen.
Zum Starten:
launcher.class
impom.xml
auswählen<launcher.class>com.pi4j.mvc/com.pi4j.mvc.templatepuiapp.AppStarter</launcher.class>
Run local
ist bei reinen PUI-Applikationen nicht sinnvoll- mit
Run on Pi
auf dem RaspPi starten
Zum Starten einer Applikation auf dem RaspPi im Debug-Mode werden die beiden Run-Konfigurationen Debug on Pi
und Attach to Pi Debugger
benötigt.
Wichtig dabei ist die Reihenfolge, mit der die Konfigurationen gestartet werden:
- Start von
Debug on Pi
via des Run-Knopfs - Warten bis die Konsolenausgabe meldet
Listening for transport dt_socket at address: 5005 (Attach debugger)
- Starten von
Attach to Pi Debugger
mit dem Debug-Knopf - Erst danach erscheint das GUI auf dem RaspPi-Bildschirm
Nun können Sie wie gewohnt den Debugger von IntelliJ IDEA verwenden und beispielsweise Breakpoints setzen oder das Programm schrittweise ausführen.
Beim klassischen Model-View-Controller-Konzept sind neben der Starter-Klasse mindestens 3 Klassen beteiligt. Das Zusammenspiel dieser Klassen ist klar geregelt:
-
Model Klassen
- enthalten den gesamten zu visualisierenden Zustand. Wir nennen diese Klassen daher Presentation-Model
- sind komplett unabhängig von Controller und View
-
Controller Klassen
- stellen die gesamte Funktionalität, die sogenannten Actions, in Form von Methoden zur Verfügung
- verwalten die Model-Klassen gemäss der zugrundeliegenden Business-Logik
- haben keinen Zugriff auf die View-Klassen
-
View Klassen
- rufen ausschliesslich Methoden auf dem Controller auf, sie "triggern Actions"
- werden vom Model über Zustandsänderungen notifiziert
- observieren den Status des Models
- ändern das Model nie direkt
-
Starter Klasse. Ist eine Subklasse von
javafx.application.Application
. Instanziiert die drei anderen Klassen und startet die Applikation.
In unserem Fall gibt es mindestens zwei View-Klassen
- GUI Klasse. Das Graphical-User-Interface. JavaFX-basierte Implementierung des auf dem Bildschirm angezeigten UIs.
- PUI Klasse. Das Physical-User-Interface. Pi4J-basierte Implementierung der Sensoren und Aktuatoren. Verwendet Component-Klassen, wie Sie sie aus dem CrowPi-Tutorial kennen.
GUI und PUI sind komplett voneinander getrennt, z.B. hat der GUI-Button zum Anschalten der LED keinen direkten Zugriff auf die LED-Component des PUIs. Stattdessen triggert der GUI-Button lediglich eine entsprechende Action im Controller, der wiederum die on-Property im Model auf den neuen Wert setzt. In einem separaten Schritt reagiert die LED-Component des PUIs auf diese Wertänderung und schaltet die LED an- bzw. aus.
GUI und PUI arbeiten mit dem identischen Controller und damit auch mit dem identischen Model.
Es ist wichtig, dass Sie dieses Konzept verstehen und für Ihr Projekt anwenden können. Gehen Sie bei Fragen auf die Fachcoaches oder OOP-Dozierenden zu.
Jede Benutzer-Interaktion durchläuft im MVC-Konzept den immer gleichen Kreislauf:
Unsere View-Klassen, also GUI und PUI, setzen das von Prof. Dierk König veröffentlichte Projector Pattern um.
Die grundlegenden Aufgaben von GUI und PUI sind gleich. Auf Code-Ebene ist dies erkennbar:
sie implementieren das gemeinsames Interface Projector
, können also auf die gleiche Weise verwendet werden.
Weitere Konsequenzen
- Es können weitere UIs hinzugefügt werden, ohne dass es Code-Änderungen bei den bestehenden Klassen (ausser der Starter-Klasse) nach sich zieht.
- Ein Beispiel dafür ist der
PuiEmulator
, der bei Bedarf zusätzlich gestartet werden kann.
- Ein Beispiel dafür ist der
- Diese Architektur ist auch geeignet für
- reine GUI-Applikationen und
- reine PUI-Applikationen (siehe
TemplatePUIApp
).
Die Basis-Klassen, die für die Implementierung des MVC-Konzepts notwendig sind, sind im Package com.pi4j.mvc.util.mvcbase
. Die Klassen sind im Code ausführlich dokumentiert.
Ein etwas fortgeschritteneres Beispiel ist die MultiControllerApp
. Sie zeigt den Einsatz und die Notwendigkeit von mehreren Controllern in einer Applikation.
Für einen einzelnen Controller gilt:
- jede Action wird asynchron und reihenfolgetreu ausgeführt
- dafür hat jeder Controller eine eigene
ConcurrentTaskQueue
integriert - das UI wird dadurch während der Ausführung einer Action nicht blockiert
- falls vom UI weitere Actions getriggert während eine Action gerade in Bearbeitung ist, werden diese in der
ConcurrentTaskQueue
aufgesammelt und ausgeführt, sobald die vorherigen Actions abgearbeitet sind.
Für einfache Applikationen reicht ein einzelner Controller meist aus.
Es gibt aber Situationen, bei denen Actions ausgeführt werden sollen, während eine andere Action noch läuft.
Die MultiControllerApp
zeigt so ein Beispiel. Es soll möglich sein, den Counter zu verändern während die LED blinkt .
- Mit einem einzigen Controller ist das nicht umsetzbar. Der Controller würde beispielsweise die 'Decrease-Action' erst ausführen, nachdem die 'Blink-Action' abgeschlossen ist.
- Bei zwei Controllern ist es jedoch einfach:
LedController
undCounterController
haben jeder eineConcurrentTaskQueue
. Actions, die die LED betreffen, werden also unabhängig von den Actions, die den Counter verändern, ausgeführt. - Es sollte zusätzlich ein
ApplicationController
implementiert werden, der die anderen Controller koordiniert und das für das UI sichtbare API zur Verfügung stellt.
Zum Starten:
launcher.class
impom.xml
auswählen<launcher.class>com.pi4j.mvc/com.pi4j.mvc.multicontrollerapp.AppStarter</launcher.class>
- mit
Run local
(oder direkt aus der IDE heraus) auf dem Laptop starten.- in
AppStarter
kann zusätzlich noch ein rudimentärer PuiEmulator gestartet werden, so dass das Zusammenspiel zwischen GUI und PUI auch auf dem Laptop überprüft werden kann.
- in
- mit
Run on Pi
auf dem RaspPi starten
Durch die klare Trennung in Model, View und Controller können grosse Teile der Applikation mittels einfachen JUnit-Tests automatisiert getestet werden. Diese Tests werden in der Regel auf dem Laptop, also nicht auf dem RaspPi, ausgeführt.
Der Controller implementiert die gesamte zur Verfügung stehende Grund-Funktionalität. Er sollte mit ausführlichen TestCases automatisch überprüft werden.
Dabei gilt es zu beachten, dass der Controller alle Veränderungen auf dem Model asynchron ausführt. Eine Überprüfung der Resultate ist also erst möglich, wenn die asynchrone Task beendet ist.
Ein Beispiel sehen Sie in ExampleControllerTest
.
Das Model ist lediglich eine Ansammlung von ObservableValues
und bietet darüber hinaus keine weitere Funktionalität. Daher sind normalerweise auch keine weiteren TestCases notwendig.
Die einzelnen PUI-Components können sehr gut via der in Pi4J integrierten MockPlatform
getestet werden. Diese Tests werden auf dem Laptop ausgeführt. Ein RaspPi ist nicht notwendig.
Beispiele für solche Component-Test sehen Sie im CrowPi-Tutorial und in diesem Projekt im Package com.pi4j.mvc.templateapp.view.pui.components
.
Das PUI ihrer Applikation kann ebenfalls gut mittels JUnit getestet werden.
Auch hier müssen die Test berücksichtigen, dass die Actions asynchron ausgeführt werden.
Ein Beispiel ist ExamplePUITest
.
This repository is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.