Master
The purpose of the master component is to start the simulation process and continue the simulation. In effect, the master is the “main” program. Like most main programs, it therefore creates instances of the other components. Since the master runs an “event” simulation with a clock, it has a loop to run the simulation and a queue of events.
Suggestions: there could be an Event class with each instance being an “event” and a priority queue from Java’s library can contain many Event objects. One member of each Event should be which “task” to do when that Event is dequeued (perhaps Task is an interface with a fire(arg) method). When E.fire(arg) is called, the fire method can be from any of the other components, Floor, Belt, Inventory, Orders, Visualizer, or Robot — probably most of these will need to implement the Task interface. Once fire(arg) is running, it can invoke Master.enqueue(newEvent) to add another (future) event to the Master’s queue — that implies that all the other components probably need to know about a Master object (perhaps as a parameter in their constructors). This can get confusing, so writing a picture of the interaction, perhaps using a Sequence diagram, is advisable.
The other thing Master will need is a “clock” which publishes “tick” continuously to all the other components (Floor, Belt, Inventory, etc). That means that all these other components will need to implement a Tickable interface so that Master can invoke O.tick(count) on each of them, where count is the clock value (which starts at zero).
If you think about it, the two concepts (queue of events and ticking clock) are redundant: we don’t need to have both. It’s a matter of what is convenient for the other components, which is more handy for programming. For instance, if a robot has a known path moving one unit per tick, maybe the tick idea is more convenient than having to create a future event for the next tick.
In unit testing the master, it will be a good idea to provide methods that record (to a file, or just by printing) how the simulation is going, what events are happening, which other components are being called. Also, for practical reasons, we can limit the number of iterations of the lock to some pre-defined limit rather than running forever. Unit tests with mock objects can test whether queueing new events works, initializing the queue with some initial events, and of course creating instances of the other (mock) components.
Visualizer
The intuition of the visualizer is easy to understand, however it’s not so simple to think how one can start programming the visualizer without everything else (Floor, Belt, Robot) being already done. Also, there is the matter of learning enough about Java Swing to make a good display.
Suggestions: instead of beginning with some fancy display of the warehouse, you can start with just printing what is happening, at each “tick” of time. That implies that the Visualizer will need to call the other components (Floor, Belt, Robot, possibly Inventory, Orders) to get information on what is their current status. There should be some kind of interface that each of these other components implement to provide such status information. Also, when the Master launches Visualizer, it will need to provide object references to these other components so they can be called. For early versions and unit testing, use mock objects for the other components, which provide some crude information about the warehouse.
After the Visualizer can print basic status information in each “tick” of time, you might then improve it so that instead of just printing (with System.out.print) it shows the text on a window using Swing.
Another consideration for a Visualizer is the rate of visualization. Instead of displaying status for each tick, maybe Visualizer can sample (like one out of every five ticks) and display more slowly than the rate of the simulation. If there are any control widgets (buttons and such) in the Visualizer’s window(s), then you might have to deal with the tricky problem of stopping the entire simulation temporarily to change some tunable value: that would require some redesign of how Master and Visualizer interact.
Floor
An important task of the Floor part of the project is not programming, it is just layout of coordinates of the warehouse, location of areas reserved for shelves,location of belts, location of highways, location of receiving dock(s), location of shipping dock(s), location of picker station(s), location of packing station(s), location of chargers. This will require some way to represent information, preferably in a file (for unit testing it might be hardcoded for a really simple warehouse). Then Floor will need to provide a way to name the various entities (“Shelf area 2”, “Highway 5”, “Packing Station A” or something similar). Methods would be needed so that a graphic Visualizer can get enough information from Floor to display the warehouse, though most likely for this project we will have some simple agreement between Visualizer and Floor on how the warehouse is laid out.
There are also things that are not stationary: robots and shelves move. Should the floor keep track of the movable things? Indirectly, items move as well: items can be contained on docks, on shelves, and moving around on belts. Should the Floor need to maintain the location of all these movable things? The Belt part of the project should keep track of how bins and packages move, which can contain items: so the Floor doesn’t keep track of every kind of movement. Similarly, when robots move, the Robot part of the project should deal with that. Shelves only move by robots, so the Robot part of the project should also manage shelf location (at least while they are moving). Once a robot parks a shelf, the Floor can remember where the shelf is. Each Shelf object will need to be able to contain items, and will need to have a capacity (maximum number of items it can contain).
Another interesting problem that Floor can solve is calculating what is the path from one place to another – robot control will need that. Unit tests for the Floor can have mock objects for Robot that move shelves, ask Floor for a route, and respond to methods asking to look up coordinates for highways, shelves, docks and so on. Early development should have a very simple warehouse layout.
Inventory
The inventory component of the project has to keep track of items in the warehouse. In reality, the inventory subsystem would be connected to vendors and data centers with business logic which analyzes inventory, so that customers and management know what is in stock, volume of sales, and so on. Above, we saw that a big effort of Floor was layout. Similarly, a large initial task of Inventory is to get some simulated list of stock in a warehouse (items and quantities). There might be something found via search engines with names of possible items; simple sequential numbering can be then associated with items to give each an item number. It need not be very realistic, we just want things in a list to have enough information that Orders and Shelves can be simulated. A more advanced implementation would have item information like weight and size.
When Inventory is initialized, it should place some items on shelves throughout the warehouse: it will need to distribute items on Shelf objects which the Floor created when it initialized. When the simulation is running, inventory might also sometimes simulate the arrival of new items to the receiving dock. Also, we can let Inventory remove items from the shipping dock (which simulates a truck taking packages away). Once items are shipped, they are no longer in the active inventory. For unit testing, mock objects can add inventory items, increase or decrease quantity, change the “association” of an item (from dock to shelf, from shelf to bin, from bin to package, from package to dock, from stock to being in an order, and so on). The idea is that the Inventory component is a simulated database which tracks where all items are in a warehouse. But, Inventory does not track the infrastructure of the warehouse: where are shelves, where are bins, where are robots.
Orders
The simulation will depend on the arrival and fulfillment of customer orders. In reality, some data center would ask the Inventory part of the project if there are enough items to complete a proposed order, and then send that order to the warehouse. For unit testing and an example simulation, we just need some simulated orders. Just as the Floor needed a layout effort and the Inventory needed to build a list of items, the Orders componet will need to build a list of initial orders. This should be done carefully, in conjuction with Inventory, so that the initial orders don’t ask for items not in the warehouse. Then, as the Masterproceeds with the simulation (ticking or by dequeuing events), new orders arrive, orders get fulfilled, and orders get tracked as they make progress. There will need to be methods that generate and enqueue (for arrival later) new orders, and there will need to be methods that provide the status of an order. There should be “CustomerOrder” objects which contain items and desired quantities, an address, and some status variable to be updated as the order progresses in the warehouse.
Belt
The belt system has to transport bins and packages. As a belt moves a bin or package, it should update status information related to location of items, progress of an order, and whether the belt needs to move or not. The Belt component will have to be coordinated with Floor to know about coordinates in the warehouse. Even though a belt has a fixed path, it may still have to reckon with some traffic problems. In reality, a belt should not just dump packages on a dock if there is no room. And if there is a system with two belts feeding into third belt, the Belt component will need to control that things are only moved when there is some place to put them. (Actually, a complicated Belt system is almost as challenging as moving robots.) For our simple simulation, we start with just a single belt from picker to packer to dock. This is enough for some basic unit tests. For purposes of the simulation we can assume that pickers have an infinite supply of bins (so the picker puts the bin on the belt and the packer removes the bin from the belt). Belts have sensors so they can sense in some detail which bin and package is at a particular place on a belt at any tick of the simulation. Stopping and starting a belt is a potentially interesting algorithm question.
Robot
Warehouse robots move shelves and sometimes rest and recharge. For the Amazon warehouse, it seems actual robots are rather simple, low-level machines with limited intelligence, so the planning and movement are controlled by a Robot Scheduler, which is the component describe here. The Robot Scheduler initializes by creating instances of Robot objects and placing them (in collaboration with Floor) to locations in the warehouse. Initially we suppose that each Robot is fully charged. When the Order component needs to move items to the picking station, the Robot Scheduler is asked to move a shelf containing the needed item(s) to the pickingstation. The Robot Scheduler can ask Inventory for the location of a shelf (hopefully it is not a shelf already being moved by a Robot, otherwise we need to think of a strategy for that). The Scheduler then has to find an available charged, idle Robot and plan a route to the Shelf (and the Scheduler will have to reserve that Shelf so that another Robot cannot take it instead). Then Floor can provide a route, and the Robot can move, tick by tick, to the Shelf location, taking another tick to lift the Shelf. The Floor can also provide another route from where the Shelf is to where the picking station is. After the desired item(s) are removed from the Shelf, the Scheduler needs to find an available place to move the Shelf and move it there.
For unit testing with one Robot object, mock objects can test the basic logic, which should be detailed, but straightforward. Unit testing can print the progress of a Robot from Order arrival to moving the shelf back to a resting point. Far more challenging is the case of two or more Robots moving in the warehouse. The Robot Scheduler will then potentially make a Robot wait in case of a traffic jam. Ultimately the way to do more complex testing is by scripting some scenario, which can be done by setting up multiple unit tests with mock objects (or, perhaps by the time multiple robots are tested, other parts of the warehouse will be good enough to use in testing).
UML Diagrams by Students for the Visualizer