cerca
LPS - Macchine di stato UML
modifica cronologia stampa login logout

Wiki

UniCrema


Materie per semestre

Materie per anno

Materie per laurea


Help

LPS - Macchine di stato UML

 :: LPS - Macchine di stato UML ::

Che cosa sono

Si tratta di una delle molteplici parti di UML, e consentono la modellazione del comportamento di un sistema.

UML è basato sul presupposto della programmazione ad oggetti, e quindi anche le macchine di stato UML vanno inserite in questo contesto. Ecco come:

Occorre infatti considerare che un oggetto è un'istanza di una classe, e che quindi la classe è il tipo dell'oggetto. Le classi sono modellate tramite il diagramma delle classi. L'oggetto è modellato tramite la macchina di stato UML. Ma, proprio per via del legame tra oggetto e classe, anche la macchina di stato UML è legata all'esistenza di una classe di cui è istanza.

UML utilizza la notazione di Harel, che prende l'avvio dalle FSM (nodi = stati, archi = transizioni), ma la espande.

Lo stato, al solito, è una certa configurazione in cui la macchina si trova in un certo istante di tempo. Lo stato cambia quando

  • accade un evento (esterno)
  • esegue azioni o attività (interne)

Nel caso di azioni o attività, quando esse vengono terminate l'oggetto cambia di stato.

Azioni ed attività non sono la stessa cosa:

  • azione = qualcosa che l'oggetto fa in modo atomico
  • attività = richiede invece un certo tempo per essere eseguita

Si assume che accada un solo evento alla volta, e che gli eventi non abbiano durata.

Lo stato

Ecco come è fatto in generale uno stato.

  • entry = quello che lo stato fa non appena la macchina vi entra. È un'azione, e non un'attività, quindi viene eseguita tutta in un colpo, e viene eseguita ogni volta che la macchina entra o rientra nello stato
  • exit = quello che lo stato fa appena prima di uscire
  • do = l'attività che lo stato esegue
  • evento[cond]/azione = altre cose che lo stato può fare, dopo il verificarsi dell'espressione booleana contenuta in cond

Lo stato iniziale è segnato con un cerchio pieno, mentre quello finale da un cerchio contenente un cerchietto più piccolo pieno.


Le transizioni

La freccia uscente dallo stato qui sopra è una transizione: nelle transizioni non ci possono andare attività, ma solo azioni. La sintassi generale è

 evento(parametri)[condizione]/azione|target.messaggio(parametri)

I parametri sono da pensare come i parametri delle funzioni. La condizione è una condizione. Quando è verificata, possiamo

  • eseguire un'azione, oppure
  • chiamare un metodo di un altro oggetto

Come dicevamo sopra, UML parte dalla programmazione ad oggetti. Chiamare il metodo di un oggetto equivale a mandargli un messaggio. Siccome è possibile inviare messaggi anche ad altri oggetti, è possibile specificare il target. Nel caso dell'invio di un messaggio a se stesso, si usa self come target.

Ovviamente, le transizioni scattano quando avviene un certo evento, e quando la condizione è soddisfatta.

Quando ci sono transizioni che partono da più stati e vanno a finire tutte nello stesso stato, è possibile usare le transizioni di gruppo, che si ottengono raggruppando gli stati di partenza interessati in un rettangolo, e facendo partire una freccia da questo rettangolo, invece che farne partire una per ogni stato.

Attività

Le attività attivano un thread concorrente, che viene eseguito finché non occorre una delle due seguenti condizioni:

  • l'attività termina per conto suo
  • si cambia stato

Un esempio di attività può essere do/while(true) alarm.ring(), e vuol dire che mentre la condizione è true, si manda il messaggio ring all'oggetto alarm.

Per segnare il passaggio da uno stato all'altro al termine di un'attività, si mette una freccia senza nessuna etichetta.

Tipi di evento

Ci sono tre tipi principali di evento:

  • interazione tra oggetti
  • occorrenza di istanti temporali
  • cambiamento di valore

Istanti temporali: sono robe del tipo "dopo 5 secondi, esplode tutto". Si segnano con scritture come after(10 seconds)/...

Il cambiamento di valore si ha quando un oggetto modifica il valore dei propri attributi. Quando invece chiama un metodo di un altro oggetto, siamo nel caso dell'interazione tra oggetti.

I tipi di interazione tra oggetti sono il call event, in cui si invoca un metodo, oppure signal event, che indica la ricezione di un segnale da parte di un altro oggetto. I segnali vengono indicati nelle transizioni con la sintassi <<signal>>nome del segnale. I cambiamenti invece si scrivono when(condizione)/...

Scelte

Quando si passa da uno stato ad un altro stato, con lo stesso evento ma con condizioni diverse, si utilizza la notazione del junction pseudostate oppure del choice pseudostate.

Il junction pseudostate è segnato come un pallino nero a cui si arriva con una transizione. Dal pallino poi partono diverse strade, a seconda delle condizioni che vengono verificate. Le condizioni devono essere statiche, ovvero non dipendere da calcoli vari.

Il choice pseudostate si segna con un pallino vuoto, ed è la stessa roba del junction, solo che la condizione è valutata non su un valore statico, ma su un valore che viene calcolato da qualcuno o da qualcosa.

Da notare che i rami che partono dagli pseudostate portano la dicitura della condizione che li attiva tra parentesi quadre [].

Vedi l'esempio della ditta che offre seminari per un junction pseudostate.

Stati sequenziali composti

Gli stati sequenziali composti sono praticamente delle macchine a se stanti, che vengono inglobate in uno stato, il quale stato fa parte di una macchina più grande. Essendo macchine a se stanti, devono avere un punto d'ingresso ed uno di uscita.

Le transizioni che portano da e verso gli stati composti:

  • possono andare fino al limite dello stato composto, cioè lo "statone" che contiene la macchina, e in questo caso lo stato composto inizia dal suo punto di inizio
  • possono andare fino ad uno stato particolare all'interno dello stato composto
  • possono partire dal limite dello stato composto, e ciò significa che, in qualsiasi stato interno ci si trova, con quell'evento si esce
  • possono partire da un singolo stato all'interno dello stato composto.

Ecco un esempio, che rappresenta il cambio automatico di una macchina. Se dalla folle si preme la leva F, si entra nello stato sequenziale Marcia Avanti. Da questo stato si esce, in qualsiasi punto ci si trovi, quando si agisce su leva N.

Transizioni di completamento

Le transizioni di completamento sono quelle su cui la macchina va quando viene terminata un'attività, cioè una roba con do qualcosa. Sono le transizioni che non hanno etichette segnate.

Stati composti paralleli

Finora, le nostre macchine di stato UML erano sequenziali, cioè passavano da uno stato all'altro in sequenza: non era possibile modellare azioni concorrenti, così come eravamo riusciti a fare con le FSM di comunicazione.

Per supplire a ciò, occorre introdurre gli stati composti paralleli, che sono stati normali ma divisi in più "sezioni" da una linea tratteggiata. Ogni sezione contiene una macchina a se stante, che viene avviata assieme alle sue sorelle. Quando tutte le macchine contenute terminano, allora anche lo stato termina. Rappresentano quindi l'esecuzione di thread paralleli.

Qui sotto c'è un esempio: un sistema che può trovarsi nello stato normale oppure in manutenzione. Se è in manutenzione, avvengono 2 attività parallele:

  1. il testing dei devices, seguito dalla diagnosi
  2. il controllo contemporaneo dei 3 comandi del sistema

Dopo la manutenzione il sistema torna ad essere normale.

Da notare che la transizione che da manutenzione porta a normale non è etichettata perché si tratta di una transizione di completamento.

Comunicazione tra macchine

Anche se le macchine vengono eseguite in parallelo, esse non si parlano. Occorre un modo per farle dialogare, e UML ce ne fornisce ben 3:

  1. le condizioni, o variabili, condivise;
  2. gli stati di sincronizzazione
  3. la sincronizzazione tramite eventi

Condizioni condivise

La cosa è molto semplice: quando si scrive la guardia, cioè la condizione, di una transizione o di un'attività, semplicemente quella guardia fa riferimento a proprietà di un'altra macchina.

Siccome UML è legato alla programmazione ad oggetti, per indicare le proprietà di una macchina si usa la notazione nomemacchina.proprietà.

Ad esempio, una transizione nella macchina fornello potrebbe essere guardata dalla condizione serbatoio.stato == pieno, che fa riferimento alla proprietà stato della macchina serbatoio.

Stati di sincronizzazione

Si tratta di stati particolari, che si mettono al confine tratteggiato tra alcune macchine parallele, e che vengono collegati a partire da sbarre di sincronizzazione, che sono quelle più spesse.

Una sbarra di sincronizzazione è tale che si passa da un suo lato all'altro quando arrivano tutti gli eventi in ingresso; quando una sbarra viene passata, partono eventi per tutte le transizioni che da essa si diramano.

Ecco un esempio di stati di sincronizzazione, che è abbastanza autoesplicativo:

Sincronizzazione tramite eventi

È simile alle condizioni condivise, solamente che qui si mette come conseguenza di una transizione il nome di un evento, e quello stesso evento con lo stesso nome viene usato da un'altra macchina per causare una transizione.

Ecco un esempio: il capomafia vuole punire chi non lo paga. Se non arrivano i soldi, dà l'ordine al picciotto, e quello va ed esegue.

Torna alla pagina di LPS