Templates
Falls Sie noch nicht sicher bei der Arbeit mit der UML sind, sollten Sie diesen Abschnitt überspringen und zu einem späteren Zeitpunkt abarbeiten.
Wiederverwendung, Architekturzentrierung, komponentenbasierte Entwicklung, Pattern, Templates, Muster… All diese Schlagworte spielen, glaubt man der Literatur, offensichtlich eine wichtige Rolle. Nur wenn man dann versucht konkret zu werden, kommt man schnell beim recht aufwendigen Mustern wie einem Observer oder einer abstrakten Fabrik an. Muster können aber auch schon viel einfacherer Natur sein. Falls Sie bereits etwas erfahrener in der Programmierung von Mikrocontrollern sind, haben Sie ein weit verbreitetes Pattern bereits oft angewendet. Es ist die MAINLOOP. So einfach können Entwurfsmuster auch daherkommen. Der Fachmann nennt solche einfachen Muster auf niedrigem Abstraktionsniveau übrigens Idiom.
Merken wir uns zunächst Folgendes: Es geht um Wiederverwendung. Dabei sollen bewährtes Verhalten oder/und bewährte Strukturen wiederverwendet werden. Einen wichtigen Wiederverwendungsmechanismus haben wir bereits kennengelernt und mehr oder weniger bewusst angewendet. Es ist die Vererbung (Generalisierung, Spezialisierung). Weitere Mechanismen sind zum Beispiel die Realisierung von Schnittstellen (Interfaces) oder generischer Klassen (Templates).
Die hier vorgestellte Vorgehensweise soll Ihnen eine praktische Anwendung dieser Konzepte ermöglichen. Zwar wurden PEC-Templates bereits vereinzelt verwendet, jedoch soll im weiteren Verlauf des Tutorials dieses Konzept entsprechend konsequenter angewendet werden. PEC-Templates sind eine spezielle Möglichkeit, in SiSy einfache Struktur- und Verhaltensmuster wiederzuverwenden.
Die Aufgabe
Es ist eine Mikrocontrolleranwendung zu entwickeln, bei der mit einem Taster ein Speaker angesteuert wird. Dabei sollen Taster und Speaker über optische Rückmeldungen verfügen. Im ersten Schritt ist die Lösung für einen „beleuchteten Taster“ zu entwickeln. Das optische Feedback soll über eine zugeordnete LED realisiert werden. Diese hat die Verarbeitung verschiedener Tasteraktionen durch kurzes Aufblitzen anzuzeigen. Zunächst sollen die Aktionen Klicken und Halten als erkannt optisch zurückgemeldet werden. Im zweiten Schritt ist der Speaker zu realisieren. Diesem ist eine LED zuzuordnen, welche solange blinkt, wie der Speaker aktiv ist. Durch das Klicken des Tasters soll der Speaker aktiviert werden. Das Halten setzt den Speaker wieder zurück.
Vorbereitung
Falls Sie jetzt noch das Klassendiagramm geöffnet haben wählen Sie im Kontextmenü (rechte Maustaste) des Diagramms den Menüpunkt nach oben. Falls das Projekt nicht mehr geöffnet ist, öffnen sie das SiSy UML-Projekt wieder. Legen Sie ein neues Klassendiagramm an und wählen Sie die Sprache ARM C++. Beachten Sie die Einstellungen für die Zielplattform XMC4500 Relax Kit. Beim Öffnen des Diagramms (rechte Maustaste, nach unten) laden Sie die Diagrammvorlage für eine PEC Applikation und fügen das Treiberpaket für den XMC4500 ein.
Grundlagen
Der Taster ist wie gehabt an Pin1.15 angeschlossen. Als Tasterbeleuchtung wollen wir die an Pin1.0 angeschlossene LED zuordnen. Wir wissen aus vorhergehenden Übungen, dass es nette Lösungen für den Taster und die LED in den Paketen des Framework zu finden gibt. Bei genauem Hinsehen bemerken wir, dass es sich bei den dargestellten Lösungen für Button und, Led nicht nur um Basisklassen im eigentlichen Sinne handelt, sondern letztlich um Templates.
Wir können mit dem, was wir bisher über die UML wissen, bereits ablesen, dass der PecButtonClickAndHold sowie die PecLed über eine Reihe von wichtigen Operationen verfügen und über alle Eigenschaften eines PecAppModul und des PecPinInput bzw. PecPinOutput. Besonders beim PecButtonClickAndHold fällt auf, dass es Operationen gibt, die öffentlich (public) sind.
+isPressed()
+waitForPress()
Und es gibt Operationen, die geschützt (protected) sind und demzufolge nicht von außen, sondern nur intern in der Klasse oder deren Ableitungen benutzt werden können. Ein weiterer auffälliger Umstand ist, dass diese Operationen auch noch kursiv gestellt sind. Das bedeutet in diesem Fall, dass diese Operationen virtueller Natur sind und überschrieben werden können. Sie sind vom Anwendungsentwickler in einer eigenen Realisierung des PecButtonClickAndHold zu überschreiben. Damit werden dann sogenannte Eventhandler für die Ereignisse „Taste klicken“ und „Taste halten“ implementiert.
#onClick()
#onHoldStart()
#onHolding()
#onHoldEnd()
Der PecButtonClickAndHold weist neben dem Sterotyp «template» und den aufgeführten Merkmalen noch eine weitere Besonderheit auf. Er verfügt über ein Attribut mit dem Merkmal {sm}. Folgen wir diesem Zustandsattribut „nach unten“, finden wir das Verhaltensmodell des Button als Zustandsmaschine.
Damit haben wir uns einen kurzen Überblick zu den vorhandenen Lösungen für Taster und LED verschafft. Wir arbeiten in der vorhandenen Diagrammvorlage weiter. Die nächste Überlegung zielt auf die Struktur der Komponente „Taster mit Beleuchtung“. Zunächst geben wir dem Ding einen Namen und legen für den Button eine entsprechende Klasse an: LightedButton. Er hat offensichtlich eine eigene LED. Diesen Umstand würdigen wir mit der Aggregation (Ganz-Teil-Beziehung) einer LED in der Buttonklasse.
Für die weitere Arbeit können wir auf vorhandene UML-Pakete zurückgreifen (Navigator / UML-Pakete / :Pec / :pec_InOut). Um die vorhandenen Lösungen für unsere neue Komponente wiederzuverwenden, ziehen wir den PecButtonClickAndHold sowie die Led in das Diagramm und verknüpfen diese mit den Bausteinen unserer Komponente. Als Verbindungstyp wählen wir die Realisierung. Die Zuordnung der konkreten Port-Pins erfolgt ebenfalls über Templates, die wir im UML-Ordner pinList finden.
Die Operationen onClick und onHoldStart fügen wir aus der Objektbibliothek in die Klasse ein. Dabei bietet uns das Werkzeug die vorhandenen virtuellen Operationen zum Überschreiben an. Bei Templates können wir alle Operationen Überschreiben.
Nun fehlt nur noch die Verbindung zur Anfangs eingefügten Diagrammvorlage. Ziehen Sie eine Verbindung von Controller zu LightedButton und weisen Sie eine Aggregation mit dem Namen +button zu.
Es lohnt sich jetzt schon mal den Code für diese Komponente zu generieren und anzuschauen. Das Generieren des Codes erfolgt über das Aktionsmenü in der Objektbibliothek (nur Erstellen). Um den Quellcode einzusehen, selektieren Sie die Klasse LightedButton und betätigen Sie die Taste F3.
class LightedButton : public PecAppModul // implements PecButtonClickAndHold, pinA00 { protected: PinInput pin; // Attribut from Template PinInput protected: ButtonLed led; // Aggregation from Model protected: uint8_t volatile holdCounter; // Attribut protected: uint8_t volatile releaseCounter; // Attribut protected: state_t state; // Attribut for State Machine enum{ state_state_Nothing=1, state_state_down, state_state_click, state_state_hold }; // States for State Machine public: bool isPressed(); // Operation from Template ButtonClickAndHolde public: void waitForPress(); // Operation from Template public: virtual void config(...); // Operation from Template PinInput public: virtual bool getState(); // Operation from Template PinInput public: void changeState_state(state_t newState); // Operation from State Machine public: LightedButton(); //Konstruktor public: ~LightedButton(); //Destruktor protected: void onClick(); // Operation from Template ButtonClickAndHolde protected: void onHoldStart(); // Operation from Template ButtonClickAndHolde protected: virtual void onTimer10ms(); // overwritten Operation from Base Class AppModul protected: virtual void onHoldEnd(); // Operation from Template ButtonClickAndHolde protected: virtual void onHolding(); // Operation from Template ButtonClickAndHolde };
Der hier gezeigte Quellcode ist etwas kompakter als der vom Codegenerator erstellte Code und zeigt auch nur die Headerdatei der generierten Klasse. Aber es lässt sich an dieser bereits erkennen, wie der Codegenerator mit den einzelnen Modellfestlegungen umgegangen ist. Die Generalisierung des Templates zum PecAppModul wurde als Vererbung umgesetzt. Alle als Realisierung ausgeführten Beziehungen haben bewirkt, dass die entsprechenden Struktur- und Verhaltensmerkmale nicht über eine Vererbung sondern durch „Hineinkopieren“ der neuen Komponente zugeordnet wurden.
Klassenmodell der Anwendung in der ersten Ausbaustufe:
Realisierung
Die Applikation selbst bleibt bei dieser Art der Programmierung etwas im Hintergrund. Sie dient als Rahmen und Moderator für die in ihr agierenden aktiven Klassen. Füllen wir jetzt also die Operationen onClick und onHoldStart mit dem erforderlichen Code.
led.flash();
LightedButton::onHoldStart:
led.flash();
Test der ersten Ausbaustufe
Übersetzen Sie das Programm. Korrigieren Sie ggf. Schreibfehler. Übertragen Sie das lauffähige Programm in den Programmspeicher des Controllers.
- Erstellen (Kompilieren und Linken)
- Brennen
Die zugeordnete LED blitzt jetzt kurz auf, wenn ein Klick- oder ein Halten-Ereignis festgestellt wurde.
Erweiterung
Eine vorgefertigte Lösung für einen einfachen Speaker ist in der hier verwendeten Bibliothek zwar schon enthalten aber wir wollen an diesem Beispiel lernen wie man eigene Templates mit SiSy erstellt. Wir setzen diese Komponente aus verfügbaren Bausteinen zusammen und ergänzen die fehlende Funktionalität. Unser einfacher Speaker soll einen beliebigen Pin mit einer bestimmten Frequenz umschalten (toggeln) um einen Ton zu erzeugen. Wenn dieser Baustein gut funktioniert, können wir den so erstellten einfachen Speaker als Lösungsmuster (Template), zum Beispiel in unsere Bibliothek, aufnehmen. Dazu legen wir uns ein Paket „eigene Muster“ an.
Achten sie darauf, dass die Option „fast UML“ deaktiviert ist. Öffnen Sie das Paket. Laden Sie keine Vorlage aus dem LibStore.
Der einfache Speaker ist ein Ausgabe-Pin und der Einfachheit halber ein PecAppModul. Wir können dann das 10 Millisekunden Ereignis zum Umschalten des Pins benutzen. Der Speaker soll mit der Operation start aktiviert werden und mit der Operation stop wieder angehalten werden. Das Ereignis onTimer10ms des PecAppModuls überschreiben wir.
Schalten Sie jetzt die Klasse SimpleSpeaker im Dialog „Definieren“ auf „Template“ um.
Erstellen Sie die Klasse wie dargestellt. Die Operationen sind wie folgt zu ergänzen:
SimpleSpeaker::start:
isActive=true;
isActive=false;
SimpleSpeaker::onTimer10ms:
if (isActive) this->toggle(); else this->off();
Das von uns vorbereitete Muster können wir jetzt in unserer Anwendung benutzen. Sie finden dies genau wie alle anderen Templates über den Navigator „UML-Pakete“ unter dem Paket eigeneMuster. Wir gehen zurück in das Klassendiagramm unserer Anwendung und ergänzen dieses wie folgt:
Beachten Sie die Verbindungen zum Speaker an Pin2.14 und der dazugehörigen LED an Pin2.15.
Ergänzen Sie die Methoden wie folgt:
led.flash(); app.speaker.start();
led.flash(); app.speaker.stop();
if (isActive) led.toggle(); else led.off();
Test der Erweiterung
Übersetzen Sie das Programm. Korrigieren Sie ggf. Schreibfehler. Übertragen Sie das lauffähige Programm in den Programmspeicher des Controllers.
- Erstellen (Kompilieren und Linken)
- Brennen
Gratulation! Sie haben ihr erstes eigenes UML-Template erstellt. Verbinden Sie den Speakter mit dem Pin2.14 und die benachbarte LED mit Pin2.15.
Sie können jetzt per Click den Speaker einschalten und durch langes Drücken des Tasters diesen wieder abschalten.
Videozusammenfassung
Erlernte und gefestigte Arbeitsschritte:
- Klassendiagramm anlegen und öffnen
- Diagrammvorlage für ARM C++ Applikation auswählen und laden
- Navigator auf UML Pakete umschalten
- gewünschte Klasse im Navigator suchen und ins Diagramm ziehen
- Klassen aggregieren
- eine eigene Klasse konstruieren
- Templates anlegen und Realisierungsparameter festlegen
- Operationen anlegen und in eine Klasse/Template einfügen
- Operationen einer Basisklasse überschreiben
- Attribute einer Klasse anlegen
- Klassen und Templates zu Komponenten zusammenbauen
- den nötigen Quellcode in den Operationen erstellen
- Erstellen und Brennen einer ARM Applikation im Klassendiagramm
Und hier diesen Abschnitt wiederum als Videozusammenfassung.
Übung
Erweitern Sie die Anwendung selbständig, so dass die rote LED des Speakers solange leuchtet, wie der Speaker aktiv ist.