Stavové stroje
chytré zariadenie ako stavový stroj, návrhový vzor
State
, prechody medzi stavmi, pripojenie zariadenia k
WiFi
Video pre cvičenie
Motivácia
Na mnohé chytré zariadenia sa dá pozerať ako na stavové stroje. To znamená, že celé správanie je možné opísať pomocou stavového stroja, na základe ktorého vieme povedať, že v ľubovoľnom momente sa bude zariadenie nachádzať vždy v jednom konkrétnom stave. No a dnes implementáciu nášho chytrého zariadenia upravíme tak, že z neho stavový stroj spravíme.
Jednou z výhod tohto prístupu bude, že dôjde k reorganizácii výsledného kódu, kde každý stav sa bude nachádzať v samostatnom module. Tým pádom sa vieme pri implementácii správania zariadenia naozaj sústrediť len na všetky aspekty daného stavu.
Ciele
aplikovať návrhový vzor
Stav
v jazyku Pythonrozumieť stavovým diagramom
naučiť sa reprezentovať chytrú vec pomocou stavového stroja
Postup
Stav a stavový stroj
Na väčšinu IoT zariadení sa vieme pozerať ako na stavové stroje. To znamená, že celú činnosť zariadenia vieme opísať pomocou diagramu stavov a výsledný stavový stroj vieme implementovať pomocou návrhového vzoru Stav. No a práve v tomto kroku sa pripravíme na uvedenú zmenu organizácie nášho projektu a reprezentácie nášho zariadenia.
Stavový diagram, ktorý bude reprezentovať náš stavový stroj, sa nachádza na nasledujúcom obrázku.
Význam jednotlivých stavov je nasledovný:
Self Tests
- V tomto stave dôjde k overeniu a inicializácii pripojených senzorov a akčných členov.Init
- Tento stav načíta konfiguračný súbor a v prípade, že bolo tlačidlo na zariadení podržané min. 5 sekúnd, prejde do stavuFactory Reset
.Factory Reset
- Stav, v ktorom bude zariadenie uvedené do výrobných nastavení.Measurement
- V tomto stave prebehne proces merania.Connecting to WiFi
- V tomto stave sa zariadenie pokúsi pripojiť k internetu.Publish Data
- V tomto stave dôjde k publikovaniu nameraných údajov do internetu.Sleep
- Pri prechode zariadenia do tohto stavu ho uspíme.
Okrem uvedených stavov bude existovať aj stav Error
, do
ktorého zariadenie prejde v prípade vzniku chyby.
Task
Vytvorte balík s názvom states
, do ktorého budeme
ukladať všetky stavy.
Task
V balíku states
vytvorte abstraktnú triedu
AbstractState
, ktorá bude reprezentovať jeden stav. Všetky
ostatné stavy budú potomkami tejto triedy.
Upozornenie
Pre vytvorenie abstraktnej triedy viete v štandardnej knižnica jazyka
Python použiť balík abc
. Ten však v jazyku
MicroPython nenájdete. Takže triedu AbstractState
v tomto prípade implementujte ako normálnu triedu.
Trieda bude mať nasledovné metódy:
__init__(self, device)
- initorexec(self)
- metóda, ktorá obsahuje správanie daného stavu
Poznámka
Do stavu môžete pridať aj metódy enter()
a
exit()
. Môžete ich použiť na logovanie alebo na nastavenie
kontextu pri vchádzaní alebo odchádzaní z daného stavu.
Task
V balíku states
vytvorte kostru všetkých stavov podľa
diagramu stavov vytváraného zariadenia.
Každý jeden stav uložte do samostatného modulu s názvom daného stavu.
Napr. pre reprezentáciu stavu Init
vytvoríte modul
init.py
, ktorého kód bude vyzerať nasledovne:
from .state import AbstractState
class Init(AbstractState):
def exec(self):
pass
Po skončení bude váš balík states
vyzerať
nasledovne:
states/
├── __init__.py
├── connect_to_wifi.py
├── factory_reset.py
├── init.py
├── measurements.py
├── publish_data.py
├── self_tests.py
└── state.py
Trieda Device
V prvom kroku vytvoríme triedu Device
, ktorá bude
reprezentovať naše chytré zariadenie. Táto trieda bude mať len niekoľko
členských premenných:
state
- v ktorom stave sa naše chytré zariadenie ako stavový stroj práve nachádza,config
- konfigurácia v podobe slovníka, a- premenné reprezentujúce jednotlivé pripojené senzory a akčné členy.
Task
Vytvorte triedu Device
podľa diagramu tried spolu s
initor-om.
V initor-e vytvorte prázdne členské a nastavte aktuálny stav na
Init
.
Task
Vytvorte metódu .change_state()
, pomocou ktorej bude
možné zmeniť aktuálny stav zariadenia.
Task
Vytvorte metódu .run()
, ktorá bude v nekonečnej slučke
volať metódu .exec()
nad aktuálnym stavom zariadenia.
Task
Otestujte vytvorenú implementáciu.
Pre otestovanie a spúšťanie vašej implementácie si vytvorte napr.
samostatný modul run.py
, do ktorého vložíte nasledujúci
kód:
from device import Device
if __name__ == '__main__':
= Device()
device device.run()
Ak ste postupovali správne, tak po spustení tohto modulu sa spustí aj vaše zariadenie.
Poznámka
Pre automatické spúšťanie kódu po štarte celého zariadenia je
potrebné kód uložiť do modulu main.py
. Pre testovanie to
však nepotrebujeme, pretože by nám to mohlo priniesť viac problémov ako
úžitku. To urobíme až neskôr.
Prechody stavmi
V tomto kroku implementujeme jednotlivé stavy podľa diagramu stavov.
Task
Vytvorte stav Sleep
, ktorý bude koncovým stavom v
prípade úspešného prechodu stavovým strojom.
Aktuálne nebudeme zariadenie uspávať, ale len ukončíme prechod
stavovým strojom zavolaním funkcie sys.exit()
:
import sys
0) sys.exit(
Upozornenie
Tento stav a jeho aktuálna podoba je nevyhnutná pre samotné testovanie. Ak totiž napr. pri prechode stavovým strojom po úspešnom odmeraní nezmeníte aktuálny stav, program sa zacyklí s tým, že bude donekonečna načítavať a ukladať údaje do súboru. To bude mať za následok zaplnenie jeho FLASH pamäte a ukončenie korektnej činnosti.
Tento stav používajte vždy aj pri testovaní ako koncový stav!
Task
Vytvorte stav Self Tests
, ktorý otestuje, či sú
pripojené požadované akčné členy a senzory, a či pracujú správne.
Vytvorte nasledujúce self testy:
test svetla - Svetlo len na krátky čas rozsvieťte a následne ho zhasnite.
test senzora DHT - Najprv odmerajte hodnoty zo senzora. Ak meranie prebehlo úspešne, tak overte, či namerané údaje zodpovedajú rozsahu, ktorý je udávaný výrobcom senzoru. To znamená, že pre teplotu musí platiť: \[ 0 <= teplota <= 50 \] a pre vlhkosť musí platiť \[ 20 <= vlhkosť <= 90 \].
V prípade, že dôjde k chybe niektorého z testov, ukončite
funkcionalitu chytrého zariadenia prechodom do špeciálneho stavu
Alert
, kde svetelnou signalizáciou upozorníte na vzniknutý
problém.
Task
Vytvorte stav Error
, do ktorého zariadenie prejde v
prípade chyby.
Kód chyby uložte do osobitnej členskej premennej triedy
Device
. Na základe uvedenej chyby použite LED diódu na
notifikáciu používateľa, napr. počtom jej bliknutí.
Po notifikácii prejdite do stavu Sleep
.
Task
Vytvorte stav Init
, ktoré načíta konfiguráciu
zariadenia.
Po prechode zariadenia do tohto stavu najprv overte, či nie je
stlačené tlačidlo. Ak je toto tlačidlo držané po dobru 5 sekúnd
(resp. po dobru, ktorú reprezentuje premenná
FACTORY_RESET_INTERVAL
), zariadenie prejde do stavu
Factory Reset
.
V opačnom prípade načítajte konfiguračný súbor zariadenia. V prípade,
že súbor nebude načítaný v poriadku alebo súbor nebude existovať,
prejdite do stavu Factory Reset
.
V prípade, že je všetko v poriadku, zariadenie prejde do stavu
Measurement
.
Task
Vytvorte stav Factory Reset
, ktorý uvedie zariadenie do
výrobných nastavení.
V tomto stave zatiaľ vytvoríme predvolený konfiguračný súbor. Po jeho
vytvorení zariadenie reštartujeme zavolaním
machine.reset()
.
Task
Vytvorte stav Measurement
, v ktorom odmeriate aktuálne
hodnoty z pripojeného senzora teploty a vlhkosti.
Odmerané hodnoty uložte do súboru, ktorého umiestnenie uložte do
konfiguračnej premennej MEASUREMENTS_FILE
. Namerané údaje
udržiavajte ako zoznam meraní - či už vo forme CSV súboru alebo ako
zoznam zoznamov meraní vo formáte JSON.
Poznámka
Konverziou teploty na veličinu podľa konfigurácie sa v tomto kroku nezaoberajte. To urobíte až pri odosielaní dát do internetu.
Ďalšie úlohy
- Do abstraktnej triedy
AbstractState
pridajte členskú premennú s názvom.name
. Táto premenná bude obsahovať reťazcovú reprezentáciu názvu daného stavu. Tú môžete použiť pri logovaní, keď do stavu vojdete alebo z neho odídete.