First Group | Group #5 |
---|---|
Second Group | Group #6 |
View The Original DocumentationDesign
When the application is initialized, it creates the singleton objects: WorldContext (the database that holds the objects of the world), the WorldBuilder, and various WorldContext Visitors (ex. UpdateVisitor).
The input file was supposed to be an XML description of a city (q.v. the DTD and an example) and was supposed to be parsed into a DOM Tree by a class wrapped around Xerces-C++. (The DOM Tree is traversed by another subsystem that creates objects representing each of the entities described in it.) However, this group admitted that they were unable to implement this XML reader and hard-coded a small demonstration world instead.
WorldBuilder is the class that specializes in building worlds. The WorldBuilder uses an ObjectFactory, a creational pattern for building world objects. WorldBuilder can be sub-classed and thus provides a way for building specialized worlds.
The application constructs Intersections, Lanes, Lights, and Vehicles objects. These objects are stored in a WorldContext, which acts as an object database. WorldContext limits the access to its contents to a few classes (i.e. Visitor and its derivations).
WorldContext contains the World's objects, which can only be accessed by the Visitor class and its derivations. One such derivation is UpdateVisitor. This specialized Visitor has the sole purpose of iterating through the WorldContext and sending each object an update message. By sending the UpdateVisitor through the WorldContext after each time step, the application is able to create a dynamic city environment
At each time slice, immediately following UpdateVisitor's pass through WorldContext, a second Visitor is sent through WorldContext. This visitor, OutputVisitor, is responsible for collecting information from each Vehicle about its orientation and acceleration. This information is printed to a file that can eventually be used by the trafsim GUI.
Update() is a method inherited by all Object-derived classes. Update causes the Object to change itself; for example, a Vehicle will change its acceleration, or a Light will change from AMBER to RED. The WorldContext changes accordingly, as Objects are modified to reflect their state in the new timeslice.
Updating Objects like Lights is an easy task. If a certain amount of time has elapased, then advance to the next Light state. However, Updating Objects like Vehicles is a much more daunting task because it requires some artifical intelligence.
A Vehicle consists of a Driver. A Driver has a DriverStrategy that guides the actions the Driver takes. Currently, a driver's Action is either to accelerate or decelerate.
When a Driver's update message is invoked, the Driver creates a Surveyor with the Driver's field of view. The WorldContext then accepts the Surveryor. The WorldContext passes each of its Objects to the Surveyor, which in turn determines if the Object is in the field of view. This process creates a Driver's context - the list of Objects with which it bases all its decisions.
With its context known, the DriverStrategy then finds the Lane it is in, finds the closest car in front of its Driver, finds the Light and then determines if it should accelerate or slow down according.
Code Fixes
The code did not compile properly and had to be fixed. The problem was in the Makefile and was corrected by changing the CS471_ROOT path from "/gaul/s1/student/1996/spollma/CS471GROUP/Project" to the relative "." to work in the local directory.
This problem can be avoided by NOT hard coding an absolute path name inside the Makefile and instead relying on a relative path.
Approximately 10 minutes was spent on identifying and remedying all encountered problems. A complete list of changes describing the total differences between the original source code and the modified source code, using the UNIX command "diff -bitw -l -r . ~wade/Courses/cs471/Trafsim/2000-Groups/G05 > ../diffG05.txt" can be found here.
Extension Issues
The most serious flaw in this group's code is the inability to parse the input XML description file and use that information to generate a world. Naturally, before this group's code could be used for our project, we'd have to fix this problem (or use working code from another group).
To extend the code, a builder pattern could be implemented within the OutputVisitor class to allow for different types of output formats, so that in the future, if the file format requirements of trafsim (or another program) change, then the output too can be modified easily.
I would use this code as a base for our own project as the design makes is quite extensible. That is, it would make a good base for future projects (after this year's) to build on as it provides good flexibility on both the types of worlds it is able to contain and the types of objects in theses worlds.
Design
The application's input uses the Adapter pattern. By having a Parser Adapter class and subclassing it to an Input Parser class, the code gives the ability to change from an XML Parser to some other type of parser by simply sub-classing Parser Adapter again, thus making it easier to change to another input format.
The application's output uses the Builder pattern. If the output format changed from the current TrafSim format to another format then subclassing the Output Builder class could to the handle it. This would enable the application to inherit all simple operations implemented in Output Builder. Any new functionality that is needed can be obtained by overriding the default virtual function.
The system is made up of an abstract City class that contains a concrete City class (OurCity). An abstract City class allows for the definition of common operations of a city. Specific differences between a common city and a concrete city itself can be defined by subclasses. An example why this distinction is useful is that some European cities (e.g. London, England) require drivers to drive on the left side of the road, while North American cities (e.g. London, Ontario) require drivers to drive on the right side of the road.
The abstract City class keeps track of where everything (Intersections, Vehicles, Road Segments, Lanes) is located in the city. This structure is maintained by making frequent calls to the update functions of each component of the City reflect any changes in the system since the last time slice (e.g. vehicles may change positions or Intersections, Road Segments, and Lanes can change if one of them closes). Since a concrete City has to traverse many lists to update information, traversals are used.
The application uses the Mediator pattern for the Intersection Controller, which handles the relationship between Traffic Lights and Vehicles. This pattern allows the interaction to be changed between Vehicles and the Traffic Lights without changing either of the two classes themselves. Though the project documentation implies there is a single Intersection Controller, a close inspection of the implementation suggests that there is in fact one Intersection controller for each intersection. The system uses Intersection Controllers as "local brains" to control traffic flow and send information to Vehicles (This could include telling the vehicle to speed up, slow down, stop etc.). The Intersection maintains its position in the City as part of its state, allowing it to determine where everything is. Each Intersection Controller contains one Intersection. Intersection Controllers are only responsible for vehicles that are coming into that Intersection. The next Intersection's Intersection Controller controls vehicles leaving the intersection. This strategy tries ensures that no one will crash by having all vehicles entering a common intersection controlled by a single source.
The Vehicle class itself is subclassed to allow for specialty vehicle types, making it easy to change one type of vehicle without changing the entire vehicle class. Vehicles are only responsible for collision avoidance. The vehicle will ignore an Intersection Controller's suggestion to speed up if for example a tree falls on the road and the vehicle must slow down to avoid collision.
An Intersection Element contains a Road Segment, a Connecting Road Segment (This represents the Road Segment that connects to the current Road Segment if the Vehicle were to go straight through the Intersection), and a list of Connecting Road Segments. A Road Segment is a connection to another Intersection. A Road Segment maintains its position in the Intersection, making it easy to expand the system to have vehicles turn because of the Road Segment knowing its position in the Intersection. Road Segments are made up of one of more Lanes. A Lane specifies the direction of travel on that Lane and contains information on what direction a Vehicle in that Lane may go (straight, turn left, turn right or go straight, etc.). This is allows for lanes to be defined to be able to have turning lanes, or lanes that forbid turning.
Code Fixes
Numerous fixes were necessary in order to get the application to compile. The vast majority of the fixes were casting functions or parameters to return a type. For example, the compile error message "Src/include/AbstractIntersectionController.h:23: ANSI C++ forbids declaration `setID' with no type" was usually the result of the line "virtual setID()" whereas the correct line of code would be "virtual void setID()".
Other code fixes of this type included type casting from plain C-style memory allocation / reallocation calls and properly casting variables for the "=" operator. For example the original code "parser = XML_ParserCreate(NULL);" was corrected to "parser = (XML_Parser *) XML_ParserCreate(NULL);"
A few of the errors were corrected by including the proper C library files in the header file as well.
These errors were all due to the weaker type-checking abilities of the old version of the compiler. The newer compiler, which detects these errors, would likely prevent them from getting into the code. In general, the best way to prevent these errors would be to properly define or cast types for functions and variables and to rely more on the C++ "new" command for memory allocation.
A total of 17 files were modified. (Download these and replace the original with these to get it to work):
AbstractIntersectionController.h
AbstractIntersectionController.cpp
City.h
City.cpp
IntersectionElement.cpp
IntersectionController.h
IntersectionController.cpp
OurCity.cpp
ParserAdapter.cpp
ParserAdapter.h
ntersectionElement.h
OurCity.h
xmlwf.c
xmlfile.c
unixfilemap.c
xmltok.c
xmlparse.c
It took about two hours and a half to go through and fix all the files that were modified. A complete list of changes describing the total differences between the original source code and the modified source code, using the UNIX command "diff -bitw -l -r . ~wade/Courses/cs471/Trafsim/2000-Groups/G06 > ../diffG06.txt" can be found here.
On a non-compiling but important note, the command "make clean" doesn't work, leaving an onscreen error message of "make: Fatal error in reader: Makefile, line 52: Unexpected end of line seen". When cutting and pasting the command they used for make clean, it didn't clean out everything either.
Extension Issues
The biggest limitation to this group's code base is that it relies on Intersection Controller's to determine the way cars drive. That is, the cars themselves have no control over their path and rely exclusively on the Controller's to tell them where to go and avoid a collision (only by stopping, no evasive maneuvers or speeding up) if the Controller's directions are wrong. The first thing to extend would be the vehicle (or driver's) AI and give the more free will. This will however go against the whole Intersection Controller paradigm.
This paradigm does, however, fit with a computer automated traffic controller vision or a railroad / subway system in which the conductor can only control the speed of the train and the tracks determine the direction. (In the latter case, curves the track would probably have to be implemented as an intersection with only two road segments connecting it, but I digress.) Another positive aspect of the group06 design is that every intersection will know what vehicles are approaching it so it would be easier to modify the code to allow a single intersection to make a decision to, for example, stay green longer and let all the cars pass.
The second thing to extend would to give the Vehicles the ability to do more than just go straight and stop. Turning (at degrees of 90) and changing lanes would be the next thing to extend.
Given the current framework of this group's code, I would say that this wouldn't be a good one to use as a code base for our project as the Intersection Controller paradigm severely restricts the worlds it can represent. However parts of it may be able to make its way into a project. The fact that it can parse an XML input file and it change which lane to drive easily by changing the lane-sorting algorithm is a plus.
The best aspect of group06 is easily the ability to parse an XML input file. The fact that Intersection Controllers are responsible for traffic decisions (as opposed to the drivers of the vehicles) probably won't be an issue for this year's project, but might be an issue if any future project needs to alter the driving behaviour of Vehicles (aggressive, drunk, etc.).
Both designs also force any vehicle to be on a single lane at any one time.
In terms of differences, group06's design relies Intersection Controller's to determine the direction of travel and decisions for each car while group05 has a DriverStrategy AI. The consequence for this is that group06's design can not be extended to simulate different types of driver's or attitudes while group05's design can. However, group06's design is a better platform for any future traffic worlds that require a central computer system to control all of the traffic in a city.
Group05's Object class features a Shape, which offers more flexibility over group06's TransportationArtifact class in that group05's object class, is capable of representing more varied type of objects. Group05's Object class also allows for the representation of 3D locations, which would be useful should future projects be represented in any sort of 3D world (e.g. one with bridges, ramps, etc.) This would be a good feature to include.
On a related note, a builder pattern might be more appropriate than an abstract factory pattern for group05's design. In order to construct the complicated objects they say that their application is designed to extend for, the algorithm for creating such objects should be independent of the parts that make up the object and allow for different representations of the object that's constructed. Objects can be already implemented to contain the necessary complicated interfaces needed to interact with future complicated objects (according to their documentation, they say it does). Thus, a builder pattern would probably be better as it would be more suited to handle the creation of different type of Objects such as people, trees, etc.
Group06 uses the builder pattern to create output files, which seems like a very sensible thing to do and should be done in this year's project.
In general, the two groups have taken two different approaches in designing the traffic simulator. Group05's design is better suited for simulating a real world environment while group06's design focuses on a world where the road controls the car. In general, the utility of either of the designs is dependent upon the application that it is required to fill. In most circumstances however, group05's design would probably be more applicable as it allows for individual drivers' decisions, a built in 3D location point and the capability to subclass different types of objects.