import java.util.ArrayList;
import java.util.Random;
/**
* File: PizzaParlorSimulation.java
* Author: GregBrannon, April 2014
*
* inspired by this thread:
*
* [url]http://www.javaprogrammingforums.com/whats-wrong-my-code/36975-elevator-simulation-event-time-not-clock.html#post144786[/url]
*
* demonstrates the use of a queue to accomplish events in simulation time
* rather than real or scaled time. multi-threading is not used, because this
* simulation is intended to be a precursor to a multi-threaded program, and
* more than a single thread is not needed
*
* simulation rules, assumptions, etc.
* - the simulation has 4 events: order, prepare, cook, and remove.
* - each pizza order is an Event (my custom Event class) that is modified to
* be each of the other events as the simulation proceeds
* - the first pizza is ordered at time zero
* - each subsequent pizza order arrives at intervals of 2 to 5 (inclusive)
* time units
* - a pizza order event is assigned a random queue time of at least
* 5 time units to a maximum of 8 time units
* - pizza preparation time is a random number of time units between 8 and 15
* - the simulation will run for the number MAX_PIZZAS
* - the time to bake a pizza is randomly chosen between 20 and 30 (inclusive)
* time units
* - 4 pizzas can be cooking at once (set by the variable ovenSpace)
*
* to avoid providing a total solution, the posted simulation code omits my
* custom Event and OrderedEventQueue classes. ask for help if needed to
* construct your own versions of these classes. the requirements
* for both classes should be derivable from the posted code, except for the
* queue's insertInOrder() method and the Event's compareTo() method that
* it depends on. if there's any secret sauce, it's in those two methods
*
* concept: events are added to a queue ordered by their start times,
* priorities, and event numbers. the simulation time begins at zero and
* advances to each event's start time as the event is removed from the queue
* and processed. as each event is processed, its start time and priority are
* modified to reflect the next action, and then returned to the queue or
* removed, if the event has completed the final action
*
* TODO add the option to specify the number of pizzas that can be in the
* oven at a time by varying the variable ovenSpace
*/
public class PizzaParlorSimulation
{
// instance variables
private final boolean TEST;
private int currentSimTime;
private int orderNumber;
private int pizzasInOven;
private int ovenSpace;
private int pizzasDelivered;
private final int MAX_PIZZAS;
private boolean acceptingOrders;
private OrderedEventQueue<Event> orderedEventQueue;
private Random random;
private Event newEvent;
// default constructor - used to initialize instance variables and then
// start the simulation
public PizzaParlorSimulation()
{
// a flag to indicate whether the simulation should run in the test
// or production modes
TEST = false;
// initialize instance variables
currentSimTime = 0;
orderNumber = 0;
pizzasInOven = 0;
ovenSpace = 4;
pizzasDelivered = 0;
orderedEventQueue = new OrderedEventQueue<Event>();
random = new Random( System.currentTimeMillis() );
MAX_PIZZAS = 40;
acceptingOrders = true;
// start the simulation of ordering and baking pizzas
makePizzaPies();
} // end default constructor
// a controller for the pizza demo
private void makePizzaPies()
{
// continue the simulation until event queue is empty
do
{
// add another pizza order to the queue if the simulation is still
// accepting orders
if ( acceptingOrders )
{
orderPizza();
}
// retrieve the next event from the event queue and process it,
// updating the current simulation time with the event's start time
processNextEvent();
reportStatus();
}
while ( !orderedEventQueue.isEmpty() );
} // end method makePizzaPies()
// creates a pizza order event and adds it to the queue. prints a message
// if the maximum number of orders have been accepted
public void orderPizza()
{
// the first the pizza order arrives in the queue at time zero
int eventStartTime = 0;
// only allow number of pizza orders defined by MAX_PIZZAS
if ( orderNumber < MAX_PIZZAS )
{
// in test mode, report the status of the simulation
if ( TEST )
{
System.out.println( "Pizza order #" + orderNumber +
" is being accepted" );
}
// determine the pizza order's details
// the start time for all pizzas after the first is the current
// event time plus a random number between 2 and 5, inclusive
if ( orderNumber > 0 )
{
eventStartTime = currentSimTime + random.nextInt( 4 ) + 2;
}
// the rest of the order's attributes . . .
String eventName = "OrderPizza" + orderNumber;
int eventNumber = orderNumber++;
// set the time to process the order (minimum queue time)
// a random number between 5 and 8
int eventDuration = random.nextInt( 4 ) + 5;
// the queue time for events is orderWaitTime + ovenWaitTime and
// then increases each time the event must wait for a spot in the
// oven
int eventQueueTime = eventDuration;
// the following is not used at the order stage
// TODO (or at all yet)
int eventEndTime = 0;
// create the order and add it to the queue in order
newEvent = new Event( eventName, eventNumber, eventStartTime,
eventDuration, eventQueueTime, eventEndTime );
// set the new event's priority to 3 (the highest)
newEvent.setEventPriority( 3 );
orderedEventQueue.insertInOrder( newEvent );
}
else
{
System.out.
println( "The pizza parlor is not accepting more orders." );
acceptingOrders = false;
}
} // end method orderPizza()
// retrieves the next event from the queue and processes it
private void processNextEvent()
{
Event nextEvent = null;
// take the first event from the queue
if ( !orderedEventQueue.isEmpty() )
{
nextEvent = orderedEventQueue.remove( 0 );
}
else
{
System.out.println( "Simulation complete!" );
return;
}
// advance the current time to the event's start time
// (this will be zero for the first event but will advance after that)
currentSimTime = nextEvent.getEventStartTime();
// a message to monitor progress
System.out.println( "The name of the event being processed is: " +
nextEvent.getEventName() );
// if the event is an order . . .
if ( nextEvent.getEventName().startsWith( "Order" ) )
{
// a message to monitor progress in TEST mode
if ( TEST )
{
System.out.println( "Assembling pizza # " +
nextEvent.getEventNumber() + " before cooking at time " +
currentSimTime );
}
// set the time to prepare the pizza for the oven (build the pizza)
// a random number between 8 and 15
nextEvent.setEventDuration( random.nextInt( 7 ) + 8 );
// add the next queue time to the total event queue time
nextEvent.setEventQueueTime( nextEvent.getEventQueueTime() +
nextEvent.getEventDuration() );
// set up the event to for its next step
nextEvent.setEventName( "CookPizza" + nextEvent.getEventNumber() );
// set the new event's priority to 2 (the middle)
newEvent.setEventPriority( 2 );
// set this event's next start time as the current time plus the
// event's duration. setting the event start time is the key to
// simulating correctly
nextEvent.setEventStartTime( currentSimTime +
nextEvent.getEventDuration() );
// return the event to the queue
orderedEventQueue.insertInOrder( nextEvent );
}
// . . . or if it is to cook the pizza . . .
else if ( nextEvent.getEventName().startsWith( "Cook" ) )
{
// this step is limited by the amount of oven space. to make the
// simulation interesting, the user could be asked how many pizzas
// can be cooked at a time before the simulation begins. the default
// value is four, but that can be increased to see the effect of
// cooking space to complete the desired number of pizzas
if ( pizzasInOven < ovenSpace )
{
// a message to monitor progress in TEST mode
if ( TEST )
{
System.out.println( "Moving pizza # " +
nextEvent.getEventNumber() +
" into the oven at time " + currentSimTime );
}
// set the time to cook the pizza, a random number between
// 20 and 30
nextEvent.setEventDuration( random.nextInt( 11 ) + 20 );
// add the next queue time to the total event queue time
nextEvent.setEventQueueTime( nextEvent.getEventQueueTime() +
nextEvent.getEventDuration() );
// set up the event to for its next step
nextEvent.setEventName( "RemovePizza" +
nextEvent.getEventNumber() );
// set the new event's priority to 1 (the lowest)
nextEvent.setEventPriority( 1 );
// set this event's next start time as the current time plus the
// event's duration. setting the event start time is the key to
// simulating correctly
nextEvent.setEventStartTime( currentSimTime +
nextEvent.getEventDuration() );
// return the event to the queue
orderedEventQueue.insertInOrder( nextEvent );
// increment the number of pizzas in the oven
pizzasInOven++;
}
else
{
// a message to monitor progress in TEST mode
if ( TEST )
{
System.out.
println( "The oven is full, must wait for room." );
}
// increment the event's start time and return it to the queue
nextEvent.setEventStartTime( currentSimTime + 1 );
// increment the event's total queue time
nextEvent.
setEventQueueTime( nextEvent.getEventQueueTime() + 1 );
orderedEventQueue.insertInOrder( nextEvent );
}
}
// . . . or, finally, to remove the pizza from the oven . . .
else if ( nextEvent.getEventName().startsWith( "Remove" ) )
{
// a message to monitor progress
System.out.println( "Removing pizza # " +
nextEvent.getEventNumber() + " from the oven at time " +
currentSimTime );
// set the event's end time
nextEvent.setEventEndTime( currentSimTime );
// decrement the number of pizzas in the oven
pizzasInOven--;
// increment the number of pizzas delivered
pizzasDelivered++;
}
} // end method processNextEvent()
// report current simulation time, total pizzas ordered, total pizzas
// delivered, pizza orders in queue, waiting for oven, in oven, total
// queue time
private void reportStatus()
{
/* this is a more compact message used to monitor the sim's progress
* while debugging */
if ( TEST )
{
System.out.println( "The current eventNumber:startTime:priority"
+ ":duration of the events in the queue is: " );
for ( int i = 0 ; i < orderedEventQueue.size() ; i++ )
{
System.out.print( orderedEventQueue.get( i ).
getEventNumber() + ":" );
System.out.print( orderedEventQueue.get( i ).
getEventStartTime() + ":" );
System.out.print( orderedEventQueue.get( i ).
getEventPriority() + ":" );
System.out.print( orderedEventQueue.get( i ).
getEventDuration() + ", " );
}
System.out.println();
}
// TODO total queue time is a snapshot of the queue time for all events
// currently in queue. further coding will be required to accumulate
// all queue time that occurred during the entire simulation. care will
//be required to ensure that queue time is not double counted
else
{
// report the simulation's current results
System.out.println( "\nAt simulation time: " + currentSimTime +
", the status is:" );
System.out.println( "Total pizzas ordered: " + orderNumber );
System.out.println( "Total items in the queue: " +
orderedEventQueue.size() );
System.out.println( "Total pizzas waiting for the oven: " +
orderedEventQueue.getNumberOfEventsWithPriority( 2 ) );
System.out.println( "Total pizzas in oven: " + pizzasInOven );
System.out.println( "Total pizzas delivered: " + pizzasDelivered );
System.out.
println( "Total queue time of events currently in queue: " +
orderedEventQueue.getTotalQueueTime() );
}
} // end method reportStatus()
// a main method to simply start the application
public static void main( String[] args )
{
new PizzaParlorSimulation();
} // end main() method
} // end class PizzaParlorSimulation