SML - State Machine Language
Alberto Bellina
1 Gennaio 1996
Versione 1.0
INTRODUZIONE
SML e' l'acronimo di State Machine Language.
SML e' un ambiente operativo che consente la gestione di eventi
di diversa provenienza in un unico e standardizzato modo.
SML e' un linguaggio che consente mediante una sintassi semplicata la
generazione di macchine stati multiple, con gestione automatica del
colloquio delle stesse.
Le parti in cui e' composto l'ambiente operativo SML sono evidenziate
dal seguente disegno:
+------+
+------+ ******* +------+ |
| | * * | | |
| .sml |--->* SML *--->| | | files generati
| | * * | |-+ .c e .h
+------+ ******* +------+
CONVENZIONI DEL DOCUMENTO
Di seguito le definizioni utilizzate:
CHANNEL | canale di comunicazione tra automi |
STATEMACHINE | macchina a stati o automa |
EVENT | tipo evento |
SML | State Machine Language |
SMLGEN | programma generato da SML |
SMLTEST | programma di test generato da SML |
FUNZIONAMENTO
I programmi generati a seguito della sintassi espressa nel linguaggio SML,
permettono di gestire gli eventi relativi alle State Machine implementate.
Esistono due tipi di modalita' di funzionamento delle State Machine:
Queste modalita' differiscono in quento nella modalita' wait event le
chiamate alle funzioni utente all'arrivo di un evento sono effettuate
nel codice generato, mentre nella modalita' dispatcher le chiamate
alle funzioni utente sono gestite dal codice di libreria mediante
la funzione Dispatcher()
.
E' possibile selezionare la modalita' di funzionamento in uno
dei seguenti modi:
- opzione nel linguaggio SML
- opzione al lancio di SML
- define di compilazione dei sorgenti generati
MODALITA' WAIT EVENT
Codice Generato Codice Libreria
main
EVENTINIT()
main_SM1()
while( EVENTWAIT(SM1) ) Attende evento per la StateMachine SM1
quando evento ritorna
esegue funzione utente()
EVENTEND()
MODALITA' DISPATCHER
Codice Generato Codice Libreria
main
EVENTINIT()
while( DISPATCHER(CH1) )
while
Attende eventi sul canale CH1
quando evento esegue funzione utente()
EVENTEND()
TESTING
Vi sono due programmi che vengono generati e che operano sulle code degli
eventi, e sono il programma di gestione vero e proprio SMLGEN ed un
programma di test che consente di simulare gli eventi in coda in modo
semplificato.
*********** **********
* * +---------+ * *
* SMLTEST * --> | | | | | | --> * SMLGEN *
* * +---------+ * *
*********** event queue **********
FILES GENERATI
Sono automaticamente generati a partire dalla grammatica utente i seguenti
files:
- smlmain.c
- smlutil.c
- smlgen.c
- smlgen.h
- smlgen.doc
- smlgen.mak
- smltest.c
Questi files generano mediante il comando make -f smlgen.mak i seguenti
programmi:
MODULARITA'
La struttura modulare dei sorgenti generati consente di utilizzare i
sorgenti generati in qualsiasi ambiente operativo, in quanto le chiamate
eseguite verso le code degli eventi possono essere sostituite mediante
il cambio delle definizioni hei files di include, oppure sostituendo
i moduli events.o e queue.o con dei propri files.
La struttura modulare della libreria consente di utilizzare solamente le
parti di codice che si ritengono utili per raggiungere lo scopo
prefissato.
Per esempio se non si necessita di contesti multipli, la parte
context non viene utilizzata.
Si puo' addirittura utilizzare solamente la parte input e timer e creare
un proprio dispatcher per gestire questi eventi.
+------------------+ +------------------+
| generated code | | user code |
+------------------+ +------------------+
| | |
+---------+-------+ |
| |
......................................................................
. LIBRARY SML | | .
. +------------------+ | .
. | events | | .
. +------------------+ | .
. | | .
. +-----------+------------+-----------+ .
. | | | | .
. +---------+ +---------+ +---------+ +---------+ +---------+ .
. | userevt | | timer |--| input | | sm |--| context | .
. +---------+ +---------+ +---------+ +---------+ +---------+ .
. | .
. +----------+-----------+ .
. | | | .
. +--------+ +--------+ +--------+ .
. | queues | | pipe | | stream | .
. +--------+ +--------+ +--------+ .
. .
......................................................................
PORTABILITA'
La portabilita' di SML, grazie alla sua struttura modulare e' quasi assoluta
per quanto riguarda il linguaggio, mentre per la parte relativa alle code
fornite per ora e' limitata alle macchine Unix, la tabella di seguito
illustra le portabilita' esistenti:
Piattaforma | Linguaggio | Code | Timer |
HP-UX | SI | SI | SI |
IBM-AIX | SI | SI | SI |
SCO-UNIX | SI | SI | SI |
MS-DOS | SI | NO | NO |
WIN 3.1 | SI | NO | NO |
WIN95 | SI | NO | NO |
CODICE UTENTE
E' possibile inserire codice utente inserendolo tra i caratteri:
%{
/* ...user code here... */
}%
Il codice utente puo' essere inserito in alcuni punti particolari della
grammatica ed evita l'intervento manuale sui files generati.
In questo modo si puo' personalizzare le strutture dati, inserire codice
all'interno delle funzioni di gestione del codice utente.
Si puo' per esempio inserire nella dischiarazione di una funzione:
...
FUNCTIONS {
NAME SM1Error BEGIN
%{ UsrLogFunction( "Errore" ); }%
EMIT( "SM1: error" )
NEXTSTATE( SM1_S_IDLE )
END
NAME SM1fun1 {
...
Attenzione: Il codice utente inserito puo' rendere non portabili
i files generati.
Si puo' migliorare il problema utilizzando per il linguaggio 'C'
di #include
invece che codice puro.
ESEMPIO
La figura sottostante permette di evidenziare le fasi necessarie per
la creazione di una o piu' State Machine.
Come si puo' notare l'unica parte che l'utente deve eseguire manualmente
e' la fase di definizione delle State Machine.
definizione generazione generazione test verifica
StateMachine SML eseguibili funzionamento
***********
* editor *
***********
|
v
+---------+
| | ***********
| XXX.sml | -> * SML *
| | ***********
+---------+ | +---------+
V | smllib |
+---------+ +---------+
+---------+| |
+---------+|| V
| ||| ***********
|smlgen.X ||+ -> * CC *
| |+ ***********
+---------+ |
V
+---------+
| |
| smltest | -> +-+
| | +-+
+---------+ +-+
+---------+ +-+
| | +-+
| sml | <- +-+ +---------+
| | --------> | sml.log |
+---------+ +---------+
DICHIARAZIONI
Devono essere dichiarate le seguenti parti:
- Modulo
- Dati
- Elenco State Machine
- Funzioni
- State Machine (stati/eventi)
MODULO
La dichiarazione di modulo non ha una grande importanza ai fini
della generazione della macchina a stati, ma consente di dichiarare
lo scopo del contenuto del file.
Inoltre in questa fase e' possibile introdurre linee di codice 'C' che
saranno inserite nel codice generato.
Le linee di codice da inserire devono essere precedute dal carattere
>, dopo l'ultima linea che si desidera inserire di deve mettere la
keywork END.
MODULE Example1 %{
\#include
static flag = 1;
}%
Attenzione: Se si deve utilizzare in carattere '#' precederlo con un
carattere di escape '\', cosicche' non sia interpretato
dal precompilatore.
DATI
Gli unici dati che devono essere dichiarati sono quelli spediti
assieme ad un evento verso una State Machine.
Tutti i dati sono mappati in una struct indipendente con il nome indicato.
Tutte le struct sono poi inserite in una union, che le racchiude tutte.
Nel codice generato e' possibile inserire parti utente.
DATA TYPE BEGIN
NAME SM1data
NAME SM2data
NAME SM1toSM2
NAME SM2toSM1
END
Dalla definizione illustrata vengono generate le seguenti dichiarazioni:
typedef struct {
int state;
/* FIELDS ADDED by USER... */
} SM1data; /*C1*/
/* ...omissis... */
typedef struct {
int state;
/* FIELDS ADDED by USER... */
} SM2toSM1; /*C1*/
/* data sended on channels */
typedef struct {
Evt evt; /* event field */
long len;
union data {
SM1data SM1data;
SM2data SM2data;
SM1toSM2 SM1toSM2;
SM2toSM1 SM2toSM1;
} data;
} Data, *DataP;
CHANNELS
Il channel si puo' definire come un canale di comunicazione ove transitano
gli eventi.
Una StateMachine puo' essere associata ad un solo channel.
Questa StateMachine puo' avere associati piu' context.
Su un unico channel possono esistere piu' StateMachine di tipo diverso.
Possono essere utilizzati come channels i seguenti canali:
- message queues
- shared memory
- pipes
CONTEXT
Viene definito il nome di un contesto che viene tradotto in una
struct 'C' dove si possono aggiungere files utenti.
Il contesto permette di avere piu' istanze di una state machine attive
allo stesso tempo.
Questo permette di gestire situazioni dove, per esempio in state machine
in ambienti di comunicazione, ogni state machine segue il percorso di un
contesto da gestire.
Di seguito il legame tra Channels, StateMachine e Context:
Legenda: {N} Channels
[N] StateMachine
(N) Context
-------> {1} >>>> [1] ---> [5] ---> [9]
| | |
(1) (1) (1)
| |
(2) (2)
|
(3)
-------> {2} >>>> [2] ---> [8] ---> [7] ---> [6]
| | |
. (1) (1) (1)
. |
. (2)
-------> {N} >>>> [3] ---> [4]
| |
(1) (1)
|
(2)
EVENTI
Gli eventi possono provenire da vari tipi di trasporti:
- message queue
- pipe (fifo)
- streams
- devices
- da gestione utente
Un evento ha associato una certo blocco dati.
Un evento puo' essere interno ad una macchina a stati, oppure
spedito (SEND) ad un altra macchina a stati.
Per eventuali implementazioni diverse l'utilizzo delle Message Queue
e' stato mascherato dalle funzioni EvtXXX()
generate come sorgente,
quindi modificandole e' possibile utizzare altri metodi per spedire e/o
ricevere eventi.
Esistono alcuni eventi predefiniti che consentono di far eseguire
alle State Machine alcune operazioni particolari:
- EVENT_NONE evento nullo
- EVENT_TIMER evento da un timer
- EVENT_INPUT evento da un dispositivo di input (pipe, stream)
- EVENT_USER evento gestito dall'utente
- EVENT_QUIT evento di terminazione
- EVENT_RESET evento di reset rimette nello stato di partenza
- EVENT_ERROR errore a livello di sistema
EVENTI DA SORGENTI MULTIPLE
La tabella seguente evidenzia le tipologie di portatori di eventi che
possono essere gestite.
+------------+ +---------------+
rete locale --> streams --> | | | |
files --> pipe --> | dispatcher | --> | State Machine |
timer --> timer --> | | | |
+------------+ +---------------+
PASSAGGIO DATI
Sono definiti i tipi necessari per il passaggio dei dati tra le varie
entita' del modulo.
Il passaggio dei dati tra entita' della stessa State Machine viene
definita mediante una struct.
Il passaggio dati tra diverse State Machine viene definito canale
(CHANNEL) e corrisponde ad una struct.
GESTIONE EVENTI
Tutte le State Machine sono in ascolto ad una Message Queue
e richiedono alla stessa solamente gli eventi a loro pertinenti.
Se vi sono piu' State Machine in ascolto da diverse Message Queue e'
possibile inviare eventi ad una qualsiasi State Machine (caso A).
+-------------+
+------->| WaitEvent() |
| | | StateMachine1
Unix | +----<| EvtSend(2) |
MsgQueue | | +-------------+
+----+ | |
| |>------+ |
| |<--+---|--+
+----+ | | |
| | |
| | | +-------------+
| +------->| WaitEvent() |
| | | | StateMachine2
| +----<| EvtSend(1) |
| +-------------+
|(A)
| +-------------+
| +------->| WaitEvent() |
| | | | StateMachine3
| | +----<| EvtSend(4) |
Unix +---|--|----<| EvtSend(2) |
MsgQueue | | +-------------+
+----+ | |
| |>------+ |
| |<------|--+
+----+ | |
| |
| | +-------------+
+------->| WaitEvent() |
| | | StateMachine4
+----<| EvtSend(3) |
+-------------+
Esempio di coda di eventi in una Message Queue:
OLD - Event TX for SM1 -----+-- estratti da WaitEvent di StateMachine1
. - Event RX for SM2 --+ |
. - Event CK for SM1 --|--+
. - Event TX for SM1 --|--+
. - Event CK for SM2 --+
NEW - Event RX for SM2 --+----- estratti da WaitEvent di StateMachine2
ORDINE DI DICHIARAZIONE
- dichiarazione componente
- dichiarazione tipi
- dichiarazione gerarchia automi
- dichiarazione canali
- definizione automi
DICHIARAZIONE COMPONENTE
E' un semplice identificatore del progetto e del prefisso da
utilizzare per i nomi dei files generati.
Puo' contenere anche una parte di codice che deve essere inserita
nei files generati.
MODULE ident PREFIX ident BEGIN ... END
Tutto cio' che compare tra BEGIN ed END e ha come primo
carattere '>' viene riportato cosi' come e' sul codice generato.
Esempio:
MODULE prova PREFIX prova BEGIN
>#include
>
>int debug = 0;
>
END
DICHIARAZIONE TIPI
La seguente sintassi consente di definire il tipo di dato scambiato su un
canale di comunicazione.
DATA TYPE BEGIN
NAME name
...
END
DICHIARAZIONE STATE MACHINE
La seguente sintassi consente di dichiarare una state machine, il canale
che utilizzera', il tipo di dato che viene scambiato sul canale ed infine se
la SM e' attiva.
STATEMACHINE DEFINITION BEGIN
NAME name CHANNEL number TYPE datatype {ON|OFF}
...
END
DICHIARAZIONE GERARCHIA AUTOMI
DICHIARAZIONE CANALI
DEFINIZIONE AUTOMI
La sintassi seguente permette di definire fisicamente la state machine,
infatti di definiscono gli stati e gli eventi che la compongono.
STATEMACHINE smname {
STATES { state1, state2, ..., stateN }
EVENTS { event1, event2, ..., eventN }
FUNCTIONS {
NAME funname {
instructions...
}
NAME funname {
instructions...
}
...
}
INIT = funname
SET ( state, event ) = funname
SET ( state1, event1 ) = funname
...
SET ( stateN, eventN ) = funname
}
ISTRUZIONI
Le seguenti sono le istruzioni che possono essere inserite:
EMIT( string )
NEXSTATE( state )
SEND( channel, sm, event, datatype )
FILES
I seguenti sono i files generati automaticamente:
smlmain.c .. contiene un main di partenza programma
un main_XXX per ogni SM utilizzata
smlgen.c .. contiene le funzioni delle macchine a stati
e le stringhe di intentificazione di eventi e stati
smlgen.h .. contiene le definizioni dei tipi: dati, contesti
define per eventi e stati
prototipi funzioni
smlgen.doc .. contiene la documentazione delle SM generate
LOG
Viene automaticamente gestito il log delle operazioni.
PREPROCESSOR
Viene utilizzato il preprocessore del linguaggio 'C' con la sua
sintassi completa.
BNF
Convenzioni: XX .. keyword
.. identificatore
.. simbolo non terminale
+ .. presenza di almeno un elemento (da 1 a N)
* .. presenza di elementi (da 0 a N)
[xx] .. xx e' facoltativo
sml :=
+
module_def := MODULE
data_def := DATA TYPE BEGIN
+
END
single_data_def := NAME
machines_def := STATEMACHINE DEFINITION BEGIN
+
END
single_sm_def := NAME TYPE ON|OFF
VERSIONE DEMO
La versione demo di slm ha le seguenti limitazioni:
- consente massimo:
- 1 state machine (illimitate)
- 2 stati (illimitati)
- 2 eventi (illimitati)
- limita le tipologie di eventi a:
- 1 timer (100)
- 1 input (100)
- 1 evento utente (100)
- non consente l'inserimento di codice utente nelle funzioni
- non consente l'inserimento di codice utente nei tipi di dati
- lunghezza codice utente massimo 32 byte
- lunghezza dato massima 32 byte
TIMER
Le possibili operazioni sui timer sono:
int TimerInit( void )
int TimerDisplay( int id )
int TimerAdd( int seconds, int step, int (*fun)(int id) )
int TimerSet( int id, int seconds, int step, int (*fun)(int id) )
int TimerDel( int id )
Timer TimerGet( int id )
int TimerSetStat( int id, int stat )
int TimerCheck( int mode, int *id )
int TimerManage( int mode, int *id )
int TimerSelect( int seconds )
INPUT
Le possibili operazioni sui canali di input sono:
int InputInit( void )
int InputDisplay( int id )
int InputAdd( int fd, int (*fun)(int id) )
int InputSet( int id, int fd, int (*fun)(int id) )
int InputDel( int id )
Input InputGet( int id )
int InputGetN( int id )
int InputManage( int mode, int *id )
int InputSelect( int seconds )
USER EVENTS
Le possibili operazioni sui canali utente sono:
int UserEvtInit( int (*checkfun)(int *id) )
int UserEvtDisplay( int id )
int UserEvtAdd( int fd, int (*fun)(int id) )
int UserEvtSet( int id, int fd, int (*fun)(int id) )
int UserEvtDel( int id )
UserEvt UserEvtGet( int id )
int UserEvtGetN( int id )
int UserEvtManage( int mode, int *id )