Project 2: Restaurant Simulation
Due: Friday, March 16
API Specification
API Specification

Project Overview
In this project, you will write a simulation for a restaurant. The simulation has four parameters: the number of eaters (diners) that wish to enter the restaurant; the number of tables in the restaurant (there can only be as many eaters as there are tables at a time; other eaters must wait for a free table before placing their order); the number of cooks in the kitchen that fill orders; and the capacity of machines in the kitchen used for producing food.

Eaters in the restaurant place their orders (a list of food items) when they enter the restaurant. These orders are then handled by available cooks. Each cook handles one order at a time. A cook handles an order by using machines to cook the food items. There will be one machine for each kind of food item. Each machine produces food items in parallel (for different orders, or even the same order) up to their stated capacity.

Each food item defines a fixed average amount of time to prepare: a burger, made by machine Grill, takes on average 500ms to make; fries, made by machine Frier, takes on average 250ms to make; and a coke, made by machine Soda Fountain, takes 100ms on average to make. The actual time needed to make a food item will be computed by adding a random amount of noise (+/- 10% of the Food's average cooking time) determined by the machine doing the cooking.

Our particular restaurant will have at least three food items (and three accompanying machines) defined, but new food items and their accompanying machines can be added.

General Requirements
Much of the design of the simulation will be up to you. However, we do have some requirements.

First, you must not use intrisic synchronization in any part of your simulation. In particular, the keyword synchronized and calls to wait, notify, and notifyAll must not appear anywhere in your code. Any synchronization you use must be higher-level abstractions provided by the concurrency libraries, such as CountDownLatches, BlockingQueues, etc.

Second, you must implement a method to validate the output of the simulation. This will be run immediately after your simulation completes, to validate its output.

Third, you must implement (or use) the following classes and the methods specified for them. All of these are available in the skeleton code. You may implement any other methods you need for these classes, as you wish. You can also implement additional classes as needed:
  • Simulation.java - main() method is the simulation entry point
  • Food.java* - food items
  • Eater.java - runs in a thread to implement a restaurant patron
  • Cook.java - runs in a thread; makes orders provided by eaters/li>
  • Machine.java - cooks particular food items
  • SimulationEvent.java* - instances represent interesting events that occur during the simulation
  • Validate.java - defines method to validate the results of the simulation
* The starred classes are provided for you, and should not be changed; the remaining classes can be changed within the bounds given below.

Particulars
  • Simulation
    • main(String args[]) : This class is the entrypoint of the simulation, which is initiated by the main method. The four simulation parameters are read from the command-line in the following order: number of eaters, number of cooks, number of tables, machine capacity. All must be greater than zero (else an exception is thrown, likewise if there are insufficient arguments).
While the simulation runs, it will generate events, which are instances of the class SimulationEvent. Each event should be printed immediately, via its toString()method, and logged for later validation by the Validate.validateSimulation method.
When the simulation starts, it will generate a SimulationEvent via a call to SimulationEvent.startSimulation(). The simulation consists of the given number of eaters, cooks, and tables. It also involves exactly three machines, each with the given capacity. One machine makes burgers, one makes fries, and one makes cokes. The machine name is "Grill" for the burger machine, "Frier" for the machine that makes fries, and "Soda Fountain" for the machine that makes cokes. Each Eater places the same order: one burger, one fries, one coke. Once all Eaters have completed, the simulation terminates, shutting down the machines, and calling interrupton each of the cooks (telling them they can go home). The last thing the simulation will do is generate the event SimulationEvent.endSimulation().
  • Food
    This class is provided for you. It represents a food item. You will create at least three food items for your simulation ---hamburger, fries, and coke, as described above--- but your classes should treat food items generically; i.e., you should be able to easily change just the Simulation class if you want the simulation to have Eaters order different food items or different amounts, without changing any other class.
  • Eater implements Runnable (should run as its own thread)
    • constructor Eater(String name, List<Food> order) : takes the name of the eater and the food list it wishes to order; the format of the name is described below. You may extend this constructor with other parameters if you would find it useful. Each eater's order must be given a unique order number, which is used in generating relevant simulation events; it is efficacious to generate this number in the constructor. All eater names should be of the form "Eater "+num where num is between 0 and the number of eaters minus one.
    • run() : attempts to enter the restaurant, places order, waits for order, eats order, leaves. Eaters will generate the following events.
Before entering the restaurant: SimulationEvent.eaterStarting()

After entering the restaurant: SimulationEvent.eaterEnteredRestaurant()

After placing order (but before it has been filled): SimulationEvent.eaterPlacedOrder()

After receiving order: SimulationEvent.eaterReceivedOrder()

Just before leaving the restaurant: SimulationEvent.eaterLeavingRestaurant()
  • Cook implements Runnable (should run as its own thread)
    • constructor Cook(String name) : the name is the name of the cook (described below). You may extend this constructor with other parameters if you wish. All cook names should be of the form "Cook "+num where num is between 0 and the number of cooks minus one.
    • run() : waits for orders from the restaurant, processes the order by submitting each food item to an appropriate machine. Once all machines have produced the desired food, the order is complete, and the Eater is notified. The cook can then go to process the next order. If during its execution the cook is interrupted (i.e., some other thread calls the interrupt() method on it, which could raise InterruptedException if the cook is blocking), then it terminates. Cooks will print the following messages:
At startup: SimulationEvent.cookStarting()

Upon starting an order: SimulationEvent.cookReceivedOrder()

Upon submitted request to food machine: SimulationEvent.cookStartedFood()

Upon receiving a completed food item: SimulationEvent.cookFinishedFood()

Upon completing an order: SimulationEvent.cookCompletedOrder()

Just before terminating: SimulationEvent.cookEnding()
  • Machine
    • constructor Machine(String name, Food food, int capacity) : the name of the machine, the Food it makes, and the capacity (how many Food items may be in process at once). The names of the machines are specified in the description of the Simulation class, above.
    • makeFood() : This method is called by a Cook in order to make the Machine's food item. You can extend this method however you like, e.g., you can have it take extra parameters or return something other than void. It should block if the machine is currently at full capacity. If not, the method should return, so the Cook making the call can proceed. You will need to implement some means to notify the calling Cook when the food item is finished. While a machine is making food items, it will generate the following events:
At startup: SimulationEvent.machineStarting()

When beginning to make a food item: SimulationEvent.machineCookingFood()

When done making a food item: SimulationEvent.machineDoneFood()

When shut down, at the end of the simulation: SimulationEvent.machineEnding()
  • Hint: you will need some way for your Machine to cook food items in parallel, up to the capacity, but ensure that each item takes the required time. You might do this by having a Machine use threads internally to perform the "work" of cooking the Food (in this case, the only thing that will actually be done is waiting the proper amount of time). This approach will require some way of communicating a request by a Cook to make Food to an internal thread, and a way to communicate back to that Cook that the Food is done.

Getting Started
You will follow the similar procedure as in Project0 and Project1. You will obtain the skeleton code through Bitbucket. To fork the project, you will go to

https://bitbucket.org/cmsc433_spring2012/project2/

After downloading the source, familiarize yourself with the above requirements while looking at the skeleton code, and the javadocs found here in API Specification. You will need to implement additional methods and/or classes for your simulation; this is just the starting point.

Testing Your Code
We have allowed a great deal of freedom in the design of your classes for this project. Because of this freedom, constructing generic unit-level or system-level tests poses a problem. In particular, we've allowed you to modify the constructors of many objects (such as Machine and Eater), and unit tests would not be repeatable on modified objects. Therefore, you are responsible for your own tests for this project. We will test your code with our own validator, which will check many additional simulation characteristics.
In order to test your code with our own validator, we have asked you to place all simulation code (formerly in Simulation.main()) into a new function Simulation.runSimulation(). The new function returns a List of SimulationEvent objects which we can then pass directly to any Validate.validateSimulation() function.

Submit your solution

Follow the same steps described for Project 0 when you’re ready to submit your solution. Note that you may submit your code changes any number of times before the project deadline. We will only grade on the last submission.

You are also required to submit a short "design document" describing the design choices you made in your simulation. Argue how your design satisfies the validation criteria, particularly in its use of synchronization. Feel free to draw pictures to better make your points. Due to the non-deterministic nature of multi-threaded code, a significant portion of your project grade will come from manual inspection of your code, AS GUIDED BY THIS ACCOMPANYING DOCUMENT. Therefore, it is in your best interest to provide a sufficient amount of detail in order to reduce the chance for misinterpretation. You should submit your document along with your code to bitbucket. Follow the instructions on Piazza forum.


For this project, due to the freedom we've given you with design, your design document should also include a comprehensive test plan. The test plan should describe how you tested each piece of functionality in your code. You may also include any tests that you've written and instructions on how to run these tests.

Questions

Any questions you have, you should post on the Piazza forum. If you’re unsure if a question is allowed public audience, use the forum’s capabilities of sending private posts to the instructors.

We will examine your question, and if we decide it’s a general interest question, we’ll change its status to public.

You’re encouraged to discuss the project with each other, and answer your classmates’ questions, but remember you must not share any part of your solution code online.

Good luck!