You will write a simulation for a coffee shop. The simulation has five parameters: the number of customers (diners) that wish to enter the coffee shop; the number of tables in the coffee shop (note: there can only be as many customers inside the coffee shop as there are tables at any one time - other customers must wait for a free table before going into the shop and placing their order); the number of cooks in the kitchen that fill orders; the capacity of machines in the kitchen used for producing food; and a flag as to whether everyone has the same order or not.
Customers in the coffee shop place their orders (a list of food items) when they enter the coffee shop. 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.
Our particular coffee shop will only have three food items, each made by a single machine: a burger, made by machine Grill, which takes 500 ms to make; fries, made by machine Fryer, which take 350ms to make; and a coffee, made by machine CoffeeMaker2000, which takes 100 ms to make.
Much of the design of the simulation will be up to you. You will need to provide a design statement and progress report as a PDF, uploaded to the submit server directly as a PDF (not via Eclipse). We do, of course, have some specific requirements.
First, you must not use concurrent collections in any part of your simulation. You can and will use things like synchronized or ReentrantLock or Future or calls to wait, notify, and notifyAll or Semaphore or CountdownLatch objects. You cannot use things like BlockingQueues, etc.
Second, you must use the following classes and the methods specified for them (most of which is what you'll be writing). All of these are available in the skeleton code. You may implement any other methods you need for these classes. You can also implement additional private classes or new helper public classes as needed (just make sure they are in the same folder/package as the rest of the project files).
* The starred classes are provided for you, and must not be changed; the remaining classes can be changed within the bounds given below.
main(String args[]) : This class is the entrypoint of the simulation, which is initiated by the main method. If we need to hand-test some things, the simulation parameters will be read from the command-line in the order shown in the code that is commented out. All of the values must be greater than zero (or else an exception is thrown). For your own testing we've commented out the top of the main method where it pulls things from the command line and hard-coded values for you to start your testing. Before you submit, please change it so that it can be run from the command line in case we need to.
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. We've given you an events list to use to hold these events.
When the simulation starts, it will generate a SimulationEvent via a call to SimulationEvent.startSimulation(). The simulation consists of the given number of customers, cooks, and tables. It also involves exactly three machines, each with the given capacity. One machine makes burgers, one makes fries, and one makes coffees. The machine name is "Grill" for the burger machine, "Fryer" for the machine that makes fries, and "CoffeeMaker2000" for the machine that makes coffee. For this project, there will be two scenarios for orders; (1) if the randomOrders is set to false, then each Customer places the same order: one burger, two fries, one coffee but (2) if the randomOrders is set to true, then each customer will place an order with a random number of burgers, fries, and coffee where each random number will be between 0 and 3. These items will be in a list sent into the Customer constructor. Once all Customers have completed, the simulation terminates, shutting down the machines, and calling interrupt on each of the cooks telling them they can go home (so each of the Runnable Cook objects will be running in a thread and you can call .interrupt on that thread - you should set up the try/catch block in the Cook's run method so that it will catch the InterruptedException and invoke Simulation.logEvent(SimulationEvent.cookEnding(this)) as a result). The last thing the simulation itself will do is generate the event SimulationEvent.endSimulation().
Food
This class is provided for you. It represents a
food item. You will create only three food items for your simulation
(hamburger, fries, and coffee, 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 Customers order different food items or
different amounts, without changing any other class.
Customer implements Runnable (needs to run as its own
thread)
constructor Customer(String name, List<Food> order) : takes the name of the customer 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 customer's order must be given a unique order number, which is used in generating relevant simulation events; it is easiest to generate this number in the constructor. All customer names should be of the form "Customer "+num where num is between 0 and the number of customers minus one.
run() : attempts to enter the coffee shop, places an order, waits for their order, eats the order, leaves. Customers will generate the following events:
Before entering the coffee shop:
SimulationEvent.customerStarting()
After entering the coffee shop:
SimulationEvent.customerEnteredCoffeeShop()
After placing order (but before it has been filled):
SimulationEvent.customerPlacedOrder()
After receiving order: SimulationEvent.customerReceivedOrder()
Just before about to leave the
coffee shop: SimulationEvent.customerLeavingCoffeeShop()
Cook implements Runnable (needs to 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 coffee shop, 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 Customer 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. Note that if the cook needs more than one item from a machine for an order, the cook can place all of those requests to the machine one after the other without waiting for the previous request to be filled. This means that a machine could (for example) be making two orders of fries at the same time for the same order. Cooks will generate the following events:
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()
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. 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. In this simulation, the only thing that will actually be done during the "cooking" time is waiting the proper amount of time so you could use something like a FutureEvent on a Runnable and know which Food item it will eventually return.
First, familiarize yourself with the above requirements in general. Next, check out the project starter files from your CVS repository. After getting the source code, review the requirements while looking at the skeleton code. You will be writing helper methods, etc. so the skeleton files are just a starting point.
By 23:59:59 on Thursday, March 28th you will need to submit a document explaining the strategies you are using to approach the project goals. Discuss where you are planning on making use of things like wait and notify and Future tasks. Your plans might change as you continue to work on the project, but by March 28th you need to be far along in your design stage (and hopefully well enough into your implementation) to be able to discuss your plans and report on where things stand in terms of progress towards completion of the simulation part of the project. This statement should reflect how your design satisfies the validation criteria, particularly in its use of synchronization and ability to have concurrent activities. Remember, there is also a verification part to the project. You do not need to discuss that part of the poject in this document. The document must be submitted as a PDF (if you don't have a virtual PDF printer on your Windows machine, CutePDF is available for free online - OS X has PDF printing built in).
We have allowed a fair amount of freedom in the design of your classes for this project. Because of this freedom, and the nature of the submit server, constructing generic unit-level tests poses a problem. In particular, we've allowed you to modify the constructors of many objects (such as Machine and Customer), and unit tests would not work on modified objects since the pre-written tests wouldn't know things like what to pass into the constructor. Therefore, you might have to take on a fair bit more responsibility for your own tests as you work on this project. We've set up some preliminary public tests that will give you a sense of whether you are on the right track, but once the project deadline has passed and we're ready to do full grading, we will test your code with our own validators, and possibly with manual command line testing, to check a variety of simulation characteristics. If you honestly pass the public tests (that is, you didn't work out some way to trick our auto-validator) then you'll receive up to 40 points for this public tests (depending upon how many of them you passed). The post-due-date testing will include performing multiple test runs on the public scenarios as well as other scenarios to check that none of them fail (where with the way the submit server works, if a public test passes on some run, it will count for public test points).
In order to test your code with our own validator, we have asked you to place all simulation code into a method Simulation.runSimulation() which returns a List of SimulationEvent objects which we can then pass directly to things like the Validate.validateSimulation() method.
The tests labelled as public tests on the submit server will test some scenarios with different numbers of cooks, customers, etc. As mentioned above, a significant portion of the grading for this project will be done after your final version is submitted via "hand grading" of certain things due to the concurrent nature of the project.
Every file you submit should have your name and UID. To enforce academic integrity, Code will be checked for similarity to other submissions. Each student should make his or her own individual submission. Use Eclipse to submit your project.
Due to the non-deterministic nature of multi-threaded code, a portion of your project grade will come from manual inspection of your code, and since there are many approaches, our understanding will be guided by your description. Therefore, it is in your best interest to provide a sufficient amount of detail in your comments order to reduce the chance for misinterpretation.