(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
Die Beispiel-Konfiguration sieht folgendermaßen aus:
```json
{
- "State": {
+ "Example State": {
"plugin": "State"
},
```
"TriggerStateCheck": {
"plugin": "Alias",
"from": { "sender": "WaitCheck", "event": "finished" },
- "to": { "target": "State", "command": "get state" }
+ "to": { "target": "Example State", "command": "get state" }
},
"TriggerWaitCheck": {
"plugin": "Alias",
"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",
"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",
"State Change Logger": {
"plugin": "Log",
"filter": [
- { "sender": "State", "changed": true }
+ { "sender": "Example State", "changed": true }
]
}
}
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.
jeweilige Konfiguration eingelesen und verarbeitet haben.
```
-State 'State' running.
+State 'Example State' running.
Wait 'WaitCheck' running.
Alias 'TriggerStateCheck' running.
Alias 'TriggerWaitCheck' running.
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'}
```
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.