CS 1723/1721 Simulation Case Study

Objectives:


Index

I. Simulation and Modeling
II. Event-driven Simulation
III. Events
IV. Incoming Lines as Event Sources
V. A Simulation of One Incoming Line
VI. Using an Abstract Class to Simplify the Models
VII. The OutgoingLine class (a consumer of events)
VIII. The Router with No Buffering
IX. The Buffered Router


I: Simulation and Modeling

The word simulate means to imitate or to have the same effect as.  Computer simulation imitates an activity in the real world using a computer program in order to understand how the real-world process behaves.  The advantage of computer simulation is that you can easily change the program and observe impact of modifications on the result.

For example, an airport designer might know the rates at which airplanes are coming in from other airports and how long it takes for a single plane to land.  The designer would like to estimate how many runways are needed to handle the load and how long, on average, the airplanes would have to circle before they could land.  Because planes don't come in from different airports at perfectly spaced intervals, it would be hard to figure out the answers to these questions by hand.  However, a simple program to simulate the airport traffic can give good estimates to the designer.

The variation in traffic from the other airports can be modeled by generating arrival times at random from some statistical distribution.  Much research has been done on what this distribution should be.  The computer simulation just keeps track of the interarrivals as they are randomly picked and computes the estimates needed by the designer.

In this case study, we look at the design of a network router.  Network routers are the connection points between different links in a network.  Fig. 1 shows a diagram of a simple network router.  The router takes incoming messages from other routers and computers on a network (the incoming lines) and copies them to another destination (the outgoing line).


Figure 1: The Network Router


A network router has a set of input lines (feeder airports) that carry messages (airplanes) to the node. The router forwards the messages to the outgoing lines (runways). It takes a certain amount of time to send the message on the outgoing line (land the plane). During that time the outgoing line (runway) cannot be used for anything else. The designer would like to know, given the expected input traffic, how many output lines (runways) are needed. If the router does not allow messages to wait (planes to circle) until an outgoing line becomes available, the designer would also like to know how many messages are lost (Planes are usually sent back to originating airport instead of being thrown away.)


Exercise 1: Find another real-world problem that is in the same form as the network router problem and the airport runway simulation. Fill in the equivalents for the following.

Node:
Message:
Incoming Line:
Outgoing Line:
Waiting:


II. Event-driven Simulation

We now need to decide on a strategy for implementing our simulation in a computer program.  We could define objects representing each of the items in the model (messages, lines and the router).  We could have a variable representing time, and as we step forward (increment time) we could ask each object in the model to perform the actions it would take at this time.  This general strategy is called time-driven simulation.  The main difficulty with a time-driven strategy is that in the real world, time is continuous.  How large a step can we take in time and still be sure that we don't miss anything?

A second, more-common strategy is called event-driven simulation.  Each thing that happens is represented by an event.  Examples of events are the arrival of a message or the sending of a message in the network simulation. In the airport simulation, events may include taking off, circling the airport, landing and taxiing to the gate.  The components of the model generate events.  The simulation itself keeps track of the events and sends them to the components for processing in the order that they occurred.  Time is updated to the time of the next event at each step in the simulation.

The idea of event-driven operation may seem strange at first.  However, you have experienced this mode of operation in using interactive computer applications such as word processors.  When you open such an application, the program does an initial setup and then waits for an event (e.g., a keystroke or a mouse-click from you indicating what to do).  The program processes the event and then resumes waiting for you to do something else.  In contrast, most of the programs that you write for your courses are execution-driven.  They begin execution at a well-defined point (the main method) and continue executing a well-specified sequence of commands until completion.

A computer simulation of the airport or network router needs appropriate bookkeeping to keep track of everything that is going on.  Our event-driven simulation uses object-oriented programming techniques that make it relatively easy to keep track of time, the components and the events (interactions among the components).  A SimulationModel object will have a field keeping track of the current time.  Each event will be represented by an Event object.  Components will be represented as EventHandler objects, which may generate events (such as message arrivals and airplane arrivals) and handle events (that are generated by other event handlers, such as to route a message or to land an airplane on a specific runway).  Typically, the handling of an event results in generating a new event. To manage the generation and the handling of these events in the order in which they suppose to happen in time, the simulation model uses efficient data structures to manage the set of events and the set of event handlers.


Exercise 2: For the example that you gave in Exercise 1, list possible events. 



III. Events

Each thing that happens is called an event.  Events have a who (who caused the event to happen) a what (what type of event happened), a when (what time did the event occur), and a whom (whom the event is for).  For the network simulation, the events are mainly the sending and arrival of messages.  Fig. 2 shows a definition of the Event class that we will use in the simulation.  Notice that Event implements the Java Comparable interface.  This means that Event objects can be ordered.  In this case they are ordered primarily by their times.

Figure 2: The Event Class

public class Event implements Comparable {
public static final int STOP = 0;
public static final int MSG_ARRIVAL = 1; // message arrived at router
public static final int MSG_ROUTE = 2; // message assigned to outgoing line
public static final int MSG_SENT = 3; // message sent by outgoing line

private int who; // Who did it
private int what; // What they did
private double when; // When they did it
private int whom; // Whom is it for

public Event(int who, int what, double when, int whom) {
this.who = who;
this.what = what;
this.when = when;
this.whom = whom;
}

public int getWho( ) { return who; }
public int getWhat( ) { return what; }
public double getWhen( ) { return when; }
public int getWhom() { return whom; }

public int compareTo(Object obj) {
Event e = (Event) obj;
double timeDifference = when - e.when;
if (timeDifference < 0)
return -1;
else if (timeDifference > 0)
return 1;
// if equal times, make STOP last
if (what == STOP && e.what != STOP)
return 1;
else if (what != STOP && e.what == STOP)
return -1;
return 0;
}

public String toString() {
return getClass().getName() + "[Who:" + who + ", What:" + what
+ ", When:" + when + ", Whom:" + whom + "]";
}
}

Exercise 3: What types of events are identified by the Event class (Figure 2)?

Exercise 4: What are the events in the airport simulation?


In the simulation we will want to process events in the order that they occur.  The priority queue is a natural data structure for storing events in a specified order as they come in.  We can use the removeMin method of PriorityQueueADT to remove the event from the priority queue with the smallest when, that is, the event that happens next.


Figure 3: The PriorityQueue Interface

public interface PriorityQueueADT <T extends Comparable> {

public void add(T element); // inserts element in priority queue
public T removeMin( ); // removes and returns the smallest item
public T findMin( ); // returns smallest item
public void makeEmpty( ); // removes all items
public boolean isEmpty( ); // returns true if empty; else false
public int size( ); // returns the size of the queue
}

Exercise 5: Write code to generate 100 MSG_ARRIVAL events with consecutive ID numbers and random times of occurrence between 0 and 1000.0. Place the events on a priority queue as they are generated. Remove them from the queue and calculate the average interarrival time between events. (The interarrival time between consecutive events e1 and e2 is just e2.getWhen() - e1.getWhen()). What would you expect the interarrival time to be?
Ans:
int numberEvents = 100;
double maxTime = 1000;
PriorityQueueADT<Event> p = new LinkedPriorityQueue();
Random rand = new Random();
// Generate the events and put them on the queue
for (int i = 1; i <= numberEvents; i++) {
Event e = new Event(i, Event.MSG_ARRIVAL, rand.nextDouble() * maxTime, i);
p.add(e);
}

double lastArrival = 0.0;
while (!p.isEmpty()) {
Event e = p.removeMin();
System.out.println("Removing " + e);
lastArrival = e.getWhen();
}
System.out.println("The average interarrival: " +
(lastArrival/numberEvents));

The implementation uses the fact that the very last arrival is equal to the sum of the interarrival times. We expect the average to be about 1000/100 = 10.

Exercise 6: Complete Parts I and II of Recitation Laboratory 6.

Exercise 7: What happens when you create an event whose "what" does not correspond to one of the predefined values STOP, MSG_ARRIVAL, MSG_ROUTE, or MSG_SENT?
Ans: Nothing special. The constructor does not check to make sure that the event type is valid.

Exercise 8: What data field(s) in the Event class determine(s) the order that the events are removed from the priority queue?
Ans:
The when field.

Exercise 9: Why does the Event class need to implement the Comparable interface?
Ans:
The priority queue must have a way of ordering the events. The Comparable interface method compareTo permits an ordering.



IV. Incoming Lines as Event Sources

A simulation consists of event handlers and an event loop that maintains a queue of pending events and updates the time based on the most recent event.  Event handlers can be event sources (producers of events), event sinks (consumers of events) or both.  The network router shown in Fig. 1 has two incoming lines that act as event sources and one outgoing line that acts like an event sink.  The router in the middle acts as both an event sink (receiving events from incoming lines) and an event source (sending events to outgoing lines).  This section discusses incoming lines.  Statistically, the two incoming lines of Fig. 1 are identical, in the sense that over long periods they follow the same distribution of times between messages.  When you look at the times of the individual messages, they appear to be random.  Arrival times that are generated from statistical distributions are generally specified as relative times or interarrival times.  That is, they are specified as the time between the current message and the previous one.  These times were generated from a statistical distribution that had an average interarrival time of 20, meaning that if we generated a lot of numbers and averaged them, we would expect to get an average of about 20 time units between messages.

Exercise 10: Suppose the incoming lines of Fig. 1 generate messages at the following times:

Calculate the interarrival time for each individual line.
Ans:
Line # Arrival Time Interarrival Times Average Interarrival Time
0
10, 45, 64 10, 35, 19 (10+35+19)/3 = 64/3 = 21.33
1
10, 33, 50, 69 10, 23, 17, 19 (10+23+17+19)/4 = 69/4 = 17.25

The network router sees the messages from the two incoming lines interleaved as shown in Fig. 4.


Figure 4: Message arrival times of multiple incoming lines are interleaved at the network router.


When multiple incoming lines (sources of messages) generate messages for the network router, the router sees the messages interleaved.  In this particular run, the average time between messages for the individual lines was 20, so when two lines contribute messages, one would expect a time between messages at the router to be around 20/2 = 10 if the simulation were run for a long time.


Exercise 11: Calculate the average interleaved interarrival time for the two lines described in Exercise 10.
Ans:
Message Arrival Times: 10, 10, 33, 45, 50, 64, 69
Time Between Messages: 10, 0, 23, 12, 5, 14, 5
Average time between messages: (10+0+23+12+15+14+5)/7 = 69/7 = 9.9

In the physical world, incoming lines carry messages to the router, which then copies the messages to the correct output lines based on the message destinations.  In the simulation, the IncomingLine objects produce MSG_ARRIVAL events that represent messages in the physical world.  In our implementation, the IncomingLine objects generate event objects at time intervals at random with an average time between messages of averageInterarrival.

Fig. 5 shows an implemenation of the IncomingLine.  This class keeps an internal clock variable that tracks its time.  Each time the getNextArrival method is called, it first picks a value at random from the interval [0, 2*averageInterarrival).  Then, the method updates the value of clock by that amount and
increments the total number of messages.  Finally, the method creates and returns a new MSG_ARRIVAL event for the new time.  The average interarrival time can be computed simply as the clock value divided by the total number of messages.  We will discuss the EventHandler interface and the initialEvent and processEvent methods in a following section.

Figure 5: An IncomingLine class

public class IncomingLine implements EventHandler {
private double interarrival;
private int ID;
private int routerID;
private int totalMessages;
private Random rand;
private double clock;

public IncomingLine(int ID, double interarrival, int routerID) {
this.interarrival = interarrival;
this.ID = ID;
this.routerID = routerID;
totalMessages = 0;
rand = new Random(ID + 1); // each Random has different seed
clock = 0;
}

public int getID( ) { return ID; }
public double getClock( ) { return clock; }
public int getTotalMessages( ) { return totalMessages; }

// return the next arrival event
public Event getNextArrival() {
clock += 2 * interarrival * rand.nextDouble();
totalMessages++;
return new Event(ID, Event.MSG_ARRIVAL, clock, routerID);
}

// return the first arrival event
public Event initialEvent() {
return getNextArrival();
}

// return the next arrival event if this is the previous arrival
public Event processEvent(Event event) {
if (ID == event.getWho() &&
Event.MSG_ARRIVAL == event.getWhat())
return getNextArrival();
return null;
}

public String toString() {
return "Incoming: ID=" + ID +
", average interarrival time=" + interarrival +
", current time=" + clock +
", messages generated=" + totalMessages;
}
}

Exercise 12: Write code to create an IncomingLine whose average message interarrival time is 30. Generate 100 events and output them.
Ans:
IncomingLine line = new IncomingLine(1, 30, 2);
int numberEvents = 100;
for (int i = 0; i < numberEvents; i++)
System.out.println("Event " + i + " is " + line.getNextArrival());
Exercise 13: Complete Part III of Recitation Laboratory 6.

Exercise 14: How is the average interarrival time of an IncomingLine related to the averageInterarrival parameter in the constructor?
Ans:
They are the same.

Exercise 15: Consider two IncomingLine objects, one with an average interarrival time of 10 and the other with an average interarrival time of 30. Estimate the expected time between interleaved messages if the simulation is run until an event time exceeds 1200.
Ans:
In 1200 time units, the first IncomingLine is expected to generage about 120 events, while the second will generate about 40 events. The average time between events can be estimated as 1200/160 = 7.5

Exercise 16: Using Random to generate the interarrival times results in a uniform distribution of message arrivials, i.e., all message arrival intervals are equally likely. Do you think this is a good representation of message arrivals in the real world?
Ans:
No. For a given average arrival interval, messages are likely to arrive more closely spaced than the average. A few messages will be delayed for very long times --- much longer than twice the average. Research has shown that the Poisson distribution gives a good statistical description of arrival times for many real-world problems.



V. Simulation of a Router with One Incoming Line

How does event-driven simulation work?  Usually when an event occurs (e.g., a message arrives), it causes the state of other components in the system to change (e.g., the counter for the total messages is incremented).  The simulation keeps track of all events and processes them in order of their occurrence.  Since the events are the only interesting things that happen, the simulation updates the time to be the time of the next event.  Typically, events are stored in a priority queue ordered by the time of the occurrence.  "Finding the next event" means calling removeMin on that priority queue to retrieve the event that happens next.  We then update the current time to the time of the event that has just "occurred" and process the event.  Fig. 6 summarizes the simulation algorithm

Figure 6: The basic simulation algorithm


An implementation of this algorithm for one incoming line is shown in Fig. 7. The simulation creates a priority queue called eventSet to hold the events. The runSimulation method runs the simulation until a stoppingTime has been reached.

Figure 7: A simulation with a single incoming line

public class SingleIncoming {
private double currentTime;
private PriorityQueueADT<Event> eventSet;
private int numberMessages;
private int numberEvents;
private String title;
private boolean started;
private boolean stopped;
private IncomingLine inLine;

public SingleIncoming(String title, double arrival) {
currentTime = 0.0;
eventSet = new LinkedPriorityQueue<Event>();
numberMessages = 0;
numberEvents = 0;
this.title = title;
started = false;
stopped = false;
inLine = new IncomingLine(1, arrival, 2);
}

public void addEvent(Event e) { eventSet.add(e); }
public int getMessages( ) { return numberMessages; }
public int getEvents( ) { return numberEvents; }
public double getTime( ) { return currentTime; }
public String getTitle( ) { return title; }
public void setStopped( ) { stopped = true; }

public void initializeEvents() {
addEvent(inLine.getNextArrival());
}

public void processEvent(Event e) {
switch (e.getWhat()) {
case Event.MSG_ARRIVAL:
addEvent(inLine.getNextArrival());
numberMessages++;
break;
case Event.STOP:
setStopped();
break;
default:
System.err.println(e + " {unrecognized event}");
break;
}
}

public void runSimulation(double stoppingTime) {
if (!started) {
initializeEvents();
started = true;
}
addEvent(new Event(0, Event.STOP, stoppingTime, 0));
stopped = false;
while (!stopped && !eventSet.isEmpty()) {
Event e = eventSet.removeMin();
numberEvents++;
currentTime = e.getWhen();
processEvent(e);
}
}
}

Fig. 8 shows client code that could be use to set up a simulation of one incoming line that has an averageInterarrival of 30.  The simulation is run until time 100 and the results are output.  After that the simulation is run for 200 more time units (until time 300).  The runSimulation method continues where the last run left off rather than starting over.

Figure 8: Client code to run the simulation of Fig. 7

SingleIncoming sim = new SingleIncoming("One incoming", 30);
sim.runSimulation(100);
System.out.println("Time = " + sim.getTime() +
", Number messages:" + sim.getMessages() +
", Number events:" + sim.getEvents());
sim.runSimulation(300);
System.out.println("Time = " + sim.getTime() +
", Number messages:" + sim.getMessages() +
", Number events:" + sim.getEvents());


VI. Using Interfaces and a Superclass to Simplify the Models

All event-driven simulations handle the time and the events in essentially the same way as the algorithm of Fig. 6.  The specifics of the problem (in our case a network router with 1 incoming line) only appear in the creation of components, the generation of initial events and the processing of events.  We will allow components to create initial events and to process each event.  This will be accomplished using the EventHandler interface shown in Fig. 9.

Figure 9: The EventHandler interface

public interface EventHandler { 
  public abstract Event initialEvent(); // return an initial event or null
  public abstract Event processEvent(Event current); // process current event, returning a new event or null
}

Each component will be asked for an initial event.  If the component is not an event source, it can return null.  Also, each component will asked to process each event.  The component needs to recognize whether it should process the event, and if the component does process the event, the component can return a new event in response.  For example, consider the IncomingLine class shown in Fig. 6.  The initialEvent method returns an event for the first message. The processEvent method recognizes whether the current event was created by this object, and if so, returns an event for the next message.

The simulation will be performed using a class (SimulationModel) that includes methods for adding EventHandler objects and simulating them.  The key methods of SimulationModel are shown in Fig. 10.


Figure 10: Constructor and key methods of the SimulationModel class

SimulationModel(String title); // indicates the particular pro oblem
public final void addHandler(EventHandler h); // add h to handlerSet
public final void addEvent(Event e); // add e to eventSet
public void initializeEvents( ); // generate starting events
public void processEvent(Event e) // process an event
public final void runSimulation(double stoppingTime);
public final void setStopped( ); // sets a flag to stop simulation
public final void setDebug(boolean debug); // sets a flag to print events

The final methods are complete and cannot be changed by any subclass of SimulationModel.  We will write subclasses that extend SimulationModel for each specific simulation. The subclass can add to or override the constructor and the initializeEvents and processEvent methods. In this way, we have separated what is common to all simulations in the superclass and the specifics in the subclass.  Fig. 11 shows the actual code for the SimulationModel class.


Figure 11: An implementation of the SimulationModel class.

public class SimulationModel {
private ArraySet<EventHandler> handlerSet;
private PriorityQueueADT<Event> eventSet;
private double currentTime;
private int numberEvents;
private boolean started;
private boolean stopped;
private String title;
private boolean debug;

public SimulationModel(String title) {
currentTime = 0.0;
eventSet = new LinkedPriorityQueue<Event> ();
handlerSet = new ArraySet<EventHandler> ();
numberEvents = 0;
this.title = title;
started = false;
stopped = false;
}

public final int getEvents( ) { return numberEvents; }
public final double getTime( ) { return currentTime; }
public final String getTitle( ) { return title; }
public final void setStopped( ) { stopped = true; }
public final void setDebug(boolean debug) { this.debug = debug; }

public final void addEvent(Event e) {
if (e != null) {
if (debug) System.out.println("Adding " + e);
eventSet.add(e);
}
}

public final void addHandler(EventHandler h) {
handlerSet.add(h);
}

public void initializeEvents() {
Iterator<EventHandler> scan = handlerSet.iterator();
while (scan.hasNext())
addEvent(scan.next().initialEvent());
}

public void processEvent(Event e) {
if (debug) System.out.println("Processing " + e);
if (e.getWhat() == Event.STOP) {
setStopped();
} else {
Iterator<EventHandler> scan = handlerSet.iterator();
while (scan.hasNext())
addEvent(scan.next().processEvent(e));
}
}

final public void runSimulation(double stoppingTime) {
if (!started) {
initializeEvents();
started = true;
}
addEvent(new Event(0, Event.STOP, stoppingTime, 0));
stopped = false;
while (!stopped && !eventSet.isEmpty()) {
Event e = eventSet.removeMin();
numberEvents++;
currentTime = e.getWhen();
processEvent(e);
}
}

public String toString() {
String result = title + ": Time = " + currentTime
+ ", Events = " + numberEvents;
Iterator<EventHandler> scan = handlerSet.iterator();
while (scan.hasNext())
result += "\n" + scan.next().toString();
return result;
}
}

Fig. 12 gives an implementation of the IncomingOnlyModel that consists of a single incoming line.  Notice the uses of the super keyword.  The constructor first calls the constructor of SimulationModel (by super(title)) and then does some model-specific initialization.  The processEvent method first calls the processEvent method of SimulationModel (by super.processEvent(e)) and then monitors the event.  The toString method appends an additional line.

Figure 12: An implementation of the IncomingOnlyModel class.

public class IncomingOnlyModel extends SimulationModel {
private int numberMessages;

public IncomingOnlyModel(String title, double arrival) {
super(title); // do all of the general initialization first
addHandler(new IncomingLine(1, arrival, 2));
numberMessages = 0;
}

public void processEvent(Event e) {
super.processEvent(e);
switch (e.getWhat()) {
case Event.MSG_ARRIVAL:
numberMessages++;
break;
case Event.STOP:
break;
default:
System.err.println(e + " {unrecognized event}");
break;
}
}

public String toString() {
return super.toString() + "\n" + "numberMessages=" + numberMessages;
}
}

Exercise 17: Complete the implementation Part IV of Recitation Lab 6.


VII. The OutgoingLine class (a consumer of events)

The simplest router simulation consists of one incoming line, one router, and one outgoing line. The incoming line generates messages randomly at a specified average rate.  The router routes the message to the outgoing line.  The outgoing line takes a fixed amount of time to send a message out (the send time). If the incoming line produces a message that is routed to the outgoing line before it has finished sending the previous message, the second message is dropped (thrown away, lost).

An outgoing line will need to be represented and will be responsible for the following information:

Fig. 13 shows the implementation of the OutgoingLine class. It keeps track of the number of messages that it sends and rejects. The utilization is the fraction of time that a resource is busy. The OutgoingLine class computes its utilization from the number of messages that it sent and the message service time.

Figure 13: The OutgoingLine class

public class OutgoingLine implements EventHandler {
private int ID;
private int messagesSent;
private int messagesLost;
private double sendTime; // time to send a message
private double whenDone;

public OutgoingLine(int ID, double sendTime) {
this.ID = ID;
messagesSent = 0;
messagesLost = 0;
this.sendTime = sendTime;
whenDone = 0.0;
}

public int getID( ) { return ID; }
public int getMessagesSent( ) { return messagesSent; }
public int getMessagesLost( ) { return messagesLost; }
public double getSendTime( ) { return sendTime; }
public double getWhenDone( ) { return whenDone; }

public double getUtilization() {
return (messagesSent == 0) ? 0.0 : messagesSent * sendTime / whenDone;
}

public Event initialEvent() {
return null; // This has no initial event
}

// If a message is routed to this line, send it or drop it
public Event processEvent(Event event) {
if (ID == event.getWhom() &&
Event.MSG_ROUTE == event.getWhat()) {
if (event.getWhen() >= whenDone) {
messagesSent++;
whenDone = event.getWhen() + sendTime;
return new Event(ID, Event.MSG_SENT, whenDone, event.getWho());
} else {
messagesLost++;
}
}
return null;
}

public String toString() {
return "Outgoing: ID=" + ID + ", line speed=" + sendTime +
", msgs sent=" + messagesSent + ", msgs lost=" + messagesLost +
", utilization=" + getUtilization();
}
}

Exercise 18: Complete Part V of Recitation Laboratory 6.


VIII. The Router with No Buffering

We now look at the router in more detail. A simple router has an incoming line and an outgoing line. The router routes messages that arrive from the incoming line to the outgoing line to send. If the outgoing line is busy when a message arrives, the message is dropped. In this section, we will implement an UnbufferedRouter class for the router and an UnbufferedNodeModel class for the whole model.

The types of events that are needed for this simulation are: MSG_ARRIVAL, MSG_ROUTE, and MSG_SENT. The incoming line will produce MSG_ARRIVAL events for the router.  The router will respond by producing MSG_ROUTE events for the outgoing line.  The outgoing line will respond by producing MSG_SENT events if the messages are not dropped.

Fig. 14 shows the implementation of the unbuffered router.  The constructor needs to have the ID of the outgoing line so that events can be created for the outgoing line.  The processEvent method recognizes arrival events for the router and routes them to the outgoing line.


Figure 14: The UnbufferedRouter class.

public class UnbufferedRouter implements EventHandler {
  private int ID;
  private int outgoingID;
  private int messagesRouted;

  public UnbufferedRouter(int ID, int outgoingID) {
    this.ID = ID;
    this.outgoingID = outgoingID;
    messagesRouted = 0;
  }

  public int getID( ) { return ID; }
  public int getMessagesRouted( ) { return messagesRouted; }

  public Event initialEvent( ) {
    return null; // This has no initial event
  }

  // If a message arrived for the router, route it
  public Event processEvent(Event event) {
    if (ID == event.getWhom() &&
        Event.MSG_ARRIVAL == event.getWhat()) {
      messagesRouted++;
      return new Event(ID, Event.MSG_ROUTE, event.getWhen(), outgoingID);
    }
    return null;
  }
 
  public String toString( ) {
    return "Router: ID=" + ID + ", msgs routed=" + messagesRouted;
  }
}


Fig. 15 goes through a trace of a simple example of the unbuffered simulation.  The IDs of the incoming line, router, and outgoing line are respectively 1, 2, and 3.  The simulation is set to stop at time 25.  The send time for the outgoing line is 10.  The incoming line generates messages at times 8, 15 and 19, and 31.


Figure 15: A trace of an unbuffered simulation.

I. Generate the initial events and insert them in the priority queue:
EventQueue:
who what when whom
1
MSG_ARRIVAL 8 2

0
STOP 25 0

II. while the simulation is not stopped and the priority queue is not empty


<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ARRIVAL
8
1
0
0
<the router immediately routes the message to the outgoing line>
<the incoming line generates a new message to arrive later> EventQueue:
who what when whom
2
MSG_ROUTE
8
3
1
MSG_ARRIVAL 15 2
0 STOP 25 0

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ROUTE
8
2
0
0
<the outgoing line sends the message>
EventQueue:
who what when whom
1
MSG_ARRIVAL 15 2
3
MSG_SENT
18
2
0 STOP 25 0

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ARRIVAL
15
3
0
0
<the router routes the message to the outgoing line>
<the incoming line generates a new message>
EventQueue:
who what when whom
2
MSG_ROUTE 15 3
3
MSG_SENT
18
2
1
MSG_ARRIVAL
19
2
0 STOP 25 0

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ROUTE
15
4
1
0
<outgoing line is currently busy - so message is lost>
EventQueue:
who what when whom
3
MSG_SENT 18 2
1
MSG_ARRIVAL 19 2
0
STOP 25 0

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_SENT
18
5
1
1
EventQueue:
who what when whom
1
MSG_ARRIVAL 19 2
0
STOP 25 0

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ARRIVAL
19
6
1
1
<the router routes the message to the outgoing line>
<the incoming lines generates a new message> EventQueue:
who what when whom
2
MSG_ROUTE
19
3
0
STOP 25 0
1
MSG_ARRIVAL 31 2

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ROUTE
19
7
1
1
<the outgoing line sends the message>

EventQueue:

who what when whom
0
STOP 25 0
3
MSG_SENT 29 2
1
MSG_ARRIVAL 31 2

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
STOP
25
8
1
1

At this point the simulation breaks out of its loop. If we call toString, we will have:

UnbufferedNodeModel: Time = 25.0, Events = 8
Incoming: ID=1, average interarrival time=10.0, current time=31.0, messages generated=4
Router: ID=2, msgs routed=3
Outgoing: ID=3, line speed=1.0, msgs sent=2, msgs lost=1, utilization=0.68965517241379315
Received Messages=3, Routed Messages=3, Sent Messages=1
There is a discrepency between the numbers because the incoming line and outgoing line have generated events past time 25.
Exercise 20:
Draw a time line for the outgoing messages.
___|______|______|_____|______|_____|______|_____|______|______|_____|__
0 5 10 15 20 25 30 35 40 45 50

Fig. 16 shows a complete implementation of the UnbufferedNodeModel. It has an IncomingLine, an UnbufferedRouter and an OutgoingLine.


Figure 16: The UnbufferedNodeModel class

public class UnbufferedNodeModel extends SimulationModel {
private int receivedMessages;
private int routedMessages;
private int sentMessages;

public UnbufferedNodeModel(double arrival, double sendtime) {
super("UnbufferedNodeModel");
addHandler(new IncomingLine(1, arrival, 2));
addHandler(new UnbufferedRouter(2,3));
addHandler(new OutgoingLine(3, sendtime));
receivedMessages = 0;
routedMessages = 0;
sentMessages = 0;
}

public int getReceivedMessages( ) { return receivedMessages; }
public int getSentMessages( ) { return sentMessages; }
public int getRoutedMessages( ) { return routedMessages; }

public void processEvent(Event e) {
super.processEvent(e);
switch (e.getWhat()) {
case Event.MSG_ARRIVAL:
receivedMessages++;
break;
case Event.MSG_ROUTE:
routedMessages++;
break;
case Event.MSG_SENT:
sentMessages++;
break;
case Event.STOP:
break;
default:
System.err.println(e + " {unrecognized event}");
break;
}
}

public String toString() {
return super.toString()
+ "\nReceived Messages=" + getReceivedMessages()
+ ", Routed Messages=" + getRoutedMessages()
+ ", Sent Messages=" + getSentMessages();
}
}

Exercise 19: Complete Part VI of Recitation Laboratory 6.


VIII. The Buffered Router

The buffered router has an incoming line, a buffered router, and an outgoing line.  The buffered router has a buffer for temporarily holding messages when the outgoing line is busy.  The incoming line generates messages randomly, with a specified average rate. The outgoing line takes a fixed amount of time to send a message out.  If the incoming line produces a message before the outgoing line has finished sending, the router places the message in the buffer (we will use a queue) until the outgoing line is free.  The messages will be removed from the queue (routed) in the order they were received.

The following changes are needed to convert the UnbufferedRouter class to a BufferedRouter class:

This BufferedRouter will be implemented in the lecture.

Fig. 16 goes through a trace of a simple example of the buffered simulation using the same message arrivals as Fig. 15.  The IDs of the incoming line, router, and outgoing line are respectively 1, 2, and 3.  The simulation is set to stop at time 25.  The send time for the outgoing line is 10.  The incoming line generates messages at times 8, 15 and 19, and 31.  The trace will be the same until the second message arrives at time 15.


Figure 16: A trace of a buffered simulation from time 15


<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ARRIVAL
15
4
1
0
<outgoing line is currently busy>
<the router puts the message in the queue of pending messages>
EventQueue:
who what when whom
3
MSG_SENT 18 2
1
MSG_ARRIVAL 19 2
0
STOP 25 0
Pending Message Queue:
who what when whom
1
MSG_ARRIVAL 15 2

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_SENT
18
5
0
1
<the outgoing line is free>
<the router dequeues the message that arrived at time 15 and routes it to the outgoing line>
EventQueue:
who what when whom
2
MSG_ROUTE
18
3
1
MSG_ARRIVAL 19 2
0
STOP 25 0
Pending Message Queue is empty
<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ROUTE
18
6
0
1
<the outgoing line sends the message>
EventQueue:
who what when whom
1
MSG_ARRIVAL 19 2
0
STOP 25 0
3
MSG_SENT 28 2
Pending Message Queue is empty
<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
MSG_ARRIVAL
19
7
0
1
<the outgoing line is busy>
<the router adds the MSG_ARRIVAL event to queue of pending messages>
EventQueue:
who what when whom
0 STOP 25 0
3
MSG_SENT 28 2
1
MSG_ARRIVAL 31 2
Pending Message Queue:
who what when whom
1
MSG_ARRIVAL 19 2

<remove the smallest event>
e currentTime numberEvents messagesLost messagesSent
STOP
25
8
0
2
EventQueue:
who what when whom
3
MSG_SENT 28 2
0
MSG_ARRIVAL 31 2
Pending Message Queue:
who what when whom
1
MSG_ARRIVAL 19 2

At this point the simulation breaks out of its loop.
Exercise 21: Draw a time line for the outgoing messages.
___|______|______|_____|______|_____|______|_____|______|______|_____|__
0 5 10 15 20 25 30 35 40 45 50