Embedded Software Architektur in Feierlaune: Event driven


In meinem vorangegangen Blogpost bin ich auf Aspekte eingegangen, die den Entwurf einer guten Softwarearchitektur maßgeblich beeinflussen. Hier seien lediglich nochmal erwähnt:

  • Einfachheit: Einfacher ist in den meisten Fällen auch besser
  • Wartbarkeit und Erweiterbarkeit: Software ist in den seltensten Fällen nicht mal nach der ersten Auslieferung „fertig“
  • Testbarkeit: Versteht sich von selbst

Alisa ist im Zusammenhang mit C#-Toolentwicklung bereits detailliert auf den Aspekt Unit-Testing eingegangen (→ Unit testen in C#).
Aber gelten die Regeln für die Desktop-Entwicklung auch für „hardcore“-Embedded Software?

 

Schlechte Beispiele

Kraut- und Rüben-Code hat wohl jede(r) schon mal auf dem Schirm gehabt. Und sei’s, den Eigenen – zumindest während der Lehrjahre.
Da ruft Modul A Funktionen von Modul B auf. Modul B von C, ….. und von X geht’s wieder rein in A. Ein Traum!
Warum ist das „böse“? Das machen doch so viele Projekte genauso?

Abbildung 1: Architektur mit direkten Abhängigkeiten zwischen den Komponenten

 

Nun, zunächst durchblickt nach kurzer Zeit niemand mehr ein solches Kuddelmuddel. Und wenn es dann mal um maximale Laufzeit eines Funktionsaufrufs geht, ist die Bestimmung der maximalen Call-Chain (mit allen möglichen Branches!) ohne Tool-Support oder viel Aufwand nicht mehr möglich. Wenn dann noch die Gefahr besteht, dass zwischendurch ein Watchdog zuschlägt, wird damit begonnen, an allen möglichen und unmöglichen Stellen den Watchdog zu triggern.

Die Krönung ist dann: Es soll ein weiteres Modul integriert werden. Jetzt müssen an vielen Stellen Funktionsaufrufe zu diesem Modul (und von diesem in andere Module) ergänzt werden. Wer voraussagen kann, wie viele Stellen man anfassen muss und wie sich das auf die Erhöhung Komplexität oder das Laufzeitverhalten auswirkt, bekommt vom Chef den Wahrsagerpreis.

 

Lernen von den Erfolgreichen

Warum sind Bustechniken wie z.B. CAN (Controller Area Network) so erfolgreich? Nun, es gibt einen Master, ein paar Käbelchen und alle Teilnehmer lauschen. Wen’s interessiert, der schaut sich eine Nachricht an –  oder eben nicht. Soll z.B. ein Anzeigemodul integriert werden, schließt man es an den Bus an – fertig.

Frage: Was hindert einen daran, so ein einfaches Konzept auch für Software-Architekturen als Vorbild zu nehmen?

Antwort: Nix :-)

Bloß: Wie könnte so etwas aussehen?

Sender → „Gedöns“ → Empfänger

Eventbasierte Architekturen sind keine Rocket-Science und schon gar nichts Neues. Zu den bekannteren Beispielen gehörten Signal-Slot-Mechanismen (wird bei Qt verwendet). Unter der Haube ist das u.U. dann schon recht aufwändig und für den Hardcore-Embedded-Einsatz schon wieder zu mächtig. Denn wir haben es mit knallharten Randbedingugen zu tun (wobei: Das macht ja gerade den Reiz von Embedded Softwareentwicklung aus ;-) :

  • Wir müssen zumeist ohne dynamische Speicherallokationen auskommen (→ alle Daten/Objekte sind statisch bzw. einmalig beim Startup alloziert)
  • Laufzeit: Der Overhead einer solchen Architektur ist gering – aber umsonst gibt’s natürlich nichts.
  • Codegröße: Unter der Haube benötigten wir hier und da Verwaltungsinformation (z.B. für Listen). Das sollte aber kein Problem darstellen.

Aber was brauchen wir für eine einfache Event-basierte Lösung?

  • Events – ohne die geht nun mal nichts. Diese Nachrichten können einfache Enum-Werte sein, oder Strukturen/Klassen mit „payload“
  • Listener: Komponenten, die über Events informiert werden wollen, implementieren ein Listener-Interface und implementieren eine „handleEvent“-Funktion.
  • Event-Dispatcher: Diese Komponente nimmt Events entgegen und verteilt sie an alle Listener, die sich beim Dispatcher registriert haben.
  • Sender: Das sind alle Komponenten, die etwas mitzuteilen haben.

 

Abbildung 2: Event-basierte Architektur

Abbildung 3 zeigt anhand eines real existierenden Beispiels, wie die Eventbehandlung prinzipiell aussieht.
Man kann sicherlich gut erkennen, dass es kein Hexenwerk wäre, weitere Komponenten auf einen Tastendruck reagieren zu lassen.

 

Abbildung 3: Beispiel für die Behandlung eines Button-Press

 

Fertig? Nee, noch lange nicht

Mit dem vorgestellten Architekturprinzip sind wir schon recht weit gekommen und können die anfangs formulierten Ziele (Einfachheit, Wartbarkeit und Erweiterbarkeit, Testbarkeit) auf elegante Art und Weise erreichen.

Wer nun denkt, das wars, den möchte ich auf die nächsten Blogposts vertrösten. Ein Thema ist hier nämlich noch nicht angesprochen: Die Datenhaltung.
Da geht also noch ‚was :-)

Brauchen Sie Unterstützung beim Design einer Software-Architektur oder bei der Entwicklung von Embedded Software? Dann wenden Sie sich gerne an uns. Unsere erfahrenen MEDtech Ingenieurinnen und Ingenieure helfen ihnen gerne dabei ihr Medizinprodukt zu entwickeln oder offene Fragen zu klären.

Viele Grüße
Jürgen Welzenbach

Kontaktieren Sie uns!

Autor

  • Jürgen Welzenbach

    Jürgen hat nach seinem Elektrotechnikstudium in Erlangen seine Diplomarbeit in Kooperation mit einem Hersteller von ophthalmologischen Geräten und der Universitätsaugenklinik durchgeführt. In zwei Erlanger Unternehmen fand er zur Embedded Software und hat vor allem HMIs für Baumaschinen und Laboranalysegeräte entwickelt.

Auch interessant:

Bootloader Tutorial, Teil 3: Live-Update Bootloader

Im dritten und letzten Teil dieser Reihe möchte ich auf eine weitere Realisierungsmöglichkeit für einen Bootloader eingehen. Der Unterschied bei dieser Variante besteht darin, dass der Bootloader kein eigenständiges Firmware-Projekt ist, sondern in der Hauptapplikation integriert wird. Hierdurch erhält die Applikation eine Möglichkeit ein Update von sich selbst durchzuführen, ohne…