Ověření modelu nádraží v modelcheckeru spin a jazyce promela

Program ke stažení pro netrpělivé

stanice v promele

Informace o spinu

Potřebné informace o smv najdete na adrese spinroot.com/.
Jde se zde doklikat i k manuálu, podle kterého bylo toto stvořeno

Zprovoznění spinu a xspinu pod linuxem suse 10.1

Bez větších problému přesně podle popisu ze zmíněných stránek

Modelovaná situace

Model obsahuje:

simulace dvou koncových stanic a stanice S, která mezi nimi koordinuje dopravu. z obou koncových stanic do stanice S vede obousměrná jednokolejná trať.

Popis semantiky modelu:

Modelovaná situace:
A <-> S <->
Stanice jsou simulovány procesy, tratě kanály. Krom stanic je ještě jeden proces na monitor, a jeden proces na tzv Timer,, který posune čas, pokud už nic jiného nemůže nic udělat.
Čas (proměnná global_time) neni absolutní - je cyklický, pokud doshane určité hodnoty je nastaven na 0. To zabraňuje nadměrnému růstu stavového prostoru
Koncová stanice se v každém čase rozhoduje,zda vyslat nebo nevyslat vlak k S. Vlak se jiste pokusi vyslat, pokud už dlouho žádny neposlala, jinak se rozhodne nedeterministicky, zda to zkusi či nikoli. Pro každý vyslany vlak, musi být volné nástupiště ve stanici S. Stanice S v každém čase projde nástupiště, odešle vlaky, které mohou odjet, a pokud může, prijme vlaky které dorazily a maji být v daném čase vyzvednuty.
Ve stanici S se vlaky zastaví na nějakou minimalni dobu.(Tj nalezne se pro ne vhodné nástupiště a tam se uloží) Pote je stanice S vyšle do opacne stanice, než ze kter vyjely, pokud může. Aby se nestalo, ze vlaky začnou jezdit jen z jednoho směru a do druhého nepojedou, Ma každá trat jeste položku force sending. Pokud už dlouho nejel vlak ze stanice S do příslušné koncové stanice, vynutí se jízda tímto směrem ( z teto koncové stanice nemohou jezdit vlaky, takze drive ci později přijede nějaký z druhé strany. Koncové stanice stanice přijmou vlaky, ktere k min dorazí a zlikviduji je
tratě jsou řízeny semafory, (pro každý směr jeden semafor, tj pro každou trať dva semafory)

protokol zaručuje

( a verifikace skrz aserty ověřuje ), ze se vlaky nesrazí, ze se v každém směru jde v konečném čase dostat, že se nikdy nestane, aby ve stanici S bylo víc vlaku něž nástupišť.

Realizace v modelcheckeru spin

Mapsani kodu stanic, hlavni stanice, monituru, ktery obsahuje kontrolovane invarianty.

Kod

Kod pozuity pro verifikaci a simulaci

 /* pojmenovani jednotlivych stanic */
#define station_S 1
#define station_A 2
#define station_B 3
#define no_station 4



/* kolik vlaku se vejde do stanice S */
#define station_size 10

/*kolik vlaku se vejde na trat, je >=  nez station_size, nemusi se pak testovat, zda je plny*/
#define track_size 10

#define max_track_length 3

/* delka trate v jednotkach casu, kterou ji vlak pojede */
#define track_A_S_length 2

/* delka trate v jednotkach casu, kterou ji vlak pojede */
#define track_B_S_length 3

/* delka stani (minimalni) na stanici */
#define min_time_on_station 2

/* pokud uz takhle dlouho z koncove stanice nevyjel vlak, tak pokud muze, urcite vyjede */
#define try_send_train_interval 5

/* po jake dobe se vynuti jizda vlaku nejakym smerem, pokud tam uz dlouho nejel */
#define one_direction_timeout_A 5 
#define one_direction_timeout_B 7

/* maximum z vyse */
#define max_direction_timeout 7

#define SEMAPHORE_UP 1
#define SEMAPHORE_DOWN 0

#define TRUE 1
#define FALSE 0
/* jak dlouhe je kolecko pro cas. je vhodne ho dat vzhedem state - expolsion volit co nejmensi */
#define TIME_MODULO 15

/* typ pro vlak, ktery je posilan kanalem, trati */
typedef train_t {
  byte dest;          /* cilova stanice, bud A nebo B */
  byte arrival_time;  /* cas, kdy vlak vyrazil */
};



/* typ pro trat, jednak vlaky, ktere se vydaly na cestu, a jednak stav trati */
typedef track_t {
 chan trains = [track_size] of {train_t};  /* kanal, kde jsou vsecky vlaky vyslane na cestu */
 byte length;
 bool semaphore_from_S; /* semafor v prislusnem smeru */
 bool semaphore_to_S;   /* semafor v prislusnem smeru */
 byte leading_to;	/* kam vede tato trat */
 bit time;		/* jestli uz v tomto case byla provedena akce v rpislusne stanici(pripadne vyslani vlaku) */

 short last_train_to_S;   /* pocitadlo casu, kdy jel posledni vlak do S po teto trati */
 short last_train_from_S; /* pocitadlo casu, kdy jel posledni vlak z S po teto trati */
 bit  force_sending;      /* pokud je true, je vynuceno posilani vlaku z S do ES, a ceka se, nez takovy  nejaky vlak pojede */
};

/* typ pro nastupiste */
typedef platform_t {
  bool isfull;        /* zda je obsazene */
  train_t train;      /* vlak na danem nastupisti */
  int departure_time; /* cas odjezdu */
  byte continue_to;   /* v jakem smeru vlak pokracuje */
  bit should_gone;    /* priznak, pokud vlak uz mel odjet a jeste tak neucinil protoze trat byla plna */
}
/************************************************************************************************/
/* GLOBALNI PROMENNE */
/************************************************************************************************/
/* trati se stanice A do S a ze stanice B do S */
track_t track_A_S;
track_t track_B_S;

/* znacka, pro aktualni cas, neni to absolutni cas, ale je to cyklicka promenna, jedina podminka na ni je, aby
byla vetsi nez nejdelasi trat.
*/
byte global_time;
byte n_free_platforms = station_size; /* pocet volnych nastupist na stanici S */
short tmpx; /*pomocna promenna*/
bit checked_in_this_time = FALSE;


/*funkce na praci se zacyklenym casem */
inline increase_byte_modulo(base, addition){
	tmpx = base + addition;
	 do
	  ::(tmpx > TIME_MODULO)->
	   tmpx = tmpx - TIME_MODULO;
	  ::(tmpx == TIME_MODULO)->
	   tmpx = 0;
	  ::else -> break;
	 od;

	 base = tmpx;
}

/*funkce na inkrementaci casu a vseho co s nim souvisi */
inline increase_time(){
	track_A_S.last_train_to_S++;
	track_A_S.time = FALSE;
	track_B_S.time = FALSE;
	track_A_S.last_train_from_S++;
	track_B_S.last_train_to_S++;
	track_B_S.last_train_from_S++;
	checked_in_this_time = FALSE;
	increase_byte_modulo(global_time, 1);
}

/* ..z koncovych stanic do S. musi byt volna z atomic bloku, kde se vi, ze semaform pro danou trat je UP */
inline send_train_toS(track){
	printf("Sending train from station %d , to oposit station is %d, 
	semaphore to S is %d \n", track.leading_to, oposit_station_name, track.semaphore_to_S);
	arr_time = global_time;
	increase_byte_modulo(arr_time, track.length)
	train.arrival_time = arr_time;
	train.dest = oposit_station_name;
	n_free_platforms --; /* zabrat si misto na statnici S */
	track.semaphore_from_S = SEMAPHORE_DOWN; /*posilame vlak do S, takze semafor z S musi byt DOWN */
	printf("semaphore from S set to %d \n", track.semaphore_from_S);
	track.trains ! train;
	track.last_train_to_S = 0; /* vynulovat cas, kdy tam mel jet vlak */
}

/* nedeterministicky se rozhodne, zda vlak poslat, nebo ne. Pokud uz dlouho neposlal, tak pokud muze, tak posle */
inline maybe_send_train_toS(track){
if 
/* pokud jsme uz dlouho neposlali vlak, tak ho posleme urcite, pokud muzeme */
 ::(track.last_train_to_S > try_send_train_interval)->
	send_train_toS(track);
/* jinak se rozhodnem nedeterministicky, zda ho posleme, nebo ne */
 ::(track.last_train_to_S <= try_send_train_interval)->
	send_train_toS(track);
 ::(track.last_train_to_S <= try_send_train_interval)->
	skip;
 ::(track.last_train_to_S <= try_send_train_interval)->
	skip;
 ::(track.last_train_to_S <= try_send_train_interval)->
	skip;
fi;
}

/* jedna z koncovych stanic */
proctype EndStationA(){
 train_t train;
 byte oposit_station_name;
 byte arr_time;
 oposit_station_name = station_B;
 
 do
	/* pokud muzes poslat vlak do S */
	::((track_A_S.semaphore_to_S == SEMAPHORE_UP) && (track_A_S.time == FALSE) 
	&& (n_free_platforms > 0) && (track_A_S.force_sending == FALSE) )->
	 atomic { /* test and set atomicky, jeste jednou zkontrolovat, mohlo se to preplanovat, a nekdo mohl 
			treba poslat vlak z druhe strany */
	  if
	   ::((track_A_S.semaphore_to_S == SEMAPHORE_UP) && (n_free_platforms > 0)  && (track_A_S.time == FALSE) && (track_A_S.force_sending == FALSE) )->
	    maybe_send_train_toS(track_A_S);
	    track_A_S.time = TRUE;
	   :: else -> skip;
	  fi;
	 }

	/* prijmout vlaky, ktere maji dorazit v tomto case */
	::(track_A_S.trains?[station_A, eval(global_time)])->
		track_A_S.trains ? train; /* do toho nam nikdo nemuze skocit, nemusi to byt atomicky */
		track_A_S.force_sending = FALSE;
		track_A_S.last_train_from_S = 0;
		atomic { /* ale do tohohle uz jo */
		if
		 :: (empty(track_A_S.trains) ) -> track_A_S.semaphore_to_S = SEMAPHORE_UP;
		 :: (nempty(track_A_S.trains) )->skip;
		fi
		}
		printf ("train arived to station A \n");
  od;
}

/* jedna z koncovych stanic */
proctype EndStationB(){
 train_t train;
 byte oposit_station_name;
 byte arr_time;
 oposit_station_name = station_A;
 
 do
	/* pokud muzes poslat vlak do S */
	::((track_B_S.semaphore_to_S == SEMAPHORE_UP) && (n_free_platforms > 0) 
		&& (track_B_S.time == FALSE) && (track_B_S.force_sending == FALSE) )->
	 atomic { /* test and set atomicky, jeste jednou zkontrolovat, mohlo se to preplanovat, a nekdo mohl 
			treba poslat vlak z druhe strany */
	  if
	   ::((track_B_S.semaphore_to_S == SEMAPHORE_UP) && (n_free_platforms > 0) && (track_B_S.time == FALSE) && (track_B_S.force_sending == FALSE))->
	    maybe_send_train_toS(track_B_S);
	    track_B_S.time = TRUE;
	    printf("semaphore from S set to %d \n", track_B_S.semaphore_from_S);
	   :: else -> skip;
	  fi;
	 }

	/* prijmout vlaky, ktere maji dorazit v tomto case */
	::(track_B_S.trains?[station_B, eval(global_time)])->
		track_B_S.trains? train; /* do toho nam nikdo nemuze skocit, nemusi to byt atomicky */
		track_B_S.force_sending = FALSE;
		track_B_S.last_train_from_S = 0;
		atomic { /* ale do tohohle uz jo */
		if
		 :: (empty(track_B_S.trains) ) -> track_B_S.semaphore_to_S = SEMAPHORE_UP;
		 :: (nempty(track_B_S.trains) )->skip;
		fi
		}
		printf ("train arived to station B \n");
  od;
}

/* vycisteni nastupiste po tom co z nej odjede vlak */
inline clear_platform(platform){
 platform.isfull = FALSE;
 platform.should_gone = FALSE;

}

/*procedura na vyslani vlaku ze stanice S na trat track */
inline send_train_from_S(track) {
		  if 
		    ::(track.semaphore_from_S == SEMAPHORE_UP)->
		     track.semaphore_to_S = SEMAPHORE_DOWN;
		     increase_byte_modulo(tmp, track.length);
		     platforms[index].train.arrival_time = tmp;
		     track.trains!platforms[index].train;
		     clear_platform(platforms[index]);
		     n_free_platforms ++;
		   ::else->skip; /* nechceme, aby se nam to tu zablokovalo */
		  fi;
}

/*zkontroluje, zda nejdou nejake vlaky odeslat a pripadne je vysle */
inline send_prepared_trains(){
	index = 0;
	tmp = global_time;
	 do
	  ::(index < station_size)->	
	   if 
		 ::(platforms[index].isfull == TRUE && 
			platforms[index].departure_time == global_time)-> platforms[index].should_gone = TRUE;
		 ::else -> skip;
	   fi;

 	   if
 		 ::(platforms[index].isfull == TRUE && platforms[index].should_gone 
			&& platforms[index].continue_to == station_A  && track_A_S.semaphore_from_S == SEMAPHORE_UP )->
		   atomic {send_train_from_S(track_A_S) };
		 ::(platforms[index].isfull == TRUE && platforms[index].should_gone 
			&& platforms[index].continue_to == station_B  && track_B_S.semaphore_from_S == SEMAPHORE_UP )->
		   atomic {send_train_from_S(track_B_S)};
		 ::else -> skip;
	  fi;

	  index++;
	 ::(index == station_size)-> break;
	 od;
}



/* prijia vlaky do stanice S muze byt volana jen z bloku chraneneho jako atomic */
inline receive_train(track){
	index = 0;
	  do
	   ::(index < station_size)->
	    if
	     :: (platforms[index].isfull == FALSE)-> /* najit si nejake volne nastupiste */
		break;
	     :: (platforms[index].isfull == TRUE)-> skip;
	    fi;
	    index ++;
	   :: (index == station_size)->
		printf("Pruser jako svina, sem se to nikda nemelo dostat\n");
		assert(!(index == station_size) );
		
	  od;
	track.trains ? train;
	tmp = global_time;
	increase_byte_modulo(tmp,min_time_on_station);
	platforms[index].departure_time = tmp;
	platforms[index].isfull = TRUE;
	platforms[index].continue_to = train.dest;
	platforms[index].train.dest = train.dest;
	if
	 ::(empty(track.trains))-> //trat je prazdna, zvednout semafor semafor
	 	track.semaphore_from_S = SEMAPHORE_UP;
         ::(nempty(track.trains))-> skip;
	fi;
}

/* vynuti to, ze v konecnem case bude vyaln vlak z A do prislusne stanice */
inline force_send(track){
	track.force_sending = TRUE;
}

proctype StationS (){
/* pole nastupist */
platform_t platforms[station_size];

byte index;
byte tmp;
train_t train;


/*inicializace nastupist */
index = 0;
do
 ::(index < station_size)->
	clear_platform(platforms[index]);
	index ++;
 :: (index == station_size)->
	break;
od;


do
	/*zkontrolovat zda je na nadrazi je nejaky vlak, ktery muze odjet*/
	::(checked_in_this_time == FALSE)-> 
		checked_in_this_time = TRUE;
		send_prepared_trains(); 
	/* je na trati je nejaky vlak k prijeti z A? */
	::(track_A_S.trains ? [ station_B , eval(global_time)])->
		atomic {receive_train(track_A_S);} /* nesmime se preplanovat v pulce prijimani vlaku, */ 
	/* je na trati je nejaky vlak k prijeti z B? */
	::(track_B_S.trains ? [station_A , eval(global_time)])->
		atomic { receive_train(track_B_S); }
	/*dlouho jsme neposlali vlak do A*/
	::(track_A_S.last_train_from_S > one_direction_timeout_A &&  track_A_S.force_sending == FALSE )->
		force_send(track_A_S);	
	/*dlouho jsme neposlali vlak do B*/
	::(track_B_S.last_train_from_S > one_direction_timeout_B &&  track_B_S.force_sending == FALSE )->
		force_send(track_B_S);
od;
}


/* spousti se, pokud nic jineho nemuze popojet, posouva u vlaku, jak dlouho jsou na trati/nastupisti */
proctype Timer(){
do	
	::timeout -> 
		increase_time();	
od;
}

/*kontroluje model, ze je v poradku */
proctype Monitor(){
byte max_time_S_End = 255;
byte max_time_End_S = 255;

do
	/*kontrola, ze se nesrazeji vlaky na trati A_S */
	:: ( (track_A_S.trains ?? [station_B,_]) && track_A_S.trains ?? [station_A,_] ) ->
      		assert( !(track_A_S.trains ?? [station_B,_] && track_A_S.trains ?? [station_A,_]) );
	/*kontrola, ze se nesrazeji vlaky na trati B_S */
	:: (track_B_S.trains ?? [station_B,_] && track_B_S.trains ?? [station_A,_]) ->
      		assert(!(track_B_S.trains ?? [station_B,_] && track_A_S.trains ?? [station_A,_]) );
	/* jde se ve zhora omezenem case dostat z z S do konocovych stanic */
	:: (track_A_S.last_train_from_S > max_time_S_End) ->
		assert(!(track_A_S.last_train_from_S > max_time_S_End));
	:: (track_B_S.last_train_from_S > max_time_S_End)->
		assert(!(track_B_S.last_train_from_S > max_time_S_End));
	/* jde se ve zhora omezenem case dostat z koncovych stanic do S */
	:: (track_A_S.last_train_to_S > max_time_S_End)->
		assert(!(track_A_S.last_train_from_S > max_time_S_End));
	:: (track_B_S.last_train_from_S > max_time_S_End)->
		assert(!(track_B_S.last_train_from_S > max_time_S_End));
od;
}

init{
  /* inicializovat trati */
  track_A_S.length = 3;
  track_A_S.time = FALSE;

  track_A_S.force_sending = FALSE;
  track_B_S.force_sending = FALSE;
  track_A_S.semaphore_from_S = 1;
  track_A_S.semaphore_to_S = 1;
  track_A_S.leading_to = station_A;
  track_B_S.length = 4;
  track_B_S.semaphore_from_S = 1;
  track_B_S.semaphore_to_S = 1;
  track_B_S.leading_to = station_B;
  track_B_S.time = FALSE;
  /* spusteni koncovych stanic */
  run EndStationA();
  /* spusteni stanice S */
  run StationS(); 
  /* spusteni koncovych stanic */
  run EndStationB();
  /*timer */
  run Timer();
  /* kontorla */
  run Monitor();
}  
	
   
  

Simulace

Simulace je pekna v xspinu, je tam pekne videt napr, ze se vlaky nesrazi. Pri ladeni, kdyz byla v protokolu chyba objevila se v simulaci velmi brzy

Verifikace

Mela by se pouzit exhaustiv, ale protoze na to jsem nemel pamet, pouzil jsem bitstate (jen nektere stavy a hasovani mezi nimi)

Vystup verifiace

Kod pozuity pro verifikaci a simulaci

error: max search depth too small
Depth=  999999 States=   1e+06 Transitions= 1.07884e+06 Memory= 53.801
Depth=  999999 States=   2e+06 Transitions= 2.86111e+06 Memory= 53.801
Depth=  999999 States=   3e+06 Transitions= 4.64646e+06 Memory= 53.801
Depth=  999999 States=   4e+06 Transitions= 6.56122e+06 Memory= 53.801
Depth=  999999 States=   5e+06 Transitions= 8.51564e+06 Memory= 53.801
Depth=  999999 States=   6e+06 Transitions= 1.04725e+07 Memory= 53.801
Depth=  999999 States=   7e+06 Transitions= 1.228e+07 Memory= 53.801
Depth=  999999 States=   8e+06 Transitions= 1.40956e+07 Memory= 53.801
Depth=  999999 States=   9e+06 Transitions= 1.59908e+07 Memory= 53.801
Depth=  999999 States=   1e+07 Transitions= 1.77607e+07 Memory= 53.801
Depth=  999999 States= 1.1e+07 Transitions= 1.93466e+07 Memory= 53.801
Depth=  999999 States= 1.2e+07 Transitions= 2.09984e+07 Memory= 53.801
Depth=  999999 States= 1.3e+07 Transitions= 2.26599e+07 Memory= 53.801
Depth=  999999 States= 1.4e+07 Transitions= 2.4494e+07 Memory= 53.801
Depth=  999999 States= 1.5e+07 Transitions= 2.62457e+07 Memory= 53.801
Depth=  999999 States= 1.6e+07 Transitions= 2.81444e+07 Memory= 53.801
Depth=  999999 States= 1.7e+07 Transitions= 2.98162e+07 Memory= 53.801
Depth=  999999 States= 1.8e+07 Transitions= 3.15215e+07 Memory= 53.801
Depth=  999999 States= 1.9e+07 Transitions= 3.32735e+07 Memory= 53.801
(Spin Version 4.2.7 -- 23 June 2006)
	+ Partial Order Reduction

Bit statespace search for:
	never claim         	- (not selected)
	assertion violations	+
	cycle checks       	- (disabled by -DSAFETY)
	invalid end states	+

State-vector 244 byte, depth reached 999999, errors: 0
1.96241e+07 states, stored
1.47749e+07 states, matched
3.4399e+07 transitions (= stored+matched)
1.87497e+07 atomic steps

hash factor: 3.41971 (best if > 100.)

bits set per state: 3 (-k3)

Stats on memory usage (in Megabytes):
4866.782	equivalent memory usage for states (stored*(State-vector + overhead))
16.777	memory used for hash array (-w26)
36.000	memory used for DFS stack (-m1000000)
0.964 	other (proc and chan stacks)
0.060 	memory lost to fragmentation
53.801	total actual memory usage

unreached in proctype EndStationA
	line 220, "pan_in", state 80, "-end-"
	(1 of 80 states)
unreached in proctype EndStationB
	line 256, "pan_in", state 81, "-end-"
	(1 of 81 states)
unreached in proctype StationS
	line 321, "pan_in", state 100, "assert(!((index==10)))"
	line 321, "pan_in", state 142, "assert(!((index==10)))"
	line 381, "pan_in", state 181, "-end-"
	(3 of 181 states)
unreached in proctype Timer
	line 134, "pan_in", state 11, "tmpx = (tmpx-15)"
	line 390, "pan_in", state 25, "-end-"
	(2 of 25 states)
unreached in proctype Monitor
	line 400, "pan_in", state 2, "assert(!((track_A_S.trains??[3,_]&&track_A_S.trains??[2,_])))"
	line 403, "pan_in", state 4, "assert(!((track_B_S.trains??[3,_]&&track_A_S.trains??[2,_])))"
	line 406, "pan_in", state 6, "assert(!((track_A_S.last_train_from_S>max_time_S_End)))"
	line 408, "pan_in", state 8, "assert(!((track_B_S.last_train_from_S>max_time_S_End)))"
	line 411, "pan_in", state 10, "assert(!((track_A_S.last_train_from_S>max_time_S_End)))"
	line 413, "pan_in", state 12, "assert(!((track_B_S.last_train_from_S>max_time_S_End)))"
	line 415, "pan_in", state 16, "-end-"
	(7 of 16 states)
unreached in proctype :init:
	(0 of 18 states)
85.80user 0.09system 1:26.32elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+13296minor)pagefaults 0swaps




 

Co nam to říká

Na prvnim řádku, že jsme neprošli celý satvový prostor, že to je jen odhad, jak by to mohlo dopadnout.
Nedosažitelné stavy v monitoru a ve stanici S značí, že žádná z kontrolovaných cest neporušila žádný konrolovaný invariant
Nedosažitelný stav v timeru je způsoben tím, že procedura increase_byte_modulo může mít jako druhý parametr i něco jiného než jedničku, ovšem timer vždy zvyšuje čas jen o jedničku. Tento nedosažitelný stav je tedy OK (daná inline procedura je společná několika procesům, v jiných je tento stav dosažitelný)

Důležité je zejména, že invarianty jsou validní a je značná šance, že protokol je funkční

Program ke stažení

stanice v promele

Poznámky a problémy při programování

Programovaci jazyk se tvari jako normalni, (if - fi;), ale normalne se nechova (blokovaniv if),. Nepodarilo se mi vymyslet, jak predat nejakou promennou procesu referenci a ne hodnotou.