Diese Seite ist Teil des Projekts Automatische Gewächshauslüftung.

Implementierung der GUI-Komponenten

 Screenshot der App

Im Folgenden werden die Besonderheiten der GUI-Steuerung beschrieben

Slider-Steuerung

Die Anzeige und Eingabe der numerischen Parameter der Gewächshaussteuerung erfolgt über fünf gleich aufgebaute Blöcke, mit einem Slider als zentrales Element. Jeder Block wird in einem HorizontalAlignment-Element gekapselt. In jedem HorizontalAlignment-Element gibt es ein Label zur Anzeige des Parameternamens, einen Slider zur Anzeige des aktuellen Parameterwertes und zu dessen Änderung und ein Label zur numerischen Anzeige des Parameterwerts.  Die Namen der Elemente ist jeweils eine Kombination aus einem Elementkürzel ha.., txt.., sl.., dsp.. und dem Parameterkürzel ..MaxTemp, ..MinTemp, ..MaxHum, ..PhaseDuration und ..BtTimeout, also z.B. haMaxTemp ... dspBtTimeout.

Slider Konfiguration  
ha<Param>   Width: Fill Parent
txt<Param>   FontSize: 12, TextAlignment: right, Width: 80 Pixel
sl<Param>   Width: Fill Parent, MinValue u. MaxValue: s.u.
dsp<Param>   FontSize: 18, TextAlignment: right, Width: 28 Pixel

Zu beachten ist, dass die ThumbPosition eine Fließkommazahl, also mit Dezimalstellen behaftet ist. In dieser App sind alle Steuerparameter Ganzzahlen, deshalb wird der immer der gerundete Wert benutzt. Das muss bei der Wahl der Minimal- und Maximalwerte berücksichtigt werden. Stellt man diese auf die sachlich korrekten Werte ein, ist der Slider-Bereich für den kleinsten und den größten Wert nur halb so groß, wie der für die anderen Werte.

   Beispiel: MinValue = 1.0, MaxValue = 3
      round(ThumbPosition) = 1: im Bereich 1,00 .. 1,49 ≙ 0,5 Einheiten
      round(ThumbPosition) = 2: im Bereich 1,50 .. 2,49 ≙ 1,0 Einheiten
      round(ThumbPosition) = 3: im Bereich 2,50 .. 3,00 ≙ 0,5 Einheiten

MinValue muss also kleiner (-0,45) und MaxValue größer (+0,45) gewählt werden: MinValue = 0.55, MaxValue 3.45. Eigentlich wären ±0,49 besser, aber wegen möglicher Rundungsfehler ist etwas Reserve nötig.

   Beispiel: MinValue = 0.55, MaxValue = 3.45
      round(ThumbPosition) = 1: im Bereich 0,45 .. 1,49 ≙ 0,95 Einheiten
      round(ThumbPosition) = 2: im Bereich 1,50 .. 2,49 ≙ 1,00 Einheiten
      round(ThumbPosition) = 3: im Bereich 2,50 .. 3,45 ≙ 0,95 Einheiten
  Das ist fast identisch. Den kleine verbleidende Unterschied ist nicht merkbar.

Die Steuerung der fünf Slider für die Einstellung der Steuerparameter ist für alle identisch und deshalb in die Prozedur handlePositionChanged ausgelagert.

Slider Steuerung

Die Slider-Steuerung ist etwas komplizierter. Lider lassen sich die Slider im App Inventor nicht sperren. Im then-Zweig der äußeren if-Anweisung wird verhindert, dass sich die Slider im Zustand "nicht verbunden" bewegen. Sie bleiben am linken Rand stehen, in dem nach jeder Änderung der ThumbPostion wieder der kleinste Wert zugewiesen wird MinValue.

Slider Steuerung

In dem else-Zweig wird der Fall behandelt, dass die ThumbPosition zulässig geändert wurde. Zunächst wird das zum Slider gehörende Label aktualisiert. Wegen der Fließkommaeigenschaft von ThumbPosition muss der gerundete Wert benutzt werden. Wenn die ThumbPosition durch Code geändert wurde, wurde vorher SlidersEnabled auf false gesetzt. Wurde jedoch die Position durch eine Anwenderaktion geändert (SlidersEnabled ist true), müssen die neuen Einstellungen übertragen werden. Deshalb wird enterUpdatePending aufgerufen.


Modus- und manuelle Lüftersteuerung

Modus und Lüfterzustand können nur gemeinsam sinnvoll gesteuert werden. Entweder ist die Automatik eingeschaltet, dann kann kein Einfluss auf den Lüfter genommen werden. Oder der Lüfter soll an oder aus sein, dann muss der Modus "manuelle Steuerung sein. Deshalb gibt es die drei Schaltflächen "Lüfter an", "Lüfter aus" und "Automatik".

Schaltflächen zur Modus- und manuellen Steuerung

Die zugehörigen Funktionsblöcke sind für alles drei Schaltflächen im Wesentlichen identisch.

Modus- und manuelle Lüftersteuerung

Der Standard-Timer wird abgeschaltet, damit nicht irgendein asynchrones Ereignis dazwischen pfuscht. Der Steuercode "M", "O" oder "A" je nach Schaltfläche wird übertragen und die Anzeige angepasst. Im Zustand mit der Nr. 2 (UpdatePending) wird durch Aufruf der Prozedur updateParameters die Übertragung der weiteren Steuerparameter veranlasst. updateParameters ruft seinerseits enterConnected auf, dass den Timer wieder initialisiert. In den anderen Zuständen muss der Timer an dieser Stelle wieder aktiviert werden.


Animation

Die fertige App unterstützt leider keine animierten GIFs. Ein Timer, der die einzelnen Bilder nacheinander anzeigt, löst das Problem.

Animation

Zu jedem Zustand gibt es eine eigene Bildsequenz. Zur Vereinfachung werden diese Sequenzen in einer zweidimensionalen Liste abgelegt. Bei jedem Timer-Ereignis wird der Listenindex AnimationCnt inkrementiert. AnimationCnt und der Statusindex State bestimmen, welcher Listeneintrag in der Image-Komponente imgConnection angezeigt wird. Der Wechsel erfolgt nach jeweils 300 ms.