(WS19-01)Wetterballon Design: Unterschied zwischen den Versionen

Aus Verteilte Systeme - Wiki
Wechseln zu: Navigation, Suche
K (Barometer auf GY-86 ergängzt)
(Ausführliche Designhintergründe für measurement und master hinzugefügt.)
Zeile 55: Zeile 55:
 
Eine monolithische Implementierung als eine einzige Hauptfunktion, die alle Sensoren und andere Funktionalitäten, wie das Schreiben der Daten in Dateien, überwacht, sah zunächst nach einem uneleganteren Weg aus, da ihr Hauptvorteil nur die einfachere Programmierung im Gegensatz zu Threads zu sein schien.
 
Eine monolithische Implementierung als eine einzige Hauptfunktion, die alle Sensoren und andere Funktionalitäten, wie das Schreiben der Daten in Dateien, überwacht, sah zunächst nach einem uneleganteren Weg aus, da ihr Hauptvorteil nur die einfachere Programmierung im Gegensatz zu Threads zu sein schien.
  
Neben der Lösung für die Problematik der Live-datenverarbeitung bietet dieser zentralisierte Ansatz allerdings noch einige andere Vorteile. Neben der geringeren semantischen Komplexität erleichtert das auch die Implementierung der Wartungsschnittstelle und eröffnet die Möglichkeit für weitere Dateninteraktionen.
+
Neben der Lösung für die Problematik der Live-Datenverarbeitung bietet dieser zentralisierte Ansatz allerdings noch einige andere Vorteile. Neben der geringeren semantischen Komplexität erleichtert das auch die Implementierung der Wartungsschnittstelle und eröffnet die Möglichkeit für weitere Dateninteraktionen. Es können außerdem gleiche Abläufe für jeden Sensor nicht mehr redundant programmieren zu müssen, sondern sie einmal im Master zu beschreiben.
 +
 
 +
Durch die engere Bindung der einzelnen Komponenten wurde eine größere, gut durchdachte und klar definierte Schnittstelle für die Sensoren nötig, die hilft, den Master algorithmisch übersichtlich zu halten, die aber auch auf die Eigenheiten der unterschiedlichen Sensoren eingeht.
  
 
===Komponentenentwurf===
 
===Komponentenentwurf===
 +
[[Datei:Komponentendiagramm.jpg]]
  
 +
====Detaillierte Erklärung====
  
[[Datei:Komponentendiagramm.jpg]]
+
=====Messdatenstruktur=====
 +
Die Messdaten der Sensoren müssen über Programmteile hinweg kommuniziert werden können, z. B. zur internen Verarbeitung oder zur Serialisierung. Um eine solch vielseitige Nutzung zu ermöglichen, braucht es eine Datenstruktur, die nicht nur Zahlenwerte speichert sondern ihnen auch einen Kontext gibt. Daher ist für jeden Messwert der zugehörige Messdatentyp gespeichert.
 +
 
 +
=====Sensorschnittstelle=====
 +
Alle Methoden dieser Schnittstelle sind mit Rückgabewerten definiert, die eindeutig Fehlercodes darstellen können. Das erlaubt von außen zu entscheiden, ob eine Fehlermeldung relevant ist und z. B. zum Programmabbruch führen soll, oder ob anderweitig bzw. überhaupt auf sie reagiert werden muss.
 +
 
 +
; init()
 +
: Vor allem komplexere I²C-Sensoren bieten die Möglichkeit, sie über ihre Register zu konfigurieren. Für solche Sensoren existiert diese Funktion. Dies ist die einzige Funktion, die kein informelles Zeitlimit hat. Dauert die Konfiguration länger, stellt das kein Problem dar.
 +
 
 +
; init_data(measurement*)
 +
: Um möglichst hohe Flexibilität zu wahren, wird der Speicher für die Messdaten nicht durch den Sensor selbst verwaltet. Dieser bietet Lediglich die Funktionalität, Speicherplatz in der für ihn korrekten Größe und Struktur anzulegen. Alle weiteren Methoden erwarten eine solche Struktur als Parameter.
 +
 
 +
; free_data(measurement*)
 +
: Gibt eine allokierte Messdatenstruktur wieder frei.
 +
 
 +
; measure(measurement*)
 +
: Für Sensoren, z. B. aufgrund eines Analog-Digital-Converters etwas Zeit brauchen, um nach dem Messen Daten bereit zu stellen, ist diese Funktion gemacht. Es wird eine Messdatenstruktur erwartet, um den Zeitpunkt, zu dem die tatsächliche Messung ausgeführt wurde, festzuhalten.
 +
 
 +
; get_data(measurement*)
 +
: In dieser Funktion werden die Sensordaten abgefragt und in die Messdatenstruktur gespeichert. Je komplexer der Sensor, desto mehr passiert hier. Es ist trotzdem wichtig, dass diese Funktion nicht übermäßig viel Zeit braucht.

Version vom 16. Januar 2020, 00:36 Uhr

Design

Verbaut werden folgende Sensoren:

Messung Anzahl Name Adressen Erfassungsrate Bemerkung
Temperatur innen 1 MCP9808 0x18 - 0x1F
Temperatur außen 1 TMP 117 Breakout 0x48, 0x49
Luftfeuchtigkeit außen 2 SHT 31 0x44, 0x45
Luftfeuchtigkeit innen 1 SHT 31 0x44, 0x45
Luftdruck 3 MS5611 auf GY-86 0x76, 0x77
9-Achsen 1 IMU 10DOF mit MPU-9250 & BME280 0x68, 0x69
Helligkeit 1 Si1145 0x60, ?
UV-A und UV-B 1 GYML8511, ? 0x50
Kamera Bild 1
Kamera Video 1
GPS 1
LoRaWAN 1
Buzzer 2

Problem:

Bei den vorhandenen Sensoren sind wir auf kleinere Probleme gestoßen. Alle Sensoren sind bis zu einer Minimaltemperatur von -40°C funktionsfähig. Auf dem Flug werden diese allerdings deutlich unterschritten. Wir haben uns dennoch für den Einsatz dieser Sensoren entschieden, da auf dem Markt keine geeignerten Sensoren zu finden waren. Um Messungenauigkeiten oder gar Ausfälle zu kompensieren, werden wir die Sensoren für den Außeneinsatz mehrfach verbauen. Dabei kann es zu Adresskonflikten der einzelnen Sensoren kommen. Eine Lösung dafür steht noch aus.

Hardware-Architektur

Eine Skizze der von uns verfolgten Hardwarearchitektur:

HW Architecture.png

Software-Architektur

Für die Software-Architektur stellte sich die Frage, wie die unterschiedlichen Messzyklen der Sensoren am besten zu realisieren sind. Dazu gab es hauptsächlich zwei Vorschläge.

Threads

Eine Implementierung mit Threads bietet den Vorteil, dass jeder Sensor autonom läuft, also auch in keiner Weise von den anderen Sensoren abhängig ist. Jeder Sensor muss nur eine (oder für jede Phase eine) run-Methode implementieren, die vom Master gestartet wird. Der Zugriff auf den Bus kann per Mutex synchronisiert werden. Beim Wechsel des Moduses kann der Master dann jeden Thread beenden.

Die Nachteile dieser Methode sind größtenteils erst nach einer Probeimplementierung aufgefallen. Dazu zählen neben der ohnehin höheren Komplexität paralleler Programme, dass die Abgrenzung der Sensoren untereinander auch dazu führt, dass sie auch vom Master weitestgehend entkoppelt sind. Das ist vor allem im Bezug auf Live-Datenauswertung ei Problem, da ein spezieller Sensor keinen simplen Weg hat, mit dem Master zu kommunizieren. Des weiteren führt die Anforderung der Unterbrechbarkeit in Verbindung mit der Synchronisierung per Mutex zum Problem von Race-Conditions, die, wenn nicht korrekt vorgebeugt, das komplette Programm lahmlegen könnten.

Aus diesen Gründen wurde sich gegen die Implementierung mit Threads entschieden.

Monolithisch

Eine monolithische Implementierung als eine einzige Hauptfunktion, die alle Sensoren und andere Funktionalitäten, wie das Schreiben der Daten in Dateien, überwacht, sah zunächst nach einem uneleganteren Weg aus, da ihr Hauptvorteil nur die einfachere Programmierung im Gegensatz zu Threads zu sein schien.

Neben der Lösung für die Problematik der Live-Datenverarbeitung bietet dieser zentralisierte Ansatz allerdings noch einige andere Vorteile. Neben der geringeren semantischen Komplexität erleichtert das auch die Implementierung der Wartungsschnittstelle und eröffnet die Möglichkeit für weitere Dateninteraktionen. Es können außerdem gleiche Abläufe für jeden Sensor nicht mehr redundant programmieren zu müssen, sondern sie einmal im Master zu beschreiben.

Durch die engere Bindung der einzelnen Komponenten wurde eine größere, gut durchdachte und klar definierte Schnittstelle für die Sensoren nötig, die hilft, den Master algorithmisch übersichtlich zu halten, die aber auch auf die Eigenheiten der unterschiedlichen Sensoren eingeht.

Komponentenentwurf

Komponentendiagramm.jpg

Detaillierte Erklärung

Messdatenstruktur

Die Messdaten der Sensoren müssen über Programmteile hinweg kommuniziert werden können, z. B. zur internen Verarbeitung oder zur Serialisierung. Um eine solch vielseitige Nutzung zu ermöglichen, braucht es eine Datenstruktur, die nicht nur Zahlenwerte speichert sondern ihnen auch einen Kontext gibt. Daher ist für jeden Messwert der zugehörige Messdatentyp gespeichert.

Sensorschnittstelle

Alle Methoden dieser Schnittstelle sind mit Rückgabewerten definiert, die eindeutig Fehlercodes darstellen können. Das erlaubt von außen zu entscheiden, ob eine Fehlermeldung relevant ist und z. B. zum Programmabbruch führen soll, oder ob anderweitig bzw. überhaupt auf sie reagiert werden muss.

init()
Vor allem komplexere I²C-Sensoren bieten die Möglichkeit, sie über ihre Register zu konfigurieren. Für solche Sensoren existiert diese Funktion. Dies ist die einzige Funktion, die kein informelles Zeitlimit hat. Dauert die Konfiguration länger, stellt das kein Problem dar.
init_data(measurement*)
Um möglichst hohe Flexibilität zu wahren, wird der Speicher für die Messdaten nicht durch den Sensor selbst verwaltet. Dieser bietet Lediglich die Funktionalität, Speicherplatz in der für ihn korrekten Größe und Struktur anzulegen. Alle weiteren Methoden erwarten eine solche Struktur als Parameter.
free_data(measurement*)
Gibt eine allokierte Messdatenstruktur wieder frei.
measure(measurement*)
Für Sensoren, z. B. aufgrund eines Analog-Digital-Converters etwas Zeit brauchen, um nach dem Messen Daten bereit zu stellen, ist diese Funktion gemacht. Es wird eine Messdatenstruktur erwartet, um den Zeitpunkt, zu dem die tatsächliche Messung ausgeführt wurde, festzuhalten.
get_data(measurement*)
In dieser Funktion werden die Sensordaten abgefragt und in die Messdatenstruktur gespeichert. Je komplexer der Sensor, desto mehr passiert hier. Es ist trotzdem wichtig, dass diese Funktion nicht übermäßig viel Zeit braucht.