Download the starter files for this project here
In this and subsequent assignments, we are going to investigate the simulation of a shipping business. Our simulation is going to simplify this domain significantly and focus only on a small aspect of the business. Nonetheless, this simulation will help us practice the fundamental techniques of the modeling and simulation approach to development.
The shipping business is a service that transports packages from one location to another. Your task is to design and implement a class library that enables a client program to specify a configuration and query the state of a shipping network. The configuration is specified in terms of shipping entities, such as locations or connections, and the relationships between them. While designing your library, keep in mind that in later assignments you will have to extend it to simulate shipping behavior (i.e., shipments traveling from location to location). You also need to create a sample client program that instantiates some connections and locations and connects them together.
A shipping network may include any number of the shipping entities, where a shipping entity is either a location or a segment.
Your library should support the following query types:
We want the shipping system structured in three layers. The top layer is the client program that sets up and queries a shipping network. We provide a very simple client program to get you going. You also need to write your own in order to test your library for more complicated configurations and queries.
The client program can instantiate shipping entities and access or change their attributes, but it only knows of each entity by a string name. The operations requested by the client program are handled by a middle layer. The interface exported by the middle layer to the client program is a string-valued service (this interface was chosen for greatest flexibility and support for a wide range of user interfaces). We provide the interface for this middle layer, and you are responsible for the implementation.
The operations requested by the middle layer are handled by the lowest layer, which we refer to as the "engine." It is this lowest layer that keeps all necessary state; e.g., for each port created by the client program, there must be an engine object that stores the port's name, the segments that list it as a source, and so on. The interface exported by the engine layer to the middle layer is private to your library, so you are free to design it any way you want, as long as you adhere to the principles taught in class. You are responsible for both the interface and implementation of the engine layer.
We refer to the middle layer as the "rep" layer because it represents the client requests to the engine and represents the engine state to the client.
The client program does not directly create or manage engine objects. Instead, a client must create and manage Instance objects provided by the rep layer. Instance objects handle the correlation between the string-based interface and the engine objects. More specifically, to create a new shipping entity, the client program creates a new Instance object using strings to specify the desired name and entity type. For example, to create a new truck terminal, the client would use the command: instanceNew("myTruckTerminal", "Truck terminal"). To query or configure an existing entity, the client program retrieves the corresponding Instance object and reads or writes its attributes through the string-based interface.
The allowed string values for communication between the client and rep layers are categorized as either entity types or attributes. They fall into several categories:
where seg0 is a segment whose source is loc0, len0 is its length, seg0′ is its return segment (whose source is loc1) and so on.loc0(seg0:len0:seg0′) loc1(seg1:len1:seg1′) …locN
To create a new Instance object, the client program passes the Instance's absolute name and type to Instance::Manager::instanceNew(). To get a pointer to an existing Instance object, the client program passes the Instance's absolute name to Instance::Manager::instance(). Finally, to read or write an attribute, the client program uses Instance::attribute() and Instance::attributeIs(). Some attributes accept a limited set of values. It is up to you to see that these constraints are maintained. Invalid writes can simply be discarded with an error message printed to the standard error (stderr or cerr).
In general, if the client program requests an incorrect operation, you should ignore the request and print an error to stderr. A client operation is incorrect in the following cases:
Instances of type "Stats", "Conn" or "Fleet" are special in the following ways:
You may assume that user-defined absolute names will be at most 50 characters long.
For each numeric value, output the result as a string with no linebreaks or whitespace. For instance,
if there are 10 Truck segments in the shipping network, a stats query for "Truck segment"
should result in the output
"10"
Notice that there is neither leading nor trailing whitespaces. Integer-only numbers (such as a capacity attribute) should be printed without a decimal, but floating-point types (such as MPH) should always be printed with 2 decimal digits.
Output each path on its own line and do not output extra lines in between two
paths or after the whole set of paths. For each path, include only 1 whitespace between the end of a segment description and the next
location's name. For example, if you have a path from loc0 to loc3:
"loc0(seg0:len0:seg0')^loc1(seg1:len1:seg1')^loc2(seg2:len2:seg2')^loc3"
where ^ is a single space. As in the statistics reporting format, there is no leading or trailing whitespace
(except for the newline character '\n' between paths). Note that, as a floating-point value, the segment lengths should be printed to 2 decimals.
You are expected to read and apply the material of Chapters 1 through 4 to this assignment. Use the attribute-based approach described in Chapter 2.
We are providing a client program which should aid you in testing your library. We encourage you to exercise any specified library services that our client program does not invoke (by modifying the test client or replacing it with your own client program), as they will be exercised during the grading of this assignment.
Try to model your code as a shared library. This means that any test programs you write must use the shippingInstanceManager() entry point to get a handle on the Shipping::Manager object, and after that must only use the interface specified in the Instance.h header file we provided. The code links as a static executable because this will be much easier for you to debug, but our goal is to design code that could be loaded as a shared library.
This assignment is not meant to test error handling, so you may handle unanticipated errors (e.g., running out of memory) by any reasonable means you choose. One good way is to use exceptions — but we have not covered them yet in class. An alternative is to print an error message to stderr and attempt to continue on. Please indicate the method you use for handling unanticipated errors in your README file.
Another important issue not tested in this assignment is memory management. Although not covered yet in class, you are strongly encouraged to use the smart pointer reference counting technique from Chapter 5 of your course reader. We provided you with the relevant source code in Assignment 2 and will give plenty of examples, so don't worry — smart pointers are easy to use and will not get in the way.
You will be required to come up with a description of the interface you will implement between the rep and engine layers. You need to provide a list of classes, their attributes, and the public methods exposed to the rep layer by the engine. None of those methods need to be implemented at this point, we are only concerned with the design of the interface for the milestone. This is due Friday, October 23, but we encourage you to finish it and submit it to us as soon as you can so that we can provide feedback and you can start the implementation as soon as possible. Please type this up and attach it in an email to the course staff list (cs249a-aut0910-staff@lists.stanford.edu). This interface does not need to be perfect, and you may modify it after you turn it in, we just want to make sure you're heading in the right direction.
Submit the assignment using the /usr/class/cs249a/bin/submit program. Your submission must include the following:
The source bundle for this assignment includes the Instance.h header file, which is the immutable contract between the client code and the rep layer, and also some sample code in Instance.cpp, which is intended as a starting point for you, and which you will need to change!
You many re-submit as many times as you like. If you re-submit a file, the new version will overwrite the old one.
class Mile : public Nominal<Mile, unsigned int> {
public:
Mile(unsigned int num) : Nominal<Mile, unsigned int>(num) {
//can do additional checks on bounds here
}
};
...
class Segment {
private:
Mile distance_;
public:
//initialization of distance_ is required.
Segment() : distance_(1) {
//Segment constructor stuff...
}
};
seg4->attributeIs("location", "loc3");
then the segment4 attribute of loc3 will return seg4. If the location attribute of a segment is set to null, this has the effect of also removing the segment from the location, ie:
seg3->attributeIs("location", "");
The location should "shift-down" the segment numbers to fill in the gaps. In this case,
loc3->attribute("segment3");
now will return seg4.