Tilstandsmaskiner med UML og Java DAT2160 DAT2160 Høst Høst 2002 2002 Tilstandsmaskiner Tilstandsmaskiner med med UML UML og og Java Java
Hva er en (endelig) tilstandsmaskin? En tilstandsmaskin kan sees på som en komponent som kommuniserer med omverdenen ved å konsumere og produsere meldinger. Hvordan den reagerer på meldingene den mottar er avhengig av hvilken tilstand den er i. En tilstands maskin består av: Et sett av tilstander Lokale variable Transisjoner Et sett av meldinger den kan forstå
Finite state machine (Endelig tilstands maskin) Endelig Et endelig antall tilstander. I denne sammenheng et begrenset antall navngitte tilstander. Tilstand En stabil situasjon hvor en prosess venter på å motta et stimuli. En tilstand i en tilstandsmaskin representerer historien til et eksekveringsforløp. Maskin Bare stimuli (signal, melding) trigger (utløser) en handling. Handling utføres i det en tilstandsmaskin går i fra en tilstand til en annen (i transisjonen). En tilstandsmaskin kan også ha data/variable.
Hvorfor bruke tilstandsmaskiner? Tilstandsmaskiner har vist seg å være velegnet for å modellere, validere og implementere reaktive systemer med krav til sanntids oppførsel, parallellprossesering og høy grad av sikkerhet/forutsigbarhet. Tilstandsmaskiner gjør det enklere å lage systemer som er avhengig av synkronisering sammenlignet med sekvensiell trådprogrammering. Bruk av tilstandsmaskiner gjør det mulig å håndtere større kompleksitet. Bruk av asynkront kommuniserende tilstandsmaskiner er en naturlig måte å håndtere samtidighet. Bruk av tilstandsmaskiner vil ofte være mer effektivt sammenlignet med tradisjonell trådprogrammering. (Mindre context switching ).
Eksempel; bruk av tilstandsmaskiner til styring av legobil.
Hva vil det si at til tilstandsmaskiner kommuniserer asynkront? Scheduler Når hver tilstandsmaskin har en postkasse / meldingskø kan tilstandsmaskinene kommunisere asynkront. Det betyr at en tilstandsmaskin kan fortsette prosesseringen uten å måtte vente på at en melding den har sendt blir prosessert hos mottakeren. Meldingen blir liggende i postkassen til mottakeren har mulighet/tid til å behandle den. En scheduler bestemmer når tilstandsmaskinene får lov til å prosessere meldinger fra postkassen / meldingskøen. Slik deles systemressursene mellom alle tilstandsmaskinene.
JavaFrame lar oss lage tilstandsmaskiner i Java og UML 2.0 Et aktivt objekt representerer noe som kan handle på egenhånd. I JavaFrame finnes det to typer aktive objekter; tilstandsmaskiner og kompositter. Kompositt begrepet er innført for å gi bedre skalerbarhet. En kompositt kan inneholde tilstandsmaskiner og nye kompositter. All kommunikasjon til og fra aktive objekter går igjennom porter/mediatorer. Porter/mediatorer er meldingsgrensesnitt for de aktive objektene. Ved å bruke porter/mediatorer oppnår man uavhengighet mellom de aktive objektene.
Mapping mellom modell og kode ved bruk av patterns Vi kan bruke patterns for å lage kode av modellene våres. Å bruke et pattern for å omgjøre modellen til kode betyr at vi følger bestemte retningslinjer for hvordan vi skal programmere. Patterns er til for å hjelpe oss med å lage effektiv og konsistent kode og muliggjør et en-til-en forhold mellom modell og kode. UML 2.0 modell UML med JavaFrame vri mapping /transformasjon (i henhold til pattern) mapping (i henhold til pattern) modellering mens man programmerer (i henhold til pattern) JavaFrame Java JavaFrame Java JavaFrame Java
Modell, implementasjon og verifisering
Hvordan kan kildekoden til en tilstandsmaskin se ut? package hia.grm.dat2160.statemachines; import se.ericsson.eto.norarc.javaframe.*; import hia.grm.dat2160.statemachines.messages.*; public class CarControlCS extends CompositeState { public static State initializecar = new State( InitializeCar ); public static State carmovingforwards = new State( CarMovingForwards ); public static State carmovingbackwards = new State( CarMovingBackwards ); public CarControlCS() { super("carcontrolcs"); initializecar.enclosingstate = this; carmovingbackwards.enclosingstate = this; carmovingforwards.enclosingstate = this; public void enterstate(int nr, StateMachine statemachine) { CarControlSM carcontrolsm = (CarControlSM)stateMachine; TimerMsg initialisecarok = new TimerMsg(3000, carcontrolsm); Start transisjonen initialisecarok.starttimer(); initializecar.enterstate(statemachine); Den første tilstanden tilstandsmaskinen går i.
protected boolean exectrans(message message, State state, StateMachine statemachine) { CarControlSM carcontrolsm = (CarControlSM)stateMachine; System.out.println("execTrans: " + message); if(state == initializecar) { if(message instanceof TimerMsg) { output(new MoveForwardMsg(), carcontrolsm.carcontrolmediatorout, statemachine); carmovingforwards.enterstate(statemachine); return true; return false; else if(state == carmovingforwards) { if(message instanceof FrontSensorMsg) { output(new MoveBackwardMsg(), carcontrolsm.carcontrolmediatorout, statemachine); carmovingbackwards.enterstate(statemachine); Går i neste tilstand return true; if(message instanceof BackSensorMsg) { samestate(statemachine); return true; return false; else if(state == carmovingbackwards) { if(message instanceof BackSensorMsg) { output(new MoveForwardMsg(), carcontrolsm.carcontrolmediatorout, statemachine); carmovingforwards.enterstate(statemachine); return true; if(message instanceof FrontSensorMsg) { samestate(statemachine); return true; return false; return false; Her mottas meldingen og transisjonen utføres. Tester på hvilken tilstand tilstandsmaskinen er i Undersøker hvilken melding som er mottatt Sender ut en ny melding
Oppkobling av tilstandsmaskinens mediatorer: package hia.grm.dat2160.statemachines; import se.ericsson.eto.norarc.javaframe.*; public class CarControlSM extends StateMachine { public Mediator carcontrolmediatorout; public CarControlSM(Scheduler scheduler, Mediator carcontrolmediatorout, Mediator carcontrolmediatorin) { super(scheduler); this.carcontrolmediatorout = carcontrolmediatorout; carcontrolmediatorin.addaddress(this); protected void execstarttransition() { CarControlCS carcontrolcs = new CarControlCS(); carcontrolcs.enterstate(1, this); Her settes adresseringen for tilstandsmaskinen opp Denne metoden kalles når tilstandsmaskinen startes
Composites hjelper oss å håndtere stor kompleksitet: Eksempel: Mellom StateMachineY og StateMachineZ kan det gå hundrevis av meldinger som del av en kompleks protokoll mens det mellom CompositeA og omverdenen kanskje bare går noen få meldinger som del av en enkel protokoll. Resultat: Komposisjon lar oss abstrahere over kompleks oppførsel og arkitektur.
Hvordan kan kildekoden til en composite se ut? package hia.grm.dat2160.statemachines; import se.ericsson.eto.norarc.javaframe.*; public class CarControl extends Composite { private Mediator rcxmediatorin; private Mediator rcxmediatorout; private Mediator carcontrolin; private Mediator carcontrolout; private Scheduler scheduler; public CarControl(Scheduler scheduler, Mediator rcxmediatorin, Mediator rcxmediatorout) { this.scheduler = scheduler; this.rcxmediatorin = rcxmediatorin; this.rcxmediatorout = rcxmediatorout; carcontrolin = new Mediator(); carcontrolout = new Mediator(); rcxmediatorin.addaddress(carcontrolin); carcontrolout.addaddress(rcxmediatorout); CarControlSM carcontrolsm = new CarControlSM(scheduler, carcontrolout, carcontrolin);
Mediatorer: En mediator er et asynkront kommunikasjons grensesnitt/port. En mediator brukes i sin enkleste form til å koble sammen statemachines og composites. De enkleste mediatorene sender meldingene de mottar videre til mottakeren. Det finnes også mer avanserte mediatorer som for eksempel RMIMediator, CORBAMediator etc. Vi kan også lage våre egne mediatorer for å løse spesielle problemer, for eksempel en RCXMediator. En viktig fordel med mediatorer er at vi kan lage programlogikk uten å tenke for mye på hvordan lavnivå kommunikasjons protokoller er implementert.
Eksempel på bruk av mediatorer: Fra RCXMediatorIn mottar vi følgende meldinger: BackSensorMsg FrontSensorMsg Til RCXMediatorOut kan vi sende følgende meldinger: MoveBackwardMsg MoveForwardMsg StopMsg PlaySoundMsg TurnLeftMsg TurnRightMsg I dette eksempelet slipper vi å tenke på hvordan lavnivå kommunikasjons protokoll mot legobilen er realisert. Vi trenger bare å forholde oss til meldingene og kan heller fokusere på hvordan vi kan lage et godt styresystem ved hjelp av tilstandsmaskiner!
Hvordan kan kildekoden til en mediator se ut? package hia.grm.dat2160.statemachines; import se.ericsson.eto.norarc.javaframe.*; import hia.grm.dat2160.statemachines.messages.*; public class RCXMediatorOut extends Mediator { private RCXControl rcxcontrol; public RCXMediatorOut(RCXControl rcxcontrol) { this.rcxcontrol = rcxcontrol; Her mottas meldingen og meldings typen bestemmes public void forward(message signal) { System.out.println("In RCXMediatorOut, signal: " + signal); if(signal instanceof StopMsg) { rcxcontrol.sendrcxmsg("2983"); else if(signal instanceof MoveForwardMsg) { rcxcontrol.sendrcxmsg("e983"); rcxcontrol.sendrcxmsg("2983"); else if(signal instanceof MoveBackwardMsg) { rcxcontrol.sendrcxmsg("e943"); rcxcontrol.sendrcxmsg("2983"); else if(signal instanceof TurnLeftMsg) { rcxcontrol.sendrcxmsg("e941"); rcxcontrol.sendrcxmsg("e982"); rcxcontrol.sendrcxmsg("2983"); else if(signal instanceof TurnRightMsg) { rcxcontrol.sendrcxmsg("e981"); rcxcontrol.sendrcxmsg("e942"); rcxcontrol.sendrcxmsg("2983"); else System.out.println("Can't process message: " + signal + " in RCXMediatorOut!");
Oppstart av et JavaFrame system: package hia.grm.dat2160.statemachines; import se.ericsson.eto.norarc.javaframe.*; public class RCXControllerMain { public static void main(string[] args) { try { Mediator rcxmediatorin = new Mediator(); RCXControl rcxcontrol = new RCXControl("COM1", rcxmediatorin); RCXMediatorOut rcxmediatorout = new RCXMediatorOut(rCXControl); Trace trace = new Trace(true, "LegoBil.jft"); Scheduler scheduler = new Scheduler(trace); Thread thread = new Thread(scheduler); CarControl carcontrol = new CarControl(scheduler, rcxmediatorin, rcxmediatorout); thread.start(); System.out.println("CarControl is up and running..."); catch(exception e) { e.printstacktrace(); Lager mediatoren som mottar meldinger fra legobilen Lager mediatoren som sender meldinger til legobilen Angir navn på trace fil Lager en scheduler Lager styrings systemet Starter systemet
Hvordan undersøke hva som skjer i et system av tilstandsmaskiner? Svar: Vi bruker trace. JFTrace 2002-10-21 17:53:44.508 JFTrace: New SM: 0:CarControlSM@2f1921 110:Input: 1:CarControlSM@2f1921:null:StartMessage:Next state: 1:InitializeCar 3154:Input: 2:CarControlSM@2f1921:InitializeCar:TimerMsg:Output: 2:Mediator:MoveForwardMsg:Next state: 2:CarMovingForwards 7020:Input: 3:CarControlSM@2f1921:CarMovingForwards:FrontSensorMsg:Output: 3:Mediator:MoveBackwardMsg:Next state: 3:CarMovingBackwards 9714:Input: 4:CarControlSM@2f1921:CarMovingBackwards:BackSensorMsg:Output: 4:Mediator:MoveForwardMsg:Next state: 4:CarMovingForwards 10836:Input: 5:CarControlSM@2f1921:CarMovingForwards:FrontSensorMsg:Output: 5:Mediator:MoveBackwardMsg:Next state: 5:CarMovingBackwards 11927:Input: 6:CarControlSM@2f1921:CarMovingBackwards:FrontSensorMsg:Same state: 6:CarMovingBackwards 13860:Input: 7:CarControlSM@2f1921:CarMovingBackwards:FrontSensorMsg:Same state: 7:CarMovingBackwards 14811:Input: 8:CarControlSM@2f1921:CarMovingBackwards:BackSensorMsg:Output: 8:Mediator:MoveForwardMsg:Next state: 8:CarMovingForwards 16163:Input: 9:CarControlSM@2f1921:CarMovingForwards:BackSensorMsg:Same state: 9:CarMovingForwards
Trace n blir litt kan bli litt lettere lese hvis man bruker et verktøy:
Composite states / substate machine er tilstandsmaskiner inni tilstander. CarControlCS initialisecarok.starttimer() CarMovingCS 0 cartimeout.starttimer() / send lightsonmsg backsensormsg / InitializeCar frontsensormsg / backsensormsg / frontsensormsg / send moveforwardmsg initialisecarok / send stopmsg CarStopped CarMovingCS backsensormsg / send movebackwardmsg backsensormsg / send moveforwardmsg carmovingforwards cartimeout /send lightsoffmsg frontsensormsg / send movebackwardmsg, playsoundmsg 0 cartimeout /send lightsoffmsg carmovingbackwards 0 1 frontsensormsg / 0 cartimeout.starttimer() / send playsoundmsg, lightsonmsg / send stopmsg 1
Vi bruker Composite States fordi noen transisjoner er mer generelle enn andre: Man stopper Lego bilen på samme måte uansett om man kjører forover eller bakover. Når Lego bilen beveger seg kan den være i to tilstander; enten kjører den forover eller bakover. Den vil uansett være i en tilstand hvor den beveger seg. Hva er fordelene med å bruke Composite States? Bedre oversikt. Mer kompakt kode. Transisjoner kan beskrives færre steder. Composite states blir en slags komponent med mulighet for gjenbruk a tilstander. Composite states er tilstander som kan skiftes ut under run-time. Dette mulighet for å endre systemets oppførsel mens det kjører.