-
Notifications
You must be signed in to change notification settings - Fork 46
Constructing the Model
If you want to create your own model for use within ROSS, you'll first have to create some files within the ROSS codebase. For a skeleton model with some details about advanced features please see ROSS-template-model to use as a starting point. Once your newly created model files exist, you need to create a symlink from that directory into ROSS/ross/models. At this point, you're ready to re-run the CMake command from the Installation page.
Before any simulation can take place, we must first construct our simulation model. This mainly consists of the events that take place in the system along with associated variables used to maintain the state of the system. Let's build a simple airport simulation together to walk you through these steps.
We'll borrow an example application from Fujimoto's Parallel and Distributed Simulation Systems. Four variables are needed:
- InTheAir is the number of aircraft in the process of landing or circling a given airport
- OnTheGround is the number of aircraft that have landed
- RunwayFree is a (binary) variable indicating whether or not a runway is in use by a landing aircraft
- NumLanded is the number of aircraft that have landed (for book keeping purposes)
These variables are represented within a struct containing data pertinent to a given airport:
typedef struct {
int InTheAir;
int OnTheGround;
int RunwayFree;
int NumLanded;
} Airport_State;
Further, the following events are required:
- arrival event - an aircraft has arrived at the airport
- landed event - and aircraft has landed
- departure event - an aircraft has left the airport
These events are represented in code by an enum:
enum events { ARRIVAL, LAND, DEPARTURE};
Now we must write code to support all of the events that we are simulating. ROSS runs the same event handler for all events. The handler determines the specific type of event being simulated. So our event handler will look something like this:
void Airport_EventHandler()
switch(M->event_type) {
case ARRIVAL:
...
case LAND:
...
case DEPARTURE:
...
}
}
This pseudocode is fairly high-level at this point. We aren't even modifying our state variables. That is the next step:
void Airport_EventHandler(Airport_State *SV, tw_bf *CV, Msg_Data *M, tw_lp *lp) {
tw_stime ts;
tw_event *CurEvent;
Msg_Data *NewM;
*(int *)CV = (int)0;
switch(M->event_type) {
case ARRIVAL:
// Schedule a landing in the future
SV->InTheAir++;
if((CV->c1 = (SV->RunwayFree == 1))){
SV->RunwayFree = 0;
ts = tw_rand_exponential(lp->id, R);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = LAND;
tw_event_send(CurEvent);
}
break;
case LAND:
SV->InTheAir--;
SV->OnTheGround++;
SV->NumLanded++;
ts = tw_rand_exponential(lp->id, G);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = DEPARTURE;
tw_event_send(CurEvent);
if ((CV->c1 = (SV->InTheAir > 0))){
ts = tw_rand_exponential(lp->id, R);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = LAND;
tw_event_send(CurEvent);
}
else
SV->RunwayFree = 1;
break;
case DEPARTURE:
SV->OnTheGround--;
ts = tw_rand_exponential(lp->id, A);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *) tw_event_data(CurEvent);
NewM->event_type = ARRIVAL;
tw_event_send(CurEvent);
break;
}
}
Don't worry about all the odd-looking code for the moment. Just notice that the function takes an Airport_State parameter. The state is modified based on the type of event (stored in M->event_type
). So, for example, the LAND
event decreases the InTheAir
variable, increases the OnTheGround
variable, and also increases the NumLanded
variable.
Furthermore, all events schedule future events. This is critical because otherwise our simulator would run out of useful work to perform. You can see this in the code above by searching for tw_event_new()
. This function returns a new event which can then be populated with the data for the future event. This event is basically a wrapper for our model's data. Getting a pointer to this data can be achieved using tw_event_data()
. Once we have a pointer to our data, we can set it correctly for the future event, e.g. LAND
events naturally schedule DEPARTURE
events. Once everything is complete, we use tw_event_send()
to send it on its way.
As ROSS is an optimistic simulator, it has the potential to erroneously schedule events in the future. When such an event is detected, all events that took place between the current (virtual) time and the point at which the mistake was made must be undone or "rolled back". For example, an erroneous LAND
event could be undone (from a state perspective) by decreasing NumLanded
and OnTheGround
and increasing InTheAir
. Of course, not all operations are so trivially reversible, hence the use of bit-fields (tw_bf
is a Time Warp Bit-Field). They are necessary (in this example at least) to record which branch of the if
statement was taken. In the reverse handler, the bit-field is checked to determine which path was taken so the proper reverse operations can be performed. The reverse handler in its entirety follows:
void Airport_RC_EventHandler(Airport_State *SV, tw_bf *CV, Msg_Data *M, tw_lp *lp) {
switch(M->event_type) {
case ARRIVAL:
SV->InTheAir--;
if(CV->c1){
tw_rand_reverse_unif(lp->id);
SV->RunwayFree = 1;
}
break;
case LAND:
SV->InTheAir++;
SV->OnTheGround--;
SV->NumLanded--;
tw_rand_reverse_unif(lp->id);
if(CV->c1)
tw_rand_reverse_unif(lp->id);
else
SV->RunwayFree = 0;
break;
case DEPARTURE:
SV->OnTheGround++;
tw_rand_reverse_unif(lp->id);
break;
}
}
The entire file is at the end of this document. Interested readers are referred to the ROSS/ROSS.Net User's Guide located in rossnet/trunk/doc/users-guide
. You may need to type make
to build the pdf.
#include <ross.h>
/* This is a modify verison of airport.c which was done by Justin M. LaPre */
int A = 60;
int R = 10;
int G = 45;
int NumLanded = 0;
enum events { ARRIVAL, LAND, DEPARTURE};
typedef struct {
enum events event_type;
} Msg_Data;
typedef struct {
int OnTheGround;
int InTheAir;
int RunwayFree;
int NumLanded;
} Airport_State;
void Airport_StartUp(Airport_State *, tw_lp *);
void Airport_EventHandler(Airport_State *, tw_bf *, Msg_Data *, tw_lp *);
void Airport_RC_EventHandler(Airport_State *, tw_bf *, Msg_Data *, tw_lp *);
void Airport_Statistics_CollectStats(Airport_State *, tw_lp *);
#define AIR_LP 1
tw_lptype airport_lps[] = {
{
AIR_LP, sizeof(Airport_State),
(init_f) Airport_StartUp,
(event_f) Airport_EventHandler,
(revent_f) Airport_RC_EventHandler,
(final_f) Airport_Statistics_CollectStats,
(statecp_f) NULL
},
{ 0 },
};
int main(int argc, char * argv[]) {
tw_lp *lp;
tw_kp *kp;
g_tw_ts_end = 300;
g_tw_gvt_interval = 16;
/* tw_lptype NumPE NumKP NumLP Message_Size*/
tw_init(airport_lps,1,1,1,sizeof(Msg_Data));
lp = tw_getlp(0);
kp = tw_getkp(0);
tw_lp_settype(lp, AIR_LP);
tw_lp_onkp(lp, kp);
tw_lp_onpe(lp, tw_getpe(0));
tw_kp_onpe(kp, tw_getpe(0));
tw_run();
printf("Number of Landings: %d\n", NumLanded);
return 0;
}
void Airport_StartUp(Airport_State *SV, tw_lp * lp) {
int i;
tw_event *CurEvent;
tw_stime ts;
Msg_Data *NewM;
SV->OnTheGround = 0;
SV->InTheAir = 0 ;
SV->RunwayFree = 1;
SV->NumLanded = 0;
for(i = 0; i < 5; i++) {
ts = tw_rand_exponential(lp->id, A);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = ARRIVAL;
tw_event_send(CurEvent);
}
}
void Airport_EventHandler(Airport_State *SV, tw_bf *CV, Msg_Data *M,
tw_lp *lp) {
tw_stime ts;
tw_event *CurEvent;
Msg_Data *NewM;
*(int *)CV = (int)0;
switch(M->event_type) {
case ARRIVAL:
// Schedule a landing in the future
SV->InTheAir++;
if((CV->c1 = (SV->RunwayFree == 1))){
SV->RunwayFree = 0;
ts = tw_rand_exponential(lp->id, R);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = LAND;
tw_event_send(CurEvent);
}
break;
case LAND:
SV->InTheAir--;
SV->OnTheGround++;
SV->NumLanded++;
ts = tw_rand_exponential(lp->id, G);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = DEPARTURE;
tw_event_send(CurEvent);
if ((CV->c1 = (SV->InTheAir > 0))){
ts = tw_rand_exponential(lp->id, R);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *)tw_event_data(CurEvent);
NewM->event_type = LAND;
tw_event_send(CurEvent);
}
else
SV->RunwayFree = 1;
break;
case DEPARTURE:
SV->OnTheGround--;
ts = tw_rand_exponential(lp->id, A);
CurEvent = tw_event_new(lp, ts, lp);
NewM = (Msg_Data *) tw_event_data(CurEvent);
NewM->event_type = ARRIVAL;
tw_event_send(CurEvent);
break;
}
}
void Airport_RC_EventHandler(Airport_State *SV, tw_bf *CV, Msg_Data *M,
tw_lp *lp) {
switch(M->event_type) {
case ARRIVAL:
SV->InTheAir--;
if(CV->c1){
tw_rand_reverse_unif(lp->id);
SV->RunwayFree = 1;
}
break;
case LAND:
SV->InTheAir++;
SV->OnTheGround--;
SV->NumLanded--;
tw_rand_reverse_unif(lp->id);
if(CV->c1)
tw_rand_reverse_unif(lp->id);
else
SV->RunwayFree = 0;
break;
case DEPARTURE:
SV->OnTheGround++;
tw_rand_reverse_unif(lp->id);
break;
}
}
void Airport_Statistics_CollectStats(Airport_State *SV, tw_lp * lp) {
NumLanded += SV->NumLanded;
}
Continue this example in Running the Simulator.