Rundinstrument

Unser Rundinstrument: RoundedIndicator

Als Basis unseres Rundinstrumentes nehmen wir javax.swing.JComponent und überschreiben die
Methode protected void paintComponent(Graphics g). Da diese Methode keinerlei Implementierung besitzt, ist in unserer Testanwendung nur eine weisse Fläche zu sehen. Sie repräsentiert den Komponentencontainer in den wir unseren RoundedIndicator eingefügt haben.

 

alt

  Testanwendung mit dem weissen Komponentencontainer.

alt

Hintergrund mit Rahmen

In Zeile 12 und 13 definieren wir die Dimension unseres Instrumentes. In den nächsten beiden Zeilen legen wir zwei Stacks an die Zeichenobjekte zwischenspeichern. Die Stacks initialisieren wir im neu angelegten Konstruktor (Zeile 17 bis 21). In Zeile 26 konvertieren wir das Grafiksystem auf Graphics2D, weil wir dadurch die Möglichkeit haben auf Koordinatentransformationen, Strichdicken usw… zuzugreifen.
Als nächstes übergeben wir den Stacks das Grafiksystem. Jetzt sind die Stacks in der Lage Zeichenobjekte aus dem System zwischenzuspeichern und wieder zu setzen.
Funktionen die allgemein verügbar sein sollen werden normalerweise in sog. Utility – Klassen ausgelagert. Methoden zweier solcher Klassen werden in Zeile 30 und 31 aufgerufen. Zunächst weisen wir das Grafiksystem an beim zeichnen von Konturen die Kantenglättung (Antialiasing) zu benutzen. Eine speziell für unsere Senderanwendung definierte Klasse zeichnet den Hintergrund und den Rahmen aller unserer Instrumente. Diese Methode erläutern wir nach diesem Quelltextausschnitt.

alt

Die Utility Klasse Cockpit

In dieser Hilfsklasse sind Methoden und Konstanten definiert die zum Zeichnen unserer Cockpitinstrumente benötigt werden.

Die Stacks für das Koordinatensystem und die Farbe werden mit dem Graphics2D Objekt versorgt und sichern anschliessend das momentan gesetzte Koordinatensystem und die Farbe. In Zeile 180 verschieben wir den Nullpunkt in die linke obere Ecke der zu zeichnenden Komponente. CockpitBorder implementiert das Interface javax.swing.border.Border und ist somit in der Lage Rahmen auf alle üblichen Swing Komponenten zu zeichnen. Unsere Implementierung ist ein Singleton der uns Zusatzinformationen bereitstellt (Zeile 182, 183). Wir setzen unsere Farbkonstante und füllen damit ein abgerundetes Rechteck (Zeile 185). In Zeile 186 wird der graue Rahmen darübergezeichnet. Zum Abschluss restaurieren wir das Koordinatensystem und die Farbe.
Das Ergebnis ist ein schwarzer Hintergrund mit einem grauen Rahmen.

alt

Hintergrund mit Rahmen.

alt

Der Skalenhintergrund (Zifferblatt)

Um den Skalenhintergrund zu zeichnen instanzieren wir die Klasse java.awt.geom.Arc2D.Double, und versorgen den Konstruktor mit Position, Ausdehnung, Start / Endwinkel sowie den Typ des Kreises (Zeile 28). Die Hilfsmethode _setOriginToLeftTop(Graphics2D g) verschiebt das Koordinatensystem. Das brauchen wir noch öfter, deshalb ist der Einzeiler in dieser Methode ausgelagert. Der gesamte Zeichenvorgang des Zifferblattes ist ebenfalls in einer eigenen Methode definiert. Diese Methode schreiben wir als letzte Anweisung in die Methode paintComponent(Graphics pG) (Zeile 40).
Diese Vorgehensweise wird bis zum Schluss beibehalten und wird deshalb nachfolgend nicht mehr näher beschrieben.

Als erstes sichern wir wieder das Koordinatensystem, anschliessend erfolgt eine Translation auf die linke obere Ecke unseres Rundinstrumentes. Wir setzen die Farbe und füllen den Bereich mit der Kontur unseres Bogens. Mit der letzten Anweisung restaurieren wir das Koordinatensystem (Zeile 60).

alt

Das Zifferblatt

alt

Skalenbogen mit Beschriftung

Im Konstruktor wird wieder ein java.awt.geom.Arc2D.Double definiert, allerdings mit anderen Winkelangaben und Typ. Wir verfahren wie vorher, bis zur Zeile 91: Jetzt sichern wir die aktuelle Strichdicke und setzen einen Strich mit der Dicke 2 Pixel. In der Zeile 93 rufen wir diesmal g.draw() anstatt g.fill() auf. Das hat zur Folge dass der Bogen jetzt als Kontur und nicht als Fläche gezeichnet wird.
Das Koordinatensystem wird nochmals verschoben, so dass der Nullpunkt am Knick des Zifferblattes ist.  Die Variable ticMark ist unser Skalenstrich der in der for() Schleife fünf mal im Abstand von 43° gezeichnet wird. Die tatsächliche Position des Striches ist bei der Zahl 10, bei jedem Schleifendurchlauf wird das Koordinatensystem um 43° gegen den Uhrzeigersinn verdreht (Zeile 101).

Zurücksetzen des Koordinatensystems (Zeile 103), setzen einer bestimmten Schriftart und zeichnen der Skalenbeschriftungen relativ zum Koordinatenmittelpunkt des Zifferblattes. Abschliessend restaurieren wir wieder den Grafikkontext.

alt

Zifferblatt mit Skala und Beschriftung

alt

Die Schubbegrenzung

Bei der Niederschrift dieses Textes befinden wir uns noch in der Erprobungsphase und begrenzen deshalb den Maximalschub der den Motoren zur Verfügung steht. Diesen Parameter machen wir über einen roten Skalenbogen sichtbar, der exakt an der gleichen Position über der grauen Skala gezeichnet wird. In Zeile 146 berechnen wir den Winkel mit dem aktuellen Grenzwert limit und setzen das Ergebnis über die Methode setAngleExtent(). Der Bogen wird dann mit einer Strichdicke von 3 Pixeln in roter Farbe gezeichnet. Es werden nur diejenigen Skalenstriche rot gezeichnet die den Winkel einschliessen (Zeile 161, 162).

alt

Die Schubbegrenzung bei 50%

alt

Der momentan gesetzte Schub

Diesen Wert berechnet das Modul FlightControlUnit für jeden der vier Motoren. In einem separaten Thread lesen wir diese Werte aus dem Phidget Treiber aus und schicken sie an den Sender zurück, dort werden sie an die vier Rundinstrumente verteilt. Der Wert wird in der Variablen actualValue gehalten und in der Methode _rotate() die entsprechende Koordinatendrehung berechnet. Dann zeichnen des Line2D.Double Objektes (Zeile 193) und wie immer aufräumen.

alt

Motorschub von 10%

alt

Der Schubhebel

Der Schub wird über die Tastatur des Senders kommandiert. Er ist für alle vier Motoren gleich. Den Prozentwert senden wir an den Empfänger sowie an die vier Rundinstrumente. Die Variable thrust enthält den Wert. Die Ungenauigkeit von einem Pixel relativ zum Skalenbogen gleichen wir durch eine Transformation nach rechts um ein Pixel aus. Der Schubhebel wird durch zwei blaugraue konzentrische Kreise repräsentiert (Zeile 214 und 217).

alt

Schubhebelstellung von 25%

alt

Die Digitalanzeige

Den momentan gesetzten Schub visualisieren wir zusätzlich noch durch eine numerische Anzeige. In Zeile 241 zeichnen wir einen Rahmen um die Anzeige. In Zeile 242 erzeugen wir aus dem double Wert einen formatierten String. Von Zeile 243 bis 249 werden die grossen Ziffern vor dem Dezimalpunkt aufbereitet und dargestellt. In den beiden vorletzten Zeilen zeichnen wir noch die Dezimalstellen.

alt

Die Digitalanzeige bei 10% Schub

alt

Der EventBus 

Der EventBus ersetzt in unserer GUI das Listener Konzept. Ein Objekt das Nachrichten aussendet braucht deshalb keine Referenz auf einen Listener. Das sendende Objekt legt seine Nachricht unter einem bestimmten Schlüssel (Topic) auf den Bus: EventBus.publish(“limit”,42).
Ein Objekt das Interesse an Nachrichten dieses Topics hat muss die Schnittstelle EventTopicSubscriber implementieren (hier als innere Klasse im RoundedIndicator). Es ist eine einzige Methode zu implementieren: onEvent(topic, data). Diese Methode wird vom EventBus aufgerufen wenn eine Nachricht mit dem entsprechenden Topic vorliegt. Wir klammern die Zahl zunächst zwischen 0% und 100% (Zeile 281). Je nach Topic wird die Zahl an eine unserer Variablen weitergeleitet (Zeile 283 bis 288). Abschliessend teilen wir dem Grafiksystem mit dass die Komponente neu gezeichnet werden muss, was einen Aufruf von paintComponent() nach sich zieht.

alt

Subscriber registrieren

Wir registrieren den Subscriber im Konstruktor unseres Rundinstrumentes und leiten die neu angelegten Parameter für unsere drei Werte direkt weiter. RoundedIndicator ist jetzt voll funktionsfähig.

alt

Daten versenden

Das versenden der Daten ist hier am Beispiel eines Sliders unserer Testumgebung dargestellt.

alt

Das vollständig implementierte Rundinstrument

Unser Rundinstrument zeigt hier die Werte aus den Slidern (“desired” = Schubhebel hellblau, “actual” = vom Motortreiber grün, “limit” = Schubbegrenzung rot).

alt

 

Robotik in Java.