Dokumentation med State-machines

Fra HTX-Arduino
Skift til: navigering, søgning
Video med forklaring til kapitlet

Når man kommer til mere komplekse programmer, som skal holde styr på flere forskellige ting, sådan som det er beskrevet i Tid og Samtidighed i Software, så er det sjældent at det giver mening at dokumentere med Flowcharts, da det ikke giver mening at beskrive et bestemt flow gennem softwaren. Det bliver alt for komplekst og uoverskueligt.

Kodeeksempler

Til dette kapitel er der nogle kodeeksempler man kan anvende. De er samlet i en ZIP-fil.

State-machine

Princippet i en state-machine[1] er normalt knyttet op på en enkelt variabel der angiver hvilket state (tilstand) at programmet er i. Dette er normalt bare angivet som et tal.

Normalt vil en state-machine starte i en hvile-tilstand (variablen er typisk 0), hvor der ventes på en ydre begivenhed, en måleværdi, en indtastning, noget der sker i et interrupt eller hvad det er der skal løses. Når begivenheden sker, så skifter tilstanden (til et andet tal end 0) og nu ventes på en ny begivenhed, der kan opsamle data, registrere et eller andet eller føre state-machinen ind i en ny tilstand.

Det smarte ved state-machinen er at man for hver tilstand kan bestemme hvad der skal testes på, og ud fra resultatet af testen kan der opsamles data, målinger, indtastninger eller hvad det er programkoden skal løse, og på den rette begivenhed kan der føres ind i en ny tilstand, indtil programmet er kommet igennem de states der skal til, og typisk ender i hvile-tilstanden igen.

Dokumentation af state-machine

Den måde man dokumenterer en state-machine på er ved at hvert state er en cirkel, og hver begivenhed der påvirker state-machinen er en pil der fører enten til samme state eller til et nyt state.

Hvert state har en funktion, der viser hvad det er der foregår i det state, mens der ved pilene beskrives hvad der udløser skiftet, og i nogle tilfælde beskrives også hvad der udføres ved pilen.

State-machinen beskriver altså ikke et egentlig flow i programkoden, men dokumenterer hvad der sker i den del af programmet, når der kommer hændelser der har indflydelse på den del af programmet.

Det eksempel der er vist i figur 1 er baseret på indtastning af et tal fra et tastatur, og her er tallet et setpunkt.

Eksempel på en state-machine der beskriver indtastning af et setpunkt
Figur 1 Eksempel på en state-machine der beskriver indtastning af et setpunkt.

Ideen i denne state-machine er at den normalt står i hvile-tilstanden, og at der kun er to taster der kan bringe den ud af hvile-tilstanden, nemlig * og # tasterne.

Tastes der *, så går state-machinen i en tilstand hvor der vises hvilket setpunkt der er aktuelt, og hvis der i denne tilstand tastes *, så går den tilbage i hvile-tilstanden. Alle andre indtastninger reageres der ikke på. Hvis der vises setpunktet over en vis tid, så går den også tilbage til hvile-tilstanden.

Hvis der tastes #, så går den i en tilstand hvor der indtastes et tal som skal være det nye setpunkt. I indtast-tilstanden er det meningen at der skal opsamles de cifre der indtastes til det setpunkt der ønskes. Hver gang der indtastes et ciffer, så kommer til at indgå i tallet der skal være det nye setpunkt. Når der så tastes #, så går den i tilstanden der viser setpunktet (beskrevet ovenfor). Det der også skal ske er at ved indtastningen af # så skal det nye setpunkt lagres (det er ikke vist i state-machinen, men må tages for givet). Der kan ligeledes forekomme timeout under indtastningen af setpunktet, hvorved state-machinen skal gå i hviletilstanden, og her skal det indtastede ikke gemmes. I state-machinen er der heller ikke vist hvornår man klargør indtastningen af et nyt setpunkt, så det er op til programmøren at løse det.

En måde at strukturere state-machinen i kode er illustreret i de næste afsnit. Dele af koden er ikke løst, men kun angivet som kommentarer.

Grundideen i koden er at udnytte en switch-case-struktur[2], hvor hver enkelt tilstand repræsenteres af en case, og måden man skifter state er ganske enkelt ved at ændre på state-variablen, så det er et nyt state der gennemføres næste gang switchen afvikles i loop(). Det samme kan selvfølgelig etableres med if, hvis man ønsker det.

Koden til de to første states
Figur 2 Koden til de to første states.

Den kode der vises i figur 2 viser hvordan de to første states kan løses. Der er ikke taget stilling til hvordan visningen i programmet skal løses, men det kunne være den målte temperatur i state 0. Ud over det registreres der i state 0 om der kommer en indtastning, og hvis det er # eller * så aktiveres det relevante state, mens der indstilles start-betingelserne for de indtastninger.

I state 1 vises det der er indtastet indtil nu, og der opsamles de enkelte cifre til et tal der skal blive til setpunktet. Hvis der tastes # sættes state til 2, så gemmer den det indtastede og den går i visning af det indtastede setpunkt.

I state 1 registreres også om der er kommet timeout, og hvis der kommer timeout, så går den tilbage til hvile-tilstanden uden at gemme noget.

Programkoden der håndterer state 2 - visning af setpunktet
Figur 3 Programkoden der håndterer state 2 - visning af setpunktet.

I figur 3 vises hvordan programkoden håndterer den tilstand hvor setpunktet vises. Det eneste der skal håndteres ud over visningen er at tilstanden skal kunne afbrydes, enten ved et timeout eller ved at der tastes *.

Uden for state-machinen håndteres den tid der anvendes til timeout - her er det blot gjort ved at tælle i en variabel. Det er rimeligt simpelt, men vil kunne virke fornuftigt, hvis hvert gennemløb af loop() tager nogenlunde samme tid. Man kan også bruge millis() til at få en mere præcis timeout.

Flere State-machines

Når man kommer ind i den type programmer der skal løse flere forskellige ting samtidigt, så kan det i en del tilfælde være en god ide at have flere forskellige state-machines etableret. Det gøres ganske simpelt ved at have forskellige state-variabler, og tilsvarende switch-case strukturer til at håndtere dem.

Det kan være være en god ide at skille de forskellige state-machines ad, så der kun ændres på state-variablen inde i den state-machine der er styret af variablen. Det kan dog i nogle tilfælde være smart at lade en state-machines opførsel være afhængig af hvilket state en anden state-machine er i.


Referencer