Beginning the Current Implementation:
With the basic top down design implemented in a the simple simulator, I now needed to start to separate code into various source and header files. I wanted this code to be separated to limit compile time and to provide a way for future students to abstract from my data structures without including an entire file. I continued with the idea of a simulated where key strokes represented events of photocells, but began to separate my code into various classes.
I began implementation using a bottom up design. With this idea, I designed the train class first. I constructed a train class to have private data members and functions to control speed, direction, and lights. Since all data members were private, I also wrote many assessor functions to return the private variables. Next, I wrote a TrainList class that was a linked list of trains. I also wrote an ActiveTrainList and a BlockedTrainList class that inherited the TrainList class an overrode the virtual add and delete function of the TrainList class.
I thought that I needed to write these two separate train lists because the active list would need to update the photocell and turnout manager arrays in the resource manager. Therefore, the ActiveTrainList, I thought, needed to be a friend of the ResourceManager class in order to have access to its private data structures. The necessity for two different classes of train lists disappeared when they both became members of the resource manager. This paper will discuss later in the paper.
At this point, I continued to write the ResourceManager class and the TrackList class. The ResourceManager class contains data structures called the photocell manager and turnout manager. These two data arrays of integers keep track of which trains own specific photocells and turnouts. These arrays are of MAXTURNOUTS, AND MAXPHOTOCELLS size, which on the current track are sixteen and six respectively. (Note that there are only five physical turnouts, but I treat photocell six as a logical turnout. This will be addressed later in this paper.) The TrackList class is actually a linked list. A single instance of a TrackList class is of no importance to the track since a it only tells what resources are related to a single photocell. The TrackList data type becomes a useful graph of the actual track layout when I make an array of TrackList classes. When I made two arrays of size MAXPHOTOCELLS of TrackLists, I could describe the entire track in a graph. One of the arrays of TrackLists was called CC while the other array of TrackLists was called CW. These array structures give the relationships of photocells and turnouts in a counterclockwise and clockwise train direction respectively.
These classes were tested, individually at first, to ensure that they worked as implemented. Finally, I put together a main program in console mode to test the classes that I had designed. This main program acted as a simulator for the operating system and can be referenced on my web site. These classes make up the kernel of the operating system. This simulator was event driven, but rather than receiving events from photocells, it received events from the keyboard signifying a photocell being covered.
With some minor errors this project worked well. Through experimentation with this system I discovered that photocell six could not be used as I intended. Since I did not want to switch the direction of the train at photocell six the function described by the array of TrackList ceased to be a function. (Note that because we wanted to save switching the direction of a train for the situation an actual short is created in the track and a train’s outside wheel becomes its inside wheel). Going clockwise around the track hitting photocell six wanted two different photocells, five or twelve. I solved this problem by ignoring photocell six and allocating the sections of track between photocells eleven and five and also four and twelve respectively. During this experimentation, I also discovered that I was not checking for a nonexisted turnout in the allocation of the resources in the ResourceManager. I discovered this when the allocation for photocell fifteen would be updated to zero without the train that owned that photocell moving. This occurred because I was putting a zero in the turnout manager in the –1 position and overwriting memory in the photocell manager. Fixing these two minor errors took some tracing of code, but were relatively easy to fix.
During this time I also had some trouble including certain header files. For reasons that Dr. McVey nor I can explain we were having trouble including ActiveTrainList class into the ResourceManager class. We would get odd compilation errors that informed us that ActiveTrainList was undefined and that we could not declare a zero byte array for the photocell manager. I believe that somewhere there may be wrong includes, but neither Dr. McVey nor I could find the error. I was also concerned because I was unsure where to define the train lists and the tracklists. I did not want the user interface to have to declare these variables an send them to each of the managers. This problem was solved when they were all include as members of the resource manager.
Next, I decided that it was time to move the simulator over to the actual train system. I began to write a photocell manager class that would keep track of when photocells were covered and then uncovered. At first I wanted this class to catch the event and display a message on the screen stating an arrival or a departure. However, I ran into problems during this stage, because there was a misunderstanding of which library to include to use the ccr.dll function. Both Dr. Pankratz and I thought that we needed to include the ccr.lib and ccr.dll to use the turnout and photocell functions. However the ccr.lib was out of date. The actual library that needed to be included was dp.lib. This library pointed to the Get_PC_Status() function located in the ccr.dll that was needed to poll the status of a photocell. (The function returns 1 if the photocell is covered and 0 if the photocell is not covered.) The ccr.lib did not point to this function so the linker gave an error that the function was not defined.
When we found the correct library the photocell manager could be written. We originally had two minor problems. First, the photocells were set at the wrong sensitivities and this caused the photocell to turn on and off automatically. This created a problem with checking for arrivals and departures because the computer would show an arrival or departure when there was no event. The second problem was that I was updating one photocell at a time in a loop. The algorithm to handle an arrival and a departure needed two arrays. One that kept track of the current state of the photocells and one that kept track of the previous state of the photocells. When these two arrays differ we need to handle an arrival or a departure. If the previous state of a photocell was uncovered and covered in the current state we needed to handle an arrival. However, if the previous state showed that the photocell was covered and the current state showed uncovered we needed to handle a departure. To be surer that the code would update the current state of all photocells, I wrote a function that read in the current state of all photocells into the CurrentPCStatus array inside the photocell manager.
During the writing of the PhotocellHandler class I realized that that many of the classes written should be declared inside of the ResourceManager. The TrackList and both TrainLists became data members of the ResourceManager. Also the PhotocellManager class was no longer needed since the resource manager was able to capture the timer events and poll the photocells. The kernel of the operating system lied wholly in the ResourceManager. It made sense that all of these data structures were part of this class and made the coding much simpler. ActiveTrainList and BlockedTrainList classes disappeared and both the ActiveTrainList and BlockedTrainList variables were declared as TrainLists. This could occur because now both members had access to the private variables inside of the ResourceManager class and the ActiveTrainList could directly add to the PhotocellManager array when a train was added to the track because the array photocellmanger was a member of the ResourceManager Class.
Since I was still unable to run any tracks around the track because the dcc.dll was not created yet, I began to test the system by holding my hand over the top of photocells to signify a trains movement. For testing sake, I actually showed the ResourceManager window with the contents of the array photocellmanager and turnoutmng on the screen. By doing this, I was able to watch the trains move around the track by watching the resources that they had be updated. Also at this point, I separated the HandleArrival and HandleDeparture functions. Previously, both of these functions were part of the same function and was handled on the arrival of the photocell. I broke this up do to modularity, however, in the future one may find that it is actually easier and more efficient to handle the arrival and departure at the same time.
Most of major problems I encountered while debugging at this time were errors that took a long time to find, but were relatively easy to correct. First, in the HandleArrival function, I was attempting to update some of the values a train before checking to see if the train was NULL. Whenever a photocell was covered that did not have a train owning it the system would crash because I was pointing with NULL. I simply moved the statement to check if the train was NULL up before checking other data. Secondly, I had a small error in the ActiveToBlocked and BlockedToActive functions. When writing these functions I had forgot to return after moving the train. This created an infinite loop every time a train was blocked. The same error was in both modules. Furthermore, I had a problem traveling through the inner figure eight. I realized after some time that I had set up the TrackList function to use a "fake" turnout six where photocell six is located. I had given the TrackList instructions that would give the next photocell given that this turnout was straight or curved. Since there was no turnout, I figured that this would return either one of these outputs. However, if one polls a turnout that does not exist, the GetTOStatus function will return zero as if the turnout is moving. If one looks at the Load function in the ResourceManager one will notice that turnout six is always associated with a moving turnout. This allowed the function to work.
With the correction of these errors, the system was nearly complete. Next, I needed to find a way to actually move a train from the BlockedList back to the ActiveList. The original plan was to keep track of both the blocking photocell and/or the blocking turnout. This idea was much more complicated than I originally hoped because a turnout alone can not wake up a train. I decided to only keep track of the photocell that the train could not be allocated that blocked it. To do this, I also changed my original plan to stop the train under INTERACTIVE mode if the turnout was toggled in such a way that travel was impossible. It made it much easier just to toggle the turnout for the user and allocate the resources if possible. In this way, I did not think that a turnout could block a train. Later, I discovered that this was not necessarily true. If we remain with the logic of an invisible turnout six a turnout may block a train to prevent a collision at the center of the figure eight. Right now when this happens the train remains blocked and cannot be moved back to the activelist because it does not know when turnout six is deallocated. We may need to keep track of turnouts, or just this invisible turnout to activate and deactivate trains in the figure eight track.
I wrote a function that was called when resources were deallocated to check if any trains were blocked by the photocell that was returned to the system. This function loops through BlockedList and checks to see if the photocell released if equal to any of the blocked trains Blocking photocell. If it finds a match that train is moved to the active list and I fake an arrival and a departure (unless it is still covering the photocell) and if the train can be allocated the next resources it executes its last speed command and will start moving. If it cannot be allocated its next resources it is blocked again.