Improve documentation
authorBenjamin Braatz <benjamin.braatz@graph-it.com>
Wed, 3 Mar 2021 15:07:24 +0000 (16:07 +0100)
committerBenjamin Braatz <benjamin.braatz@graph-it.com>
Wed, 3 Mar 2021 15:07:24 +0000 (16:07 +0100)
README.md
doc/index.md

index 4f99abf5d2a3dabf39f2410de22e8a92c04e5b44..8b301f3ba35c316079993b3ca1009dad08b2faed 100644 (file)
--- 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
index 908bb58eb55bb53963c0e4d9f474c57ea2dfb7d7..bdfff6a277cff44c839799e4f4b193499d86011b 100644 (file)
@@ -85,6 +85,72 @@ des Codes mit Tests erhält man mit der zusätzlichen Option `-v`:
 (venv)$ python -m doctest -v <Pfad zur Code-Datei>
 ```
 
+## Ü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.