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 Python

  • rozumieť 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.

Diagram tried: Návrhový vzor Stav chytrého zariadenia

Stavový diagram, ktorý bude reprezentovať náš stavový stroj, sa nachádza na nasledujúcom obrázku.

Stavový stroj zariadenia

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 stavu Factory 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.

Abstraktná trieda AbstractState

Trieda bude mať nasledovné metódy:

  • __init__(self, device) - initor
  • exec(self) - metóda, ktorá obsahuje správanie 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):
        print('>> Init State')

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.
Trieda Device

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.

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

sys.exit(0)

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.

Ďalšie úlohy

  1. 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.

Ďalšie zdroje