07 Třídy

Třída je zabalená skupina proměnných a podprogramů pro manipulaci s těmito proměnnými. Vnitřním proměnným říkáme atributy. Podprogramům třídy říkáme metody.

Například mějme třídu návěstidlo SignalMast http://jmri.org/JavaDoc/doc/jmri/implementation/AbstractSignalMast.html. To má své vnitřní proměnné jako název návěstidla mUserName nebo návěst aspect. K tomu má metody pro přečtení/nastavení názvu getUserName()/setUserName() nebo návěsti getAspect()/setAspect().

Třídy se většinou nepoužívají přímo, ale prostřednictvím instancí. To znamená, že máme jednu třídu návěstidlo, ale několik instancí, např. návěstidlo PřS, S, S1, S2,… S konkrétním návěstidlem pracujeme právě přes jeho instanci.

Dědičnost

Při programování třídy velmi často tzv. dědíme. To znamená, že jednu třídu vezmeme jako základ a z ní vytvoříme třídu novou. Nová třída může používat všechny atributy a metody třídy původní a navíc může další atributy a metody definovat.

V Jythonu dědíme tak, že rodičovskou třídu napíšeme jako parametr třídy.

class nováTřída(RodičovskáTřída) :

K atributům třídy se dostaneme přes klíčové slovo self. Toto klíčové slovo musí být použito jako první argument všech metod nové třídy. Metody třídy definujeme stejně jako funkce a procedury v Jythonu.

def nováMetoda(self) :

    self.proměnná = 0

    return

V nových třídách nemusíme jenom vytvářet nové metody, ale můžeme i metody rodičovské třídy překrývat a tak jim dávat novou funkčnost.

Použití třídy

Třída se používá tak, že se nejdříve vytvoří její instance. Potom se většinou nastaví do výchozího stavu její proměnné. Potom je možno instanci třídy používat podle jejího určení.

a = MojeTřída()

a.setName(„Uživatelské jméno instance“)

a.nějakáMetoda()

Siglet

Třída Siglet je určena pro řešení úloh spojených s automatizací. Stránka s dokumentací třídy je zde http://jmri.org/JavaDoc/doc/jmri/jmrit/automat/Siglet.html. Instance třídy čeká na výskyt události na zadaných objektech. Instance třídy neběží ve vlastním vlákně a minimálně zatěžuje zdroje počítače. Instance nesmí používat metodu wait() v žádné její formě.

Při použití třídy je třeba překrýt dvě metody. V metodě defineIO() se zadává pole objektů, které má třída sledovat. Tato metoda je volána právě jednou při zřízení instance.

Druhou metodou je metoda setOutout(). V této metodě se definuje co se má provést při změně zadaného objektu.

Třída se většinou startuje při startu aplikace záznamem v tabulce Startování. Instance třídy běží až do ukončení běhu celé aplikace.

# bloky_sledovani.py

import jarray

import jmri

class bloky_sledovani(jmri.jmrit.automat.Siglet) :

    # defineIO() spouští se jednou při startu

    # načte pole pole vstupů

    # jakákoliv změna vstupu spustí metodu setOutput()

    def defineIO(self):    

        self.BK2 = blocks.getBlock("BK2")

        self.BK3 = blocks.getBlock("BK3")

        self.BK4 = blocks.getBlock("BK4")

        # registrace vstupů

        self.setInputs(jarray.array([self.BK2, self.BK3, self.BK4], jmri.NamedBean))

        return

   

    # setOutput je voláno při každé změně vstupu

    # podle výpočtu změní výstup

    def setOutput(self):    

        print self.BK2.getState(), self.BK3.getState(), self.BK4.getState()

        return

# konec definice třídy

# start

bloky_sledovani().start()

Při každé změně obsazení bloku jedné z výhybek se vypíše zpráva. V praxi se to používá např. ke změně návěsti přilehlých návěstidel.

AbstractAutomation

Třída AbstractAutomation je určena pro řešení různých úloh spojených s automatizací. Na stránce dokumentace ke třídě http://jmri.org/JavaDoc/doc/jmri/jmrit/automat/AbstractAutomaton.html je její popis a návod k použití. Instance třídy umí čekat na událost z kolejiště, a protože běží ve vlastním vlákně tak čekání neblokuje činnost programu.

Při použité třídy je potřeba překrýt dvě její metody. Jako první je třeba překrýt metodu init(). Tato metoda je volána právě jednou při zřízení instance. V této metodě se nastavují interní proměnné.

Jako druhou metodu je třeba překrýt handle(). Tato metoda je periodicky volána a její tělo je prováděno. Pokud metodu ukončíme hodnotou True, bude metoda znovu zavolána.

return 1

Pokud metodu ukončíme hodnotou False, tak se instance ukončí a proměnná přestane existovat.

return 0

Třída AbstractAutomation obsahuje sadu metod pro použití programátorem. Pro nás je zajímavá metoda waitChange(), která čeká na událost snímače, výhybky, návěstidla, …  Vykonávání vlákna programu se na této metodě zastaví. Jakmile dojde ke změně stavu vybraných objektů, tak bude program pokračovat. Metodu je možné použít s druhým volitelným parametrem, do kterého se zadává time out. Pokud ve zvoleném čase nedojde k události, tak se vlákno instance nezasekne, ale bude pokračovat dál.

CekejNaSnimace

Třída CekejNaSnimace() je příklad použití třídy AbstractAutomation().

Třída si připojí snímače S2, S3 a S4. Potom čeká na stav, kdy jsou všechny snímače ve stavu aktivní. Pokud se tak stane tak nastaví proměnnou stavSnimacu na hodnotu 1. Pokud se tak nestane do 1 minuty, tak nastaví proměnnou na hodnotu na hodnotu 2. Pokud běží vlákno instance třídy, tak má proměnná hodnotu 0.

Metodu init() překryjeme naší metodou. V ní definujeme všechny interní proměnné. Potom překryjeme metodu handle(), ve které bude logika naší úlohy. V metodě handle() využijeme metodu waitChange(), která bude čekat na změnu stavu snímačů. Na změnu stavu budeme čekat 1 vteřinu. Pokud ke změně nedojde, tak pustíme výpočet dál. Toto čekání budeme opakovat maximálně 60×.

Po definici naší třídy vytvoříme její instanci, nastavíme název instance a pomocí metody start() instanci spustíme.

# Čekání na snímače

#

import jmri

class CekejNaSnimace(jmri.jmrit.automat.AbstractAutomaton) :

  

    # init() je spuštěno právě jednou při vytvoření instance

    def init(self):

      

        # get the sensor and throttle objects

        self.S2 = sensors.provideSensor("S2")

        self.S3 = sensors.provideSensor("S3")

        self.S4 = sensors.provideSensor("S4")

        self.seznam_snimacu = [self.S2, self.S3, self.S4]

        self.stavSnimacu = memories.provideMemory("stavSnimacu")

        self.stavSnimacu.setValue(0)

        self.pocet_pokusu = 0

              

        return

    # handle() je spouštěno opakovaně dokud není navráceno False (0)

    def handle(self):

       

        self.waitChange(self.seznam_snimacu, 1000)

       

        self.pocet_pokusu = self.pocet_pokusu + 1

       

        if ((self.S2.getState() == ACTIVE) \

            and (self.S3.getState() == ACTIVE) \

            and (self.S4.getState() == ACTIVE)) :

           

            self.stavSnimacu.setValue(1)

            return 0

        else :

            if (self.pocet_pokusu >= 60) :

                self.stavSnimacu.setValue(2)

                return 0

       

        # neplněné podmínky, pokračujeme

        return 1

  

# konec definice třídy

# vytvořit instanci třídy

a = CekejNaSnimace()

# nastavení jména instance, nepoviné

a.setName("Skript čekání na snímače".decode("UTF-8"))

# spuštění třídy

a.start()

Testování skriptu

Otevřeme okna Skript vstup, Skript výstup, Monitor vlákna, tabulka Paměťové proměnné a tabulka Snímače.

Do okna Skript vstup načteme náš skript a spustíme ho. V okně Monitor vlákna se zobrazí řádek monitorující vlákno našeho skriptu. Ve sloupci Cycles se počítá počet průchodů metodou handle().

V okně Paměťové proměnné v řádku stavSnimacu se zobrazuje stav vlákna.

Pokud přestavíme snímače S2, S3 a S4 do stavu Aktivní, tak vlákno skočí stavem 1. Pokud to nestihneme do 60 vteřin, tak skript skončí stavem 2.

vytvořeno 21. 9. 2019