From: Benjamin Braatz Date: Wed, 3 Mar 2021 15:07:24 +0000 (+0100) Subject: Improve documentation X-Git-Tag: v0.3.0~79 X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=91c7cb8efb4d2ace6beabe618fd6f4bb708f7323;p=graphit%2Fcontrolpi.git Improve documentation --- diff --git a/README.md b/README.md index 4f99abf..8b301f3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # Control-Pi-Infrastruktur - ControlPi-Systeme sind Raspberry Pis, die Steuerungs-Aufgaben erledigen. Sie sind nach dem Prinzip der übergeordneten Steuerung in einem Baum organisiert, wobei jeweils höhere ControlPis die jeweils niedrigeren diff --git a/doc/index.md b/doc/index.md index 908bb58..bdfff6a 100644 --- a/doc/index.md +++ b/doc/index.md @@ -85,6 +85,72 @@ des Codes mit Tests erhält man mit der zusätzlichen Option `-v`: (venv)$ python -m doctest -v ``` +## Überblick +Die ControlPi-Infrastruktur hat zwei Haupt-Bestandteile: +- Ein Plugin-System erlaubt es, ohne Anpassungen am Code der Infrastruktur + weitere Plugin-Klassen in Python zu implementieren und (in anderen + Code-Repositories und anderen Distributions-Paketen) zum System + hinzuzufügen. + Im Modul `controlpi.plugins.util` bzw. der Datei + `controlpi/plugins/util.py` befindet sich eine Reihe nützlicher + Standard-Plugins, an denen die generelle Struktur nachvollzogen werden + kann. +- Ein Nachrichten-Bus erlaubt den Austausch von Nachrichten zwischen allen + Komponenten des Systems. + Nachrichten bestehen aus einer Menge von Schlüssel-Wert-Paaren. (Sie sind + Dictionaries bzw. Mappings.) + +Um Nachrichten zu senden und/oder zu empfangen muss ein Klient am Bus unter +einem Namen registriert werden. +Eine Plugin-Instanz kann entweder genau einen Klienten registrieren (der +dann sinnvollerweise den gleichen Namen wie die Plugin-Instanz trägt) oder +aber beliebig viele Klienten z.B. für mehrere mit einem Hardware-Bus +verbundene Geräte oder die gerade offenen Verbindungen einer +Netzwerk-Schnittstelle. + +Die Infrastruktur definiert nur die Anforderung, dass jede gesendete +Nachricht einen speziellen Schlüssel `'sender'` mit dem Namen des sendenden +Klienten als Wert enthält. +Der Bus überprüft, ob diese Art von Nachricht für diesen Sender registriert +wurde, und liefert sie nur dann aus. + +Darüber hinaus gibt es keine harten Einschränkungen an den Aufbau von +Nachrichten. +Sie müssen nicht unbedingt bestimmte Empfänger haben, sie gehören nicht +unbedingt zu einem bestimmten Typen etc. + +Einige Konventionen, die z.B. von den Plugins in `controlpi.plugins.util` +eingehalten werden, sind aber: +- Einige Nachrichten signalisieren, dass ein Ereignis eingetreten ist. + Sie haben in der Regel einen Schlüssel `'event'` mit einem Wert, der die + Art des Ereignisses passend benennt und eventuell weitere Schlüssel, die + dieses näher beschreiben. +- Einige Nachrichten sind als Kommandos an einen bestimmten Klienten + gedacht. + Sie haben in der Regel einen Schlüssel `'target'` mit dem Namen dieses + Ziel-Klienten als Wert und einen Schlüssel `'command'` mit einem + festgelegten Namen für das auszuführende Kommando und eventuell weiteren + Schlüsseln, die dann so etwas wie Parameter des Kommandos sind. + (Es können aber auch andere Klienten, beispielsweise Logger oder + Debug-Oberflächen, diese Nachrichten empfangen und verarbeiten, da + `'target'`-Schlüssel von der Infrastruktur bzw. dem Bus nicht speziell + behandelt werden.) + +Hinsichtlich der Reihenfolge in der verschiedene Klienten, die alle für die +gleiche Nachricht registriert sind, diese erhalten, werden von der +Infrastruktur keine Garantien gegeben und Plugin-Implementierungen sollten +sich nicht darauf verlassen, die einzigen zu sein, die auf eine Nachricht +reagieren. + +Die ControlPi-Infrastruktur ist auf nebenläufiger Programmierung mit +`asyncio` aufgebaut. +Diese garantiert, dass Code solange ohne Unterbrechung läuft, bis er selbst +die Kontrolle abgibt, weil er auf eine Ein- oder Ausgabe-Operation oder +ähnliches wartet. +In welcher Reihenfolge andere wartende Aufgaben dann ausgeführt werden, +sollte aber nicht vorausgesetzt werden, selbst wenn es als +Implementierungs-Detail eventuell recht sicher vorhergesehen werden kann. + ## Beispiel-Konfiguration Eine Beispiel-Konfiguration, die nur die mitgelieferten Werkzeug-Plugins verwendet, ist im Repository enthalten. Das System kann mit dieser @@ -99,7 +165,7 @@ werden.) Die Beispiel-Konfiguration sieht folgendermaßen aus: ```json { - "State": { + "Example State": { "plugin": "State" }, ``` @@ -116,7 +182,7 @@ von `state`-Ereignissen. "TriggerStateCheck": { "plugin": "Alias", "from": { "sender": "WaitCheck", "event": "finished" }, - "to": { "target": "State", "command": "get state" } + "to": { "target": "Example State", "command": "get state" } }, "TriggerWaitCheck": { "plugin": "Alias", @@ -146,7 +212,7 @@ Verschaltungen eleganter konfigurieren lassen. "TriggerStateOnOff": { "plugin": "Alias", "from": { "sender": "WaitOn", "event": "finished" }, - "to": { "target": "State", "command": "set state", "state": false } + "to": { "target": "Example State", "command": "set state", "state": false } }, "TriggerWaitOnOff": { "plugin": "Alias", @@ -160,7 +226,7 @@ Verschaltungen eleganter konfigurieren lassen. "TriggerStateOffOn": { "plugin": "Alias", "from": { "sender": "WaitOff", "event": "finished" }, - "to": { "target": "State", "command": "set state", "state": true } + "to": { "target": "Example State", "command": "set state", "state": true } }, "TriggerWaitOffOn": { "plugin": "Alias", @@ -199,7 +265,7 @@ angestoßen. "State Change Logger": { "plugin": "Log", "filter": [ - { "sender": "State", "changed": true } + { "sender": "Example State", "changed": true } ] } } @@ -214,7 +280,7 @@ stattgefunden hat, ausgibt, konfiguriert. Die Ausgabe eines Laufs dieses Beispiel-Systems sieht folgendermaßen aus: ``` $ python -m controlpi conf.json -State 'State' configured. +State 'Example State' configured. Wait 'WaitCheck' configured. Alias 'TriggerStateCheck' configured. Alias 'TriggerWaitCheck' configured. @@ -232,7 +298,7 @@ Zunächst melden alle konfigurierten Plugin-Instanzen, dass sie ihre jeweilige Konfiguration eingelesen und verarbeitet haben. ``` -State 'State' running. +State 'Example State' running. Wait 'WaitCheck' running. Alias 'TriggerStateCheck' running. Alias 'TriggerWaitCheck' running. @@ -253,7 +319,7 @@ Haupt-Routinen, da ihr Verhalten nur als Reaktion auf empfangene Nachrichten stattfindet. ``` -Debug Logger: {'sender': '', 'bus event': 'registered', 'client': 'State'} +Debug Logger: {'sender': '', 'bus event': 'registered', 'client': 'Example State'} Debug Logger: {'sender': '', 'bus event': 'registered', 'client': 'WaitCheck'} Debug Logger: {'sender': '', 'bus event': 'registered', 'client': 'TriggerStateCheck'} Debug Logger: {'sender': '', 'bus event': 'registered', 'client': 'TriggerWaitCheck'} @@ -287,45 +353,45 @@ sie wurden von `Debug Logger` empfangen. ``` Debug Logger: {'sender': 'WaitCheck', 'event': 'finished'} -Debug Logger: {'sender': 'TriggerStateCheck', 'target': 'State', 'command': 'get state'} +Debug Logger: {'sender': 'TriggerStateCheck', 'target': 'Example State', 'command': 'get state'} Debug Logger: {'sender': 'TriggerWaitCheck', 'target': 'WaitCheck', 'command': 'wait'} -Debug Logger: {'sender': 'State', 'state': False, 'changed': False} +Debug Logger: {'sender': 'Example State', 'state': False, 'changed': False} ``` Nach einer Sekunde ist die Wartezeit der `Wait`-Instanz `WaitCheck` das erste Mal abgelaufen und daraufhin werden auch die beiden -`Alias`-Nachrichten geschickt, woraufhin `State' mit seinem momentanen -Zustand (aber auch mit der Information, dass dieser sich nicht geändert -hat) antwortet. +`Alias`-Nachrichten geschickt, woraufhin `Example State' mit seinem +momentanen Zustand (aber auch mit der Information, dass dieser sich nicht +geändert hat) antwortet. ``` Debug Logger: {'sender': 'WaitOff', 'event': 'finished'} -Debug Logger: {'sender': 'TriggerStateOffOn', 'target': 'State', 'command': 'set state', 'state': True} +Debug Logger: {'sender': 'TriggerStateOffOn', 'target': 'Example State', 'command': 'set state', 'state': True} Debug Logger: {'sender': 'TriggerWaitOffOn', 'target': 'WaitOn', 'command': 'wait'} -Debug Logger: {'sender': 'State', 'state': True, 'changed': True} -State Change Logger: {'sender': 'State', 'state': True, 'changed': True} +Debug Logger: {'sender': 'Example State', 'state': True, 'changed': True} +State Change Logger: {'sender': 'Example State', 'state': True, 'changed': True} ``` Eine weitere halbe Sekunde später sorgt der Ablauf der Instanz `WaitOff` -dafür, dass `State` auf `True` gesetzt wird. Hier reagiert jetzt auch der -`State Change Logger`. +dafür, dass `Example State` auf `True` gesetzt wird. Hier reagiert jetzt +auch der `State Change Logger`. ``` Debug Logger: {'sender': 'WaitCheck', 'event': 'finished'} -Debug Logger: {'sender': 'TriggerStateCheck', 'target': 'State', 'command': 'get state'} +Debug Logger: {'sender': 'TriggerStateCheck', 'target': 'Example State', 'command': 'get state'} Debug Logger: {'sender': 'TriggerWaitCheck', 'target': 'WaitCheck', 'command': 'wait'} -Debug Logger: {'sender': 'State', 'state': True, 'changed': False} +Debug Logger: {'sender': 'Example State', 'state': True, 'changed': False} ``` Wiederum eine halbe Sekunde später wird durch Ablauf von `WaitCheck` wieder eine Abfrage des Zustands ausgelöst. ``` Debug Logger: {'sender': 'WaitOn', 'event': 'finished'} -Debug Logger: {'sender': 'TriggerStateOnOff', 'target': 'State', 'command': 'set state', 'state': False} +Debug Logger: {'sender': 'TriggerStateOnOff', 'target': 'Example State', 'command': 'set state', 'state': False} Debug Logger: {'sender': 'TriggerWaitOnOff', 'target': 'WaitOff', 'command': 'wait'} -Debug Logger: {'sender': 'State', 'state': False, 'changed': True} -State Change Logger: {'sender': 'State', 'state': False, 'changed': True} +Debug Logger: {'sender': 'Example State', 'state': False, 'changed': True} +State Change Logger: {'sender': 'Example State', 'state': False, 'changed': True} ``` -Nachdem `WaitOn` das erste Mal abgelaufen ist, wird `State` wieder auf -`False` gesetzt. +Nachdem `WaitOn` das erste Mal abgelaufen ist, wird `Example State` wieder +auf `False` gesetzt. Dies wiederholt sich jetzt solange weiter, bis das Programm abgebrochen wird.