},
```
Ein `State`-Plugin speichert intern einen Booleschen Wert (`True` oder
-`False`) und stellt Kommandos `get state` und `set state` zur Verfügung, um
-diesen Wert abzufragen oder zu ändern und reagiert darauf durch das Senden
-von `state`-Ereignissen.
+`False`) und stellt Kommandos `"get state"` und `"set state"` zur
+Verfügung, um diesen Wert abzufragen oder zu ändern und reagiert darauf
+durch das Senden von `"state"`-Ereignissen.
```json
- "WaitCheck": {
- "plugin": "Wait",
- "seconds": 1.0
+ "Example GenericWait": {
+ "plugin": "GenericWait"
},
- "TriggerStateCheck": {
+```
+Ein `GenericWait`-Plugin reagiert auf `"wait"`-Kommandos, die Schlüssel
+`"seconds"` und `"id"` enthalten, indem es die angegebene Anzahl Sekunden
+wartet und dann ein `"finished"`-Ereignis mit dem angegebenen `"id"`-String
+sendet.
+
+```json
+ "Trigger Wait Check": {
"plugin": "Alias",
- "from": { "sender": "WaitCheck", "event": "finished" },
- "to": { "target": "Example State", "command": "get state" }
+ "from": {
+ "sender": { "const": "Example GenericWait" },
+ "id": { "const": "Check" }, "event": { "const": "finished" }
+ },
+ "to": {
+ "target": "Example GenericWait", "seconds": 1.0,
+ "id": "Check", "command": "wait"
+ }
},
- "TriggerWaitCheck": {
+```
+In der Beispiel-Konfiguration verwenden wir `Alias`-Instanzen, um bei
+Ablauf verschiedener Wartezeiten Aktionen auszulösen.
+Hier wird bei Ablauf einer Wartezeit mit `"id": "Check"` sofort eine neue
+Wartezeit von einer Sekunde mit der gleichen `"id"` gestartet.
+
+```json
+ "Trigger State Check": {
"plugin": "Alias",
- "from": { "sender": "WaitCheck", "event": "finished" },
- "to": { "target": "WaitCheck", "command": "wait" }
+ "from": {
+ "sender": { "const": "Example GenericWait" },
+ "id": { "const": "Check" }, "event": { "const": "finished" }
+ },
+ "to": {
+ "target": "Example State", "command": "get state"
+ }
},
```
-Ein `Wait`-Plugin stellt ein Kommando `wait` zur Verfügung und wartet nach
-Erhalt dieses Kommandos die in der Konfiguration angegebene Zahl an
-Sekunden, bevor es ein `finished`-Ereignis sendet. Die beiden
-`Alias`-Plugin-Instanzen übersetzen dieses Ereignis zum Einen in eine
-Abfrage an das `State`-Plugin nach seinem momentanen Zustand, zum Anderen
-in einen neuen Start des `Wait`-Plugins selbst. Es wird also im Endeffekt
-jede Sekunde einmal der Zustand des `State`-Plugins abgefragt.
+Außerdem wird bei Ablauf von `"id": "Check"` einmal der momentane Zustand
+von `"Example State"` mit `"get state"` abgefragt.
+Es wird also im Endeffekt jede Sekunde einmal der Zustand des
+`State`-Plugins abgefragt.
Statt `Alias`-Instanzen zu verwenden, um Ereignisse direkt in Kommandos
anderer (oder desselben) Plugins zu übersetzen, werden im Paket
-[controlpi-statemachine](https://docs.graph-it.com/graphit/controlpi-statemachine)
+[controlpi-statemachine](https://docs.graph-it.com/graphit/controlpi-statemachine/)
Zustands-Maschinen zur Verfügung gestellt, mit denen sich komplexere
Verschaltungen eleganter konfigurieren lassen.
```json
- "WaitOn": {
- "plugin": "Wait",
- "seconds": 1.5
- },
- "TriggerStateOnOff": {
+ "Trigger Wait On Off": {
"plugin": "Alias",
- "from": { "sender": "WaitOn", "event": "finished" },
- "to": { "target": "Example State", "command": "set state", "new state": false }
+ "from": {
+ "sender": { "const": "Example GenericWait" },
+ "id": { "const": "On" }, "event": { "const": "finished" }
+ },
+ "to": {
+ "target": "Example GenericWait", "seconds": 1.5,
+ "id": "Off", "command": "wait"
+ }
},
- "TriggerWaitOnOff": {
+ "Trigger Wait Off On": {
"plugin": "Alias",
- "from": { "sender": "WaitOn", "event": "finished" },
- "to": { "target": "WaitOff", "command": "wait" }
+ "from": {
+ "sender": { "const": "Example GenericWait" },
+ "id": { "const": "Off" }, "event": { "const": "finished" }
+ },
+ "to": {
+ "target": "Example GenericWait", "seconds": 1.5,
+ "id": "On", "command": "wait"
+ }
},
- "WaitOff": {
- "plugin": "Wait",
- "seconds": 1.5
- },
- "TriggerStateOffOn": {
+```
+Mit diesen beiden `Alias`-Instanzen wird dafür gesorgt, dass bei Ablauf
+einer `"On"`-Wartezeit eine `"Off"`-Wartezeit von anderthalb Sekunden
+gestartet wird und umgekehrt.
+
+```json
+ "Trigger State On Off": {
"plugin": "Alias",
- "from": { "sender": "WaitOff", "event": "finished" },
- "to": { "target": "Example State", "command": "set state", "new state": true }
+ "from": {
+ "sender": { "const": "Example GenericWait" },
+ "id": { "const": "On" }, "event": { "const": "finished" }
+ },
+ "to": {
+ "target": "Example State", "command": "set state",
+ "new state": false
+ }
},
- "TriggerWaitOffOn": {
+ "Trigger State Off On": {
"plugin": "Alias",
- "from": { "sender": "WaitOff", "event": "finished" },
- "to": { "target": "WaitOn", "command": "wait" }
+ "from": {
+ "sender": { "const": "Example GenericWait" },
+ "id": { "const": "Off" }, "event": { "const": "finished" }
+ },
+ "to": {
+ "target": "Example State", "command": "set state",
+ "new state": true
+ }
},
```
-Mit `WaitOn` und `WaitOff` werden zwei `Wait`-Instanzen so konfiguriert,
-dass sie sich gegenseitig aufrufen, wobei die eine den Zustand der
-`State`-Instanz auf `False`, die andere dann wieder auf `True` setzt.
+Bei Ablauf einer `"On"`-Wartezeit wird `"Example State"` mit `"set state"`
+ausgeschaltet, bei Ablauf von `"Off"` eingeschaltet.
+Zusammen sorgt dies dafür, dass `"Example State"` alle anderthalb Sekunden
+den Zustand wechelt.
```json
"Test Procedure": {
"plugin": "Init",
"messages": [
- { "event": "started" },
- { "target": "WaitOff", "command": "wait" },
- { "target": "WaitCheck", "command": "wait" },
- { "event": "stopped" }
+ { "target": "Example GenericWait", "seconds": 1.5,
+ "id": "Off", "command": "wait" },
+ { "target": "Example GenericWait", "seconds": 1.0,
+ "id": "Check", "command": "wait" }
]
},
```
`Init`-Instanzen senden einfach am Beginn der Ausführung des
-ControlPi-Systems eine konfigurierte Liste von Nachrichten. Hier werden die
-beiden `Wait`-Instanzen für das Setzen und das Auslesen des
-`State`-Zustandes (die sich danach endlos selbst auslösen) das erste Mal
-angestoßen.
+ControlPi-Systems eine konfigurierte Liste von Nachrichten.
+Hier werden eine erste `"Off"`- und eine erste `"Check"`-Wartezeit
+gestartet, die sich durch die oben beschriebenen `Alias`-Instanzen dann
+endlos selbst auslösen.
```json
"Debug Logger": {
- "plugin": "Log",
- "filter": [
- {}
- ]
+ "plugin": "Log", "filter": [{}]
},
"State Change Logger": {
"plugin": "Log",
"filter": [
- { "sender": "Example State", "event": "changed" }
+ {
+ "sender": { "const": "Example State" },
+ "event": { "const": "changed" }
+ }
]
}
}
```
`Log`-Instanzen geben einfach durch einen konfigurierten Filter bestimmte
Nachrichten auf der Konsole (oder, falls das System als systemd-Unit läuft,
-im System-Journal) aus. Hier wird eine Instanz `Debug Logger`, die alle
-Nachrichten ausgibt, und eine Instanz `State Change Logger`, die nur
-Nachrichten der `State`-Instanz und diese auch nur, wenn eine Änderung
-stattgefunden hat, ausgibt, konfiguriert.
+im System-Journal) aus.
+Hier wird eine Instanz `"Debug Logger"`, die alle Nachrichten ausgibt, und
+eine Instanz `"State Change Logger"`, die nur `"changed"`-Ereignisse der
+`State`-Instanz ausgibt, konfiguriert.
Die Ausgabe eines Laufs dieses Beispiel-Systems sieht folgendermaßen aus:
```
$ python -m controlpi conf.json
-State 'Example State' configured.
-Wait 'WaitCheck' configured.
-Alias 'TriggerStateCheck' configured.
-Alias 'TriggerWaitCheck' configured.
-Wait 'WaitOn' configured.
-Alias 'TriggerStateOnOff' configured.
-Alias 'TriggerWaitOnOff' configured.
-Wait 'WaitOff' configured.
-Alias 'TriggerStateOffOn' configured.
-Alias 'TriggerWaitOffOn' configured.
-Init 'Test Procedure' configured.
-Log 'Debug Logger' configured.
-Log 'State Change Logger' configured.
-```
-Zunächst melden alle konfigurierten Plugin-Instanzen, dass sie ihre
-jeweilige Konfiguration eingelesen und verarbeitet haben.
-
-```
-State 'Example State' running.
-Wait 'WaitCheck' running.
-Alias 'TriggerStateCheck' running.
-Alias 'TriggerWaitCheck' running.
-Wait 'WaitOn' running.
-Alias 'TriggerStateOnOff' running.
-Alias 'TriggerWaitOnOff' running.
-Wait 'WaitOff' running.
-Alias 'TriggerStateOffOn' running.
-Alias 'TriggerWaitOffOn' running.
-Init 'Test Procedure' running.
-Log 'Debug Logger' running.
-Log 'State Change Logger' running.
-```
-Dann melden wiederum alle, dass ihre Haupt-Routinen jetzt laufen. Bis auf
-die `Init`-Instanz, die die konfigurierten Nachrichten in dieser
-Hauptroutine abschickt, haben alle bisher gezeigten Plugins leere
-Haupt-Routinen, da ihr Verhalten nur als Reaktion auf empfangene
-Nachrichten stattfindet.
-
-```
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'Example State',
- 'sends': [{'event': 'changed', 'state': "<class 'bool'>"},
- {'state': "<class 'bool'>"}],
- 'receives': [{'target': 'Example State', 'command': 'get state'},
- {'target': 'Example State', 'command': 'set state',
- 'new state': "<class 'bool'>"}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'WaitCheck',
- 'sends': [{'event': 'finished'}],
- 'receives': [{'target': 'WaitCheck', 'command': 'wait'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'TriggerStateCheck',
- 'sends': [{'target': 'Example State', 'command': 'get state'}],
- 'receives': [{'sender': 'WaitCheck', 'event': 'finished'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'TriggerWaitCheck',
- 'sends': [{'target': 'WaitCheck', 'command': 'wait'}],
- 'receives': [{'sender': 'WaitCheck', 'event': 'finished'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'WaitOn',
- 'sends': [{'event': 'finished'}],
- 'receives': [{'target': 'WaitOn', 'command': 'wait'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'TriggerStateOnOff',
- 'sends': [{'target': 'Example State', 'command': 'set state', 'new state': False}],
- 'receives': [{'sender': 'WaitOn', 'event': 'finished'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'TriggerWaitOnOff',
- 'sends': [{'target': 'WaitOff', 'command': 'wait'}],
- 'receives': [{'sender': 'WaitOn', 'event': 'finished'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'WaitOff',
- 'sends': [{'event': 'finished'}],
- 'receives': [{'target': 'WaitOff', 'command': 'wait'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'TriggerStateOffOn',
- 'sends': [{'target': 'Example State', 'command': 'set state', 'new state': True}],
- 'receives': [{'sender': 'WaitOff', 'event': 'finished'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'TriggerWaitOffOn',
- 'sends': [{'target': 'WaitOn', 'command': 'wait'}],
- 'receives': [{'sender': 'WaitOff', 'event': 'finished'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'Test Procedure',
- 'sends': [{'event': 'started'}, {'event': 'stopped'},
- {'target': 'WaitCheck', 'command': 'wait'},
- {'target': 'WaitOff', 'command': 'wait'},
- {'target': 'Test Procedure', 'command': 'execute'}],
- 'receives': [{'target': 'Test Procedure', 'command': 'execute'}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'Debug Logger',
- 'sends': [], 'receives': [{}]}
-Debug Logger: {'sender': '', 'event': 'registered', 'client': 'State Change Logger',
- 'sends': [], 'receives': [{'sender': 'Example State', 'event': 'changed'}]}
+Debug Logger: {'sender': '', 'event': 'registered',
+ 'client': 'Example State', 'plugin': 'State',
+ 'sends': [{'event': {'const': 'changed'},
+ 'state': {'type': 'boolean'}},
+ {'state': {'type': 'boolean'}}],
+ 'receives': [{'target': {'const': 'Example State'},
+ 'command': {'const': 'get state'}},
+ {'target': {'const': 'Example State'},
+ 'command': {'const': 'set state'},
+ 'new state': {'type': 'boolean'}}]}
+...
+Debug Logger: {'sender': '', 'event': 'registered',
+ 'client': 'State Change Logger', 'plugin': 'Log',
+ 'sends': [],
+ 'receives': [{'sender': {'const': 'Example State'},
+ 'event': {'const': 'changed'}}]}
```
Die Instanz `Debug Logger` zeigt uns alle im System verschickten
-Nachrichten an. Zunächst meldet der Nachrichten-Bus selbst alle
-Registrierungen der konfigurierten Plugins als Klienten des Busses. In
-unserem Fall gibt es für jedes Plugin genau einen Klienten. Für komplexere
-Plugins kann es aber durchaus mehrere (oder keine) Klienten für ein Plugin
-geben, beispielsweise für jede gerade offfene Verbindung zu einer
-Netzwerkschnittstelle.
-
-```
-Debug Logger: {'sender': 'Test Procedure', 'target': 'Test Procedure', 'command': 'execute'}
-Debug Logger: {'sender': 'Test Procedure', 'event': 'started'}
-Debug Logger: {'sender': 'Test Procedure', 'target': 'WaitOff', 'command': 'wait'}
-Debug Logger: {'sender': 'Test Procedure', 'target': 'WaitCheck', 'command': 'wait'}
-Debug Logger: {'sender': 'Test Procedure', 'event': 'stopped'}
-```
-Die `Init`-Instanz `Test Procedure` hat alle ihre Nachrichten gesendet und
-sie wurden von `Debug Logger` empfangen.
-
-```
-Debug Logger: {'sender': 'WaitCheck', 'event': 'finished'}
-Debug Logger: {'sender': 'TriggerStateCheck', 'target': 'Example State', 'command': 'get state'}
-Debug Logger: {'sender': 'TriggerWaitCheck', 'target': 'WaitCheck', 'command': 'wait'}
+Nachrichten an.
+Zunächst meldet der Nachrichten-Bus selbst alle Registrierungen der
+konfigurierten Plugins als Klienten des Busses mit den jeweils für sie
+registrierten gesendeten und empfangenen Nachrichten-Vorlagen.
+In unserem Fall gibt es für jedes Plugin genau einen Klienten.
+Für komplexere Plugins kann es aber durchaus mehrere (oder keine) Klienten
+für ein Plugin geben, beispielsweise für jede gerade offfene Verbindung zu
+einer Netzwerkschnittstelle.
+
+```
+Debug Logger: {'sender': 'Test Procedure', 'target': 'Example GenericWait',
+ 'seconds': 1.5, 'id': 'Off', 'command': 'wait'}
+Debug Logger: {'sender': 'Test Procedure', 'target': 'Example GenericWait',
+ 'seconds': 1.0, 'id': 'Check', 'command': 'wait'}
+```
+Die `Init`-Instanz `"Test Procedure"` sendet ihre beiden konfigurierten
+Nachrichten und startet damit sowohl die erste `"Off"`- als auch die erste
+`"Check"`-Wartezeit.
+
+```
+Debug Logger: {'sender': 'Example GenericWait',
+ 'event': 'finished', 'id': 'Check'}
+Debug Logger: {'sender': 'Trigger Wait Check',
+ 'target': 'Example GenericWait', 'seconds': 1.0,
+ 'id': 'Check', 'command': 'wait'}
+Debug Logger: {'sender': 'Trigger State Check',
+ 'target': 'Example State', 'command': 'get state'}
Debug Logger: {'sender': 'Example State', 'state': 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 `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': 'Example State',
- 'command': 'set state', 'new state': True}
-Debug Logger: {'sender': 'TriggerWaitOffOn', 'target': 'WaitOn', 'command': 'wait'}
-Debug Logger: {'sender': 'Example State', 'event': 'changed', 'state': True}
-State Change Logger: {'sender': 'Example State', 'event': 'changed', 'state': True}
-```
-Eine weitere halbe Sekunde später sorgt der Ablauf der Instanz `WaitOff`
-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': 'Example State', 'command': 'get state'}
-Debug Logger: {'sender': 'TriggerWaitCheck', 'target': 'WaitCheck', 'command': 'wait'}
+Nach einer Sekunde ist die `"Check"`-Wartezeit das erste Mal abgelaufen.
+Sie wird durch die erste ihrer `Alias`-Instanzen sofort neu gestartet.
+Durch die zweite `Alias`-Instanz wird der momentane Zustand von
+`"Example State"` abgefragt, der dann antwortet.
+
+```
+Debug Logger: {'sender': 'Example GenericWait',
+ 'event': 'finished', 'id': 'Off'}
+Debug Logger: {'sender': 'Trigger Wait Off On',
+ 'target': 'Example GenericWait', 'seconds': 1.5,
+ 'id': 'On', 'command': 'wait'}
+Debug Logger: {'sender': 'Trigger State Off On',
+ 'target': 'Example State', 'command': 'set state',
+ 'new state': True}
+Debug Logger: {'sender': 'Example State',
+ 'event': 'changed', 'state': True}
+State Change Logger: {'sender': 'Example State',
+ 'event': 'changed', 'state': True}
+```
+Eine halbe Sekunde später sorgt der Ablauf der `"Off"`-Wartezeit dafür,
+dass `"Example State"` auf `True` gesetzt wird.
+Hier reagiert jetzt auch der `"State Change Logger"`.
+
+```
+Debug Logger: {'sender': 'Example GenericWait',
+ 'event': 'finished', 'id': 'Check'}
+Debug Logger: {'sender': 'Trigger Wait Check',
+ 'target': 'Example GenericWait', 'seconds': 1.0,
+ 'id': 'Check', 'command': 'wait'}
+Debug Logger: {'sender': 'Trigger State Check',
+ 'target': 'Example State', 'command': 'get state'}
Debug Logger: {'sender': 'Example State', 'state': True}
+
```
-Wiederum eine halbe Sekunde später wird durch Ablauf von `WaitCheck` wieder
+Wiederum eine halbe Sekunde später wird durch Ablauf von `"Check"` wieder
eine Abfrage des Zustands ausgelöst.
```
-Debug Logger: {'sender': 'WaitOn', 'event': 'finished'}
-Debug Logger: {'sender': 'TriggerStateOnOff', 'target': 'Example State',
- 'command': 'set state', 'new state': False}
-Debug Logger: {'sender': 'TriggerWaitOnOff', 'target': 'WaitOff', 'command': 'wait'}
-Debug Logger: {'sender': 'Example State', 'event': 'changed', 'state': False}
-State Change Logger: {'sender': 'Example State', 'event': 'changed', 'state': False}
-```
-Nachdem `WaitOn` das erste Mal abgelaufen ist, wird `Example State` wieder
+Debug Logger: {'sender': 'Example GenericWait',
+ 'event': 'finished', 'id': 'On'}
+Debug Logger: {'sender': 'Trigger Wait On Off',
+ 'target': 'Example GenericWait', 'seconds': 1.5,
+ 'id': 'Off', 'command': 'wait'}
+Debug Logger: {'sender': 'Trigger State On Off',
+ 'target': 'Example State', 'command': 'set state',
+ 'new state': False}
+Debug Logger: {'sender': 'Example State',
+ 'event': 'changed', 'state': False}
+State Change Logger: {'sender': 'Example State',
+ 'event': 'changed', 'state': False}
+```
+Nachdem `"On"` das erste Mal abgelaufen ist, wird `"Example State"` wieder
auf `False` gesetzt.
Dies wiederholt sich jetzt solange weiter, bis das Programm abgebrochen
wird.
+Ein reales System wird weniger von sich immer wieder selbst triggernden
+Abläufen geprägt sein, sondern eher auf Einflüsse von außen reagieren.
+Beispielsweise kann ein System durch
+[controlpi-wsserver](https://docs.graph-it.com/graphit/controlpi-wsserver/)
+Nachrichten über Websockets austauschen und damit auf Web-Oberflächen und
+andere externe Systeme reagieren oder durch
+[controlpi-pinio](https://docs.graph-it.com/graphit/controlpi-pinio/) mit
+an die GPIO-Pins eines Raspberry Pi angeschlossener Hardware interagieren.
+
## Installation auf Raspberry Pi
Auf dem Raspberry Pi wollen wir den Code in ein virtuelles Environment
installieren, wobei die weitere Vorgehensweise davon ausgeht, dass dieses