Tilakoneet¶
Osaamistavoitteet: Opiskelija ymmärtää miten tilakoneita voidaan käyttää sulautetun ohjelman suunnittelussa ja toteutuksessa.
Tilakoneita (engl. finite-state machine, FSM) käytetään digitaalitekniikassa sekvenssilogiikan toteuttamiseen. Tämä lisäksi tilakoneita käytetään ohjelmistosuunnittelussa hyvin yleisesti ohjelman toiminnallisuuden tai käyttöliittymän mallintamiseksi. Sulautetuissa järjestelmissä tilakone on oiva keino mallintaa ohjelmiston toimintaa jo suunnitteluvaiheessa. Kuten rakenteellinen (modulaarinen) ohjelmointi, tilakoneiden käyttäminen nopeuttaa ja formalisoi ohjelmistosuunnittelun prosessia. Tilakone-mallinnuksen tuloksena on yksinkertaisesti parempi ymmärrys ja kontrolli ohjelman toiminnallisuudesta ja sitä myötä laadukkaampi lopputulos.
Tässä materiaalissa emme mene tilakoneiden salaisuuksiin syvemmälle, vaan esitämme miten ne on helppo ottaa osaksi suunnitteluprosessia. Ohjelmoinnin ammattilaisilla on käytössä ohjelmointiympäristöjä ja työkaluja, joilla laatia tilakoneita osaksi ohjelmakoodia. Jotkut työkalut jopa automatisoivat itse ohjelmointia tuottamalla valmista koodia tilakonekuvauksesta!
Tilakone¶
Tilakone on järjestelmä, joka kuvataan tiloina ja niiden muutoksina. Esimerkiksi Mealy-tyyppinen tilakone muuttaa tilaansa seuraavasti: tilasiirtymä riippuu nykyisestä tilasta ja tuloista (ts. inputeista). Tilasiirtymien aikana voidaan myös suorittaa toimintoja, jotka tuottavat outputin. Tilakone on deterministinen, eli yhdestä tilasta yhdellä inputilla tuloksena voi olla vain yksi tilasiirtymä. Tilasiirtymäkaavio kuvaa järjestelmän mahdolliset tilat, siirtymät niiden välillä sekä tapahtumat, jotka aiheuttavat siirtymän tilasta toiseen.
Kuvassa alla tilakoneessa on kaksi tilaa
Tila a
ja Tila b
. Nuolilla kuvattuihin tilasiirtymiin on liitetty myös niiden input
it ja mahdolliset output
it. Digitaalitekniikassa ja ohjelmistosuunnittelussa esitellään monentyyppisiä erilaisia tilakoneita, mutta tämä yksinkertaistettu esitys riittää meille. Lisäksi tilakoneen formaaleja kuvaustapoja on erilaisia, mutta tyydymme kurssilla ylläolevaan kuvaukseen.
Esimerkki tilakoneesta¶
Alla sulautetun ohjelman toiminta kuvattuna tilakoneena. Tilakoneeseen on määritelty kolme tilaa
IDLE
, READ_SENSOR
ja MSG_WAITING
ja yksi tilamuuttuja. Tilamuuttujan tarkoitus on ylläpitää ohjelman tilaa, niin että sitä voidaan halutusti muuttaa. - Tilassa IDLE vain odotetaan asioita tapahtuvaksi.
- Tapahtuma voi (tässä) olla joko komento lukea sensoridataa tilassa READ_SENSOR tai käsitellä saapunut viesti tilassa MSG_WAITING.
Nyt tilakone toimisi seuraavasti:
- Tilamuuttujan arvoa muutetaan ohjelmassamme.
- Esimerkiksi, ajastinkeskeytys voisi ohjata sensorin arvojen lukemista, asettamalla tilan READ_SENSOR kerran sekunnissa.
- Tilamuutos todetaan main-loopissa, josta ohjelma siirtyy suorittamaan tilaan liittyvää toiminnallisuutta.
- Tässä siis kun todetaan tila READ_SENSOR, siirrytään suorittamaan toiminnallisuutta Read sensor values, sitten Update screen.
- Kun tilamuutokseen on reagoitu, palataan takaisin odotustilaan IDLE, asettamalla tilamuuttujan arvo.
Toteutus C-kielellä¶
Ym. tilakoneen C-kielinen toteutus voisi olla seuraava.
// Määritellään mahdolliset tilat: IDLE, READ_SENSOR ja MSG_WAITING
enum state { IDLE=1, READ_SENSOR, MSG_WAITING };
// Esitellään ja alustetaan globaali tilamuuttuja odotustilaan
enum state myState = IDLE;
// Ajastinkeskeytys asettaa tilan (esim. kerran sekunnissa)
Void clkFxn(UArg arg0) {
// Muutetaan tilaa halutuksi
// Hox! If-lauseella tarkistetaan, että tilasiirto on mahdollinen!!
// Eli tässä vain jos nykyinen tila on IDLE
if (myState == IDLE) {
myState = READ_SENSOR;
}
}
// Tiedonsiirron taski
Void commTask(UArg arg0, UArg arg1) {
while (1) {
// Onko viesti puskurissa odottamassa ja odotustila?
if (is_message_waiting() == TRUE && myState == IDLE) {
// Muutetaan tilaa
myState = MSG_WAITING;
}
}
}
// Ohjelman main-taski
Void myTask(UArg arg0, UArg arg1) {
while (1) {
// Tilakoneen toteutus
switch (myState) {
case READ_SENSOR:
// Suoritetaan tilan vaatima operaatio
read_sensor_values();
update_screen();
// Asetetaan tila takaisin IDLE
myState = IDLE;
break;
case MSG_WAITING:
// Suoritetaan tilan vaatima operaatio
handle_message();
send_reply();
// Asetetaan tila takaisin IDLE
myState = IDLE;
break;
}
}
}
Hox! Ylläoleva tilakonetoteutus ei toimi (ihan tarkoituksella) sellaisenaan kurssin SensorTag-laitteessa!! Copy&paste omaan koodiin ei siis tässä toimi..
Tilakoneen ohjelmoinnissa on kolme periaatetta:
- Tilat toteutetaan tilafunktioina, eli
read_sensor_values
ym. - Tilamuutokset sijoitusoperaatioilla tilamuuttujaan, esimerkiksi
myState = IDLE
asettaa tilan IDLEen. Tästä syystä tilamuuttuja on globaali, jotta sen arvoa voidaan tarkastaa ja muuttaa kaikkialla ohjelmassa! - main-funktion Ikuisessa silmukassa tarkistetaan tilamuuttujan arvo ja suoritetaan sen mukainen tilafunktio, eli
switch (myState) {...
}.
Ohjelmassa on asetettu ajastinkeskeytys kerran sekunnissa, jonka käsittelijä on
clkFxn
, joka muuttaa tilan READ_SENSOR-tilaan. Näin tilakoneen avulla luemme sensorin arvon kerran sekunnissa. Samoiten commTask
-tehtävässä muutamme tilan MSG_WAITING, jos puskurissa on odottamassa viestejä. Seuraavan kerran kun mainTask
-tehtävässä tarkistamme tilan, näemme sen muuttuneen ja reagoimme muutokseen suorittamalla halutun toiminnon. Esimerkiksi, jos tila on MSG_WAITING, suoritamme funktiot handle_message
ja send_reply
.Huomatkaa koodissa yksi niksi! Eli
if
-lauseella tarkistamme onko tilamuutos luvallinen. Esimerkki funktiossa commTask
. Ilman tarkastuksia saattaisi koodissa tulla tilasiirtymiä, jotka eivät ole tarkoituksenmukaisia ja suoritus mennä harakoille, tai jokin tila saattaisi jäädä kokonaan suorittamatta. Yllä siis varmistetaan, että siirtymä MSG_WAITING-tilaan on mahdollinen vain IDLE-tilassa. (Ym. esimerkissä tosin keskeytyksen korkeampi prioriteetti varmistaa, ettei toinen tila ohita sitä.) Yleisesti, tilasiirymän luvallisuuden tarkistus on varsin oleellinen osa niiden käyttöä ohjelmissa!!Tilakonetta suunnitellessa tilat kannattaa järjestää esim. kasvavassa numerojärjestyksessä niin, että tilasiirtymien tarkistus voidaan tehdä vertailu-operaattorilla (pienempi kuin, suurempi kuin, ...) helposti. Tässäkin vältämme viimeiseen asti monimutkaisia
if-else
-rakenteita! Toinen oleellinen asia koodin rakenteen parantamiseksi on tilojen määrän minimointi. Mutta siihen emme tarkemmin tällä kurssilla. // Määritellään mahdolliset tilat: IDLE, READ_SENSOR ja MSG_WAITING
enum state { IDLE=1, READ_SENSOR, MSG_WAITING };
Lopuksi¶
Tyypillinen toinen käyttötapaus tilakoneelle sulautetuissa ohjelmissa on käyttöliitttymän toteuttaminen. Tilakoneen avulla voimme määritellä rajatulle määrälle komponentteja, esimerkiksi kahdelle painonapille, ohjelman tilasta riippuvia toimintoja. Esimerkiksi MENU-tilassa yksi nappi on omistettu virtanapiksi, mutta PELI-tilassa sitä voidaan käyttää peliohjaukseen. Kätevää!
Yhtenä osana kurssin harjoitustyötä, tulee opiskelijoiden laatia ohjelmansa korkean tason tilakoneen kuvaus.
Anna palautetta
Kommentteja materiaalista?