''======================================================================== '' '' AUTHOR: P. Van Havere '' PROGRAM ID: TRAINCP.SIM '' DATE: March, 1988 '' PURPOSE: To experiment with travel times of freight and passenger '' trains. Trains can pass one another by entering sidings. '' Passenger trains have priority. '' '' MODEL: A series of trains depart and travel sequentially. '' A train must wait for the one ahead of it to clear a segment of '' track before it can enter. '' Two data files are input, one for the train schedule generation, '' the other for the track layout, i.e. names and speed limits of '' rail segments. '' The track layout is maintained internally as a doubly linked list '' of permanent entities. '' Pointers to elements of the list are used as location indicators '' for each train process. '' ASSUMPTIONS: '' 1. Passenger trains travel 15 mph greater that the designated '' speed limits. This is to simplify the model; if desired, '' a set of speed limits for various train types could be '' implemented. '' 2. Scheduled travel time includes extra time (called '' recovery time) to allow for unforseen delays. '' 3. The length of a train does not exceed the length of any '' track segment or siding. '' 4. There cannot be 2 or more contiguous blocks of track '' without sidings. '' 5. Only one train can occupy a segment of track. '' IMPLEMENTATION: Values for activation time and travel time of '' each train process are read in from a data file. '' The WORK time for each train depends on the '' type of train, the speed limits in track segments, '' and, whether or not a train has to go into siding(s). '' Elapsed time = (exit time) - (departure time) '' note - because the train process is external '' the departure time is derived from TIME.V '' '' BUGS: (Programmers' Axiom: a bug once documented becomes a feature.) '' 1. This is the author's first large Simscript program. While the '' code is workable, more experience with Simscript would have '' resulted in more compact code. '' 2. The user input for track segment number to be displayed assumes '' that the user will enter a number rather than an alphabetic. '' Incorrect entry produces a Simscript error but allows reentry. '' '' T R A I N P R O C E S S '' ------------------------- '' '' Note: the times in the data file created by the user are run '' run through a filter to produce the times expected by Simscript. '' The filter also inserts a train number (TRN.PTR) into each record. '' FIELD DESCRIPTION FORMAT '' '' Variables read in from data files: '' '' "train" process name must be first field 5 characters '' "hh.mm" scheduled departure real '' TRAVEL.TIME expected travel time real '' (including recovery time) '' TRN.TYPE 2. passenger integer '' 5. other '' Note: any number of types could be implemented. '' TRN.PTR position of train in data file integer '' (used with LOCATION to subscript the '' sched.stop array) '' TRN.ID train identification number integer '' TRN.LENGTH length of train in feet integer '' HORSEPWR horsepower per tonnage ratio real '' TRN.ORIGIN subdivision name of point of origin text '' DESTINATION subdivision name of destination text '' SCHED.STOP holds scheduled 'stop' times for each array '' track segment '' MARK.V end of record indicator expected * '' in Simscript external process records '' NUM.TRAINS maximum number of trains (counted by the global '' filter and output to a separate file.) integer '' '' Variables initialized and maintained within the program: '' '' DIRECTION set to 0 for eastbound, integer '' or 1 for westbound '' LOCATION pointer to the track segment to be integer '' entered next '' START.TIME derived from TIME.V real '' SEGMENT.WAIT time spent waiting to enter a segment real '' SEGMENT.TRAVEL.TIME travel time in one segment real '' SEGMENT.START time the train entered a segment real '' SEGMENT.END time the train go to the end of segment real '' SIDING.WAIT time the train waited in a siding real '' SIDING.TRAVEL time the train traveled in a siding real '' ACTUAL.TRAVEL.TIME travel time from origin to dest real '' ELAPSED.TIME total of segment travel and wait times real '' T R A C K S E G M E N T P E R M A N E N T E N T I T I E S '' --------------------------------------------------------------- '' '' Track segments are subscripted from 1 to N.TRACK.SEG from east '' to west. N.TRACK.SEG is read in from file. '' '' FIELD DESCRIPTION FORMAT '' '' Variables read in from a data file: '' SUBDIVISION name of subdivision text '' SPEED.LIM designated speed limit integer '' TRK.LENGTH length of track in miles real '' SIDING a flag to indicate whether a siding integer '' exists for the track segment '' (value is 1 for present, 0 otherwise) '' D.SIDING a flag to indicate whether a siding integer '' exists for the double track segment '' DOUBLE a flag to indicate double track integer '' (value is 1 for present, 0 otherwise) '' SLOW.ORDER pairs of slow orders (passenger-other) 3-D array '' '' Variables initialized and maintained within the program: '' TRACK.STATUS contains the train ID number of integer '' the train occupying a track '' SIDING.STATUS contains the train ID number of integer '' the train occupying a siding '' DOUBLE.STATUS contains the train ID number of integer '' the train occupying the eastbound '' set of tracks in double track '' D.SIDING.STATUS contains the train ID number of integer '' the train occupying an eastbound siding '' TRACK.ENTRY.PRIORITY contains the priority (trn.type) integer '' of train in track segment '' DOUBLE.ENTRY.PRIORITY contains the priority (trn.type) integer '' of train in eastbound double track segment '' '' Associated variables: '' QUEUE stores waiting trains and priorities 3-D array '' TEMP.QUEUE used in selecting next train 2-D array '' D.QUEUE for eastbound double track waiting 3-D array '' D.TEMP.QUEUE used in selecting eastbound double 2-D array '' MAX.Q.SIZE array limit for the preceeding integer '' MAX.SLOW.ORDERS array limit for slow orders array integer '' NEXT.TRAIN flag to indicate which waiting train 1-D array '' is to proceed (in each segment) '' D.NEXT.TRAIN as per NEXT.TRAIN but for eastbound d 1-D array '' MEET set when a train has to wait in a siding 1-D array '' for opposing train to get by; tripped '' when opposing train has cleared segment '' OVERTAKE similar to MEET, allows trains to pass 1-D array '' D.OVERTAKE for eastbound double track 1-D array ''======================================================================== PREAMBLE NORMALLY, MODE IS UNDEFINED '' P R O C E S S E S '' ----------------- PROCESSES INCLUDE STARTUP AND WINDUP EVERY TRAIN HAS A START.TIME, A TRAVEL.TIME, A TRN.TYPE, A TRN.PTR, A TRN.ID, A TRN.LENGTH, A HORSEPWR, A TRN.ORIGIN, A DESTINATION, A DIRECTION, AND A LOCATION EXTERNAL PROCESS IS TRAIN EXTERNAL PROCESS UNIT IS 7 DEFINE START.TIME, TRAVEL.TIME, AND HORSEPWR AS REAL VARIABLES DEFINE TRN.ORIGIN, AND DESTINATION AS TEXT VARIABLES DEFINE TRN.PTR, TRN.ID, TRN.TYPE, TRN.LENGTH, DIRECTION, AND LOCATION AS INTEGER VARIABLES '' P E R M A N E N T E N T I T I E S '' ---------------------------------- PERMANENT ENTITIES EVERY TRACK.SEG HAS A SUBDIVISION, A SPEED.LIM, A TRK.LENGTH, A TRACK.STATUS, A SIDING, A SIDING.STATUS, A DOUBLE, A D.SIDING, A DOUBLE.STATUS, A D.SIDING.STATUS, A TRACK.ENTRY.PRIORITY, AND A DOUBLE.ENTRY.PRIORITY AND BELONGS TO SOME SEGMENT DEFINE SPEED.LIM, TRACK.STATUS, SIDING, SIDING.STATUS, DOUBLE, D.SIDING, DOUBLE.STATUS, D.SIDING.STATUS, TRACK.ENTRY.PRIORITY, AND DOUBLE.ENTRY.PRIORITY AS INTEGER VARIABLES DEFINE TRK.LENGTH AS A REAL VARIABLE DEFINE SUBDIVISION AS A TEXT VARIABLE DEFINE SEGMENT AS A FIFO SET THE SYSTEM OWNS THE SEGMENT '' G L O B A L V A R I A B L E S '' ------------------------------- DEFINE QUEUE AS AN INTEGER, 3-DIMENSIONAL ARRAY DEFINE TEMP.QUEUE AS AN INTEGER, 2-DIMENSIONAL ARRAY DEFINE D.QUEUE AS AN INTEGER, 3-DIMENSIONAL ARRAY DEFINE D.TEMP.QUEUE AS AN INTEGER, 2-DIMENSIONAL ARRAY DEFINE NEXT.TRAIN AS AN INTEGER, 1-DIMENSIONAL ARRAY DEFINE D.NEXT.TRAIN AS AN INTEGER, 1-DIMENSIONAL ARRAY DEFINE MEET AS AN INTEGER, 1-DIMENSIONAL ARRAY DEFINE OVERTAKE AS AN INTEGER, 1-DIMENSIONAL ARRAY DEFINE D.OVERTAKE AS AN INTEGER, 1-DIMENSIONAL ARRAY DEFINE SLOW.ORDER AS A REAL, 3-DIMENSIONAL ARRAY DEFINE MAX.Q.SIZE AS AN INTEGER VARIABLE '' initialized in MAIN DEFINE MAX.SLOW.ORDERS AS AN INTEGER VARIABLE '' initialized in MAIN DEFINE LOWEST.PRIORITY AS AN INTEGER VARIABLE '' " " " DEFINE K AS A REAL VARIABLE '' calculation constant " " " DEFINE WAITING.TRAINS AS AN INTEGER VARIABLE DEFINE NUM.TRAINS AS AN INTEGER VARIABLE DEFINE SCHED.STOP AS A REAL, 3-DIMENSIONAL ARRAY DEFINE DBUG AS AN INTEGER VARIABLE DEFINE RESPONSE AS A TEXT VARIABLE DEFINE R.DELAY AS A TEXT VARIABLE DEFINE TRACE.TRN AS AN INTEGER VARIABLE DEFINE SHOW.SEGMENT AS AN INTEGER VARIABLE TALLY MEAN.ELAPSED AS THE MEAN OF ELAPSED.TIME TALLY MIN.ELAPSED AS THE MINIMUM OF ELAPSED.TIME TALLY MAX.ELAPSED AS THE MAXIMUM OF ELAPSED.TIME DEFINE ELAPSED.TIME AS A REAL VARIABLE DEFINE .GREEN TO MEAN 1 DEFINE .RED TO MEAN 0 DEFINE .ENTERING TO MEAN 1 DEFINE .EXITING TO MEAN 2 DEFINE .FROM.SIDING TO MEAN 3 DEFINE .ABSENT TO MEAN 0 DEFINE .PRESENT TO MEAN 1 DEFINE .WEST TO MEAN 1 DEFINE .EAST TO MEAN 0 DEFINE FIND.DIRECTION AS AN INTEGER FUNCTION DEFINE PRINTIT AS A TEXT FUNCTION DEFINE PRINTDIF AS A TEXT FUNCTION END ''---------------------------------------------------------------- FUNCTION FIND.DIRECTION(TRAIN.NUM) '' Direction is designated by even or odd train number. '' '' An odd number means westbound i.e. .WEST (1) '' An even number means eastbound i.e. .EAST (0) '' '' Caution: Be sure that a train is in fact present in a track '' or siding before calling this function; if an argument '' of zero (i.e. no train present) is given in the call '' then a value of '.east' is returned. DEFINE TEMP.VAR AS A REAL VARIABLE DEFINE TRAIN.NUM AS AN INTEGER VARIABLE LET TEMP.VAR = TRAIN.NUM / 2 IF FRAC.F(TEMP.VAR) > 0 RETURN(1) '' westbound OTHERWISE RETURN(0) '' eastbound END '' function FIND.DIRECTION ''---------------------------------------------------------------- FUNCTION PRINTIT(REAL.NUM) '' PRINTIT converts a real number to a time format 5 character string '' e.g. 1.5 --> " 1:30" '' Rounding is done to improve printout '' e.g. 1.992 --> " 2:00" instead of " 1:60" '' The function ITOT.F converts from 'integer' to 'text' DEFINE REAL.NUM AS A REAL VARIABLE DEFINE HOUR.PART AND MINUTE.PART AS INTEGER VARIABLES DEFINE HOUR.TEXT AND MINUTE.TEXT AS TEXT VARIABLES '' separate hours from minutes and round if necessary LET HOUR.PART = TRUNC.F(REAL.NUM) IF ( (REAL.NUM - TRUNC.F(REAL.NUM)) > 0.99 ) LET MINUTE.PART = 0 LET HOUR.PART = HOUR.PART + 1 ELSE LET MINUTE.PART = (REAL.NUM - TRUNC.F(REAL.NUM)) * 60 ALWAYS IF HOUR.PART > 23 LET HOUR.PART = HOUR.PART - 24 ALWAYS '' pad with blanks if necessary IF HOUR.PART > 9 LET HOUR.TEXT = ITOT.F(HOUR.PART) ELSE LET HOUR.TEXT = CONCAT.F(" ", ITOT.F(HOUR.PART)) ALWAYS IF MINUTE.PART > 9 LET MINUTE.TEXT = ITOT.F(MINUTE.PART) ELSE LET MINUTE.TEXT = CONCAT.F("0", ITOT.F(MINUTE.PART)) ALWAYS '' return 5 character string value for printing RETURN(CONCAT.F(HOUR.TEXT, ":", MINUTE.TEXT)) END '' function PRINTIT ''---------------------------------------------------------------- FUNCTION PRINTDIF(X, Y) '' Printdif accepts 2 real number times, takes the difference '' and returns it in text form for printing DEFINE X AND Y AS REAL VARIABLES DEFINE X.MINUTE, X.HOUR, Y.MINUTE, Y.HOUR, MINUTE.PART, AND HOUR.PART AS INTEGER VARIABLES DEFINE SIGN, HOUR.TEXT, AND MINUTE.TEXT AS TEXT VARIABLES LET X.HOUR = TRUNC.F(X) IF ( (X - TRUNC.F(X)) > 0.99 ) LET X.MINUTE = 0 LET X.HOUR = X.HOUR + 1 ELSE LET X.MINUTE = (X - TRUNC.F(X)) * 60 ALWAYS LET Y.HOUR = TRUNC.F(Y) IF ( (Y - TRUNC.F(Y)) > 0.99 ) LET Y.MINUTE = 0 LET Y.HOUR = Y.HOUR + 1 ELSE LET Y.MINUTE = (Y - TRUNC.F(Y)) * 60 ALWAYS IF X.HOUR = Y.HOUR IF X.MINUTE > Y.MINUTE MINUTE.PART = X.MINUTE - Y.MINUTE SIGN = "+" ELSE MINUTE.PART = Y.MINUTE - X.MINUTE SIGN = "-" ALWAYS ALWAYS IF X.HOUR > Y.HOUR SIGN = "+" IF Y.MINUTE > X.MINUTE SUBTRACT 1 FROM X.HOUR ADD 60 TO X.MINUTE ALWAYS MINUTE.PART = X.MINUTE - Y.MINUTE HOUR.PART = X.HOUR - Y.HOUR ALWAYS IF Y.HOUR > X.HOUR SIGN = "-" IF X.MINUTE > Y.MINUTE SUBTRACT 1 FROM Y.HOUR ADD 60 TO Y.MINUTE ALWAYS MINUTE.PART = Y.MINUTE - X.MINUTE HOUR.PART = Y.HOUR - X.HOUR ALWAYS '' function printdiff finishes on next page '' function printdiff continued from last page '' pad with blanks if necessary IF HOUR.PART > 9 LET HOUR.TEXT = ITOT.F(HOUR.PART) ELSE LET HOUR.TEXT = CONCAT.F(" ", ITOT.F(HOUR.PART)) ALWAYS IF MINUTE.PART > 9 LET MINUTE.TEXT = ITOT.F(MINUTE.PART) ELSE LET MINUTE.TEXT = CONCAT.F("0", ITOT.F(MINUTE.PART)) ALWAYS '' return 6 character string value for printing RETURN(CONCAT.F(SIGN, HOUR.TEXT, ":", MINUTE.TEXT)) END '' function PRINTDIF '' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MAIN '' Initialize Global Variables LET LOWEST.PRIORITY = 6 LET MAX.Q.SIZE = 10 LET MAX.SLOW.ORDERS = 10 LET K = 0.12161 '' constant used in travel time calculations '' Display startup message and establish run-time parameters USE 6 FOR OUTPUT PRINT 1 LINES THUS Starting simulation; run with debug (Yes or No) ? USE 11 FOR OUTPUT PRINT 7 LINES THUS Simulation of Train Travel <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PRINT 4 LINES THUS Expected Actual Total Expected Train Departure Travel Travel Elapsed minus Number Time Time Time Time Elapsed SKIP 1 LINES READ RESPONSE USING 12 IF SUBSTR.F(RESPONSE ,1,1) = "Y" OR SUBSTR.F(RESPONSE,1,1) = "y" USE 6 FOR OUTPUT PRINT 8 LINES THUS Enter a number corresponding to the area of debug required: 1. Train Process 5. Check Tracks 2. Dispatcher 6. Segment Travel Calculation 3. Queue/Restart Trains 7. Random Delay 4. Siding Traffic 20. ALL OF THE ABOVE 99. NONE - (you really didn't want debug) READ DBUG USING 12 ELSE LET DBUG = 99 ALWAYS '' routine main continued on next page '' routine main continued from last page USE 6 FOR OUTPUT PRINT 2 LINES THUS If you wish to trace a specific TRACK Please enter the numeric track number or 0 for no trace: READ SHOW.SEGMENT USING 12 PRINT 2 LINES THUS If you wish to trace a specific TRAIN Please enter the 3 digit numeric train id or 0 for no trace: READ TRACE.TRN USING 12 PRINT 1 LINES THUS Do you wish to run with random delays (Yes or No) ? READ R.DELAY USING 12 IF SUBSTR.F(R.DELAY ,1,1) = "Y" OR SUBSTR.F(R.DELAY,1,1) = "y" LET R.DELAY = "SET" ALWAYS PRINT 2 LINES THUS Working . . . '' Initiate Simulation Run USE 10 FOR OUTPUT ACTIVATE A STARTUP NOW ACTIVATE A WINDUP IN 30 DAYS '' the time expression in this statement '' is required syntactically but is '' redundant logically because simulation '' is complete when all train processes '' have terminated START SIMULATION END ''---------------------------------------------------------------- PROCESS STARTUP '' Called by: MAIN '' '' This process reads in values to configure the train track network. '' A 3-dimensional array (QUEUE) is used to set up store train id numbers '' and train types for trains waiting for each segment of track. '' The maximum length of any queue is 10. '' The 2-dimensional TEMP.QUEUE array is used to simplify the process of '' determining which waiting train should be allowed on the track next. '' D.QUEUE and D.TEMP.QUEUE are used similarly for the eastbound track '' segments in double track. '' The 1-dimensional NEXT.TRAIN and D.NEXT.TRAIN lists are used to '' indicate to waiting trains which one is to proceed on single and '' double track respectively. '' The 3-dimensional SLOW.ORDER array holds up to MAX.SLOW.ORDERS '' (initialized in MAIN) pairs of slow orders for type passenger and '' type 'other' trains. '' There is a list of slow order pairs for each track segment. '' The 2-dimensional SCHED.STOP array holds N.TRACK.SEG scheduled stop '' times for each train. '' Each time a train process is initiated, a set of '' scheduled stops is placed in this array. '' NUM.TRAINS is the number of trains. When the train schedule file is put '' through a filter before this Simscript program, the filter counts '' the number of trains and puts the value (num.trains) in a separate '' file. DEFINE I AND J AS INTEGER VARIABLES USE 8 FOR INPUT IF DATA IS NOT ENDED READ N.TRACK.SEG ELSE CALL ABORT.RUN GIVING 1 '' train track file empty ALWAYS '' process startup continued on next page '' process startup continued from last page RESERVE QUEUE(*,*,*) AS N.TRACK.SEG BY MAX.Q.SIZE BY 3 RESERVE TEMP.QUEUE(*,*) AS MAX.Q.SIZE BY 2 RESERVE D.QUEUE(*,*,*) AS N.TRACK.SEG BY MAX.Q.SIZE BY 3 RESERVE D.TEMP.QUEUE(*,*) AS MAX.Q.SIZE BY 2 RESERVE NEXT.TRAIN(*) AS N.TRACK.SEG RESERVE D.NEXT.TRAIN(*) AS N.TRACK.SEG RESERVE MEET(*) AS N.TRACK.SEG RESERVE OVERTAKE(*) AS N.TRACK.SEG RESERVE D.OVERTAKE(*) AS N.TRACK.SEG RESERVE SLOW.ORDER(*,*,*) AS N.TRACK.SEG BY MAX.SLOW.ORDERS BY 3 '' now set up track layout USE 8 FOR INPUT CREATE EVERY TRACK.SEG LET I = 1 FOR EACH TRACK.SEG DO IF DATA IS NOT ENDED READ SUBDIVISION(TRACK.SEG), SPEED.LIM(TRACK.SEG), TRK.LENGTH(TRACK.SEG), SIDING(TRACK.SEG), DOUBLE(TRACK.SEG), AND D.SIDING(TRACK.SEG) ELSE CALL ABORT.RUN GIVING 1 '' train track file empty ALWAYS FILE THE TRACK.SEG IN SEGMENT FOR J = 1 TO MAX.SLOW.ORDERS DO IF DATA IS NOT ENDED READ SLOW.ORDER(I,J,1), SLOW.ORDER(I,J,2), AND SLOW.ORDER(I,J,3) ELSE CALL ABORT.RUN GIVING 1 '' train track file empty ALWAYS LOOP LET I = I + 1 LOOP '' set up storage for trains' scheduled stop times USE 14 FOR INPUT IF DATA IS NOT ENDED READ NUM.TRAINS ELSE CALL ABORT.RUN GIVING 11 '' train count file empty ALWAYS RESERVE SCHED.STOP(*,*,*) AS NUM.TRAINS BY N.TRACK.SEG BY 2 USE 5 FOR INPUT END ''---------------------------------------------------------------- PROCESS TRAIN '' Calls routines: start.train '' get.thru.siding '' travel.thru.segment '' write.segment.travel.time '' point.to.next.segment '' leave.last.segment '' dispatcher '' '' In each iteration of the WHILE loop, the train travels through a '' segment of track. At the start of of the loop, the DISPATCHER is '' called to determine if the train can proceed. '' '' RED LIGHT: '' '' IF the DISPATCHER returns a red light then the train puts itself '' in a queue for the desired segment of track. '' The train then enters a wait loop and stays in this state until '' signalled by the DISPATCHER that it can try again (NEXT.TRAIN is set). '' (When the segment is available the DISPATCHER removes the highest '' priority train from the queue and puts that TRN.ID in NEXT.TRAIN.) '' When the train sees that it can try again it calls the DISPATCHER '' to ensure that a concurrent train process has not gained possession of '' the desired track segment. If it is not available the train will enter '' a wait loop again. '' '' GREEN LIGHT: '' '' If the DISPATCHER finds that a train can proceed, it puts the train '' in the desired segment and returns a green light. The train removes '' itself from the prior segment only after it is assured that it can '' proceed. At this point it calls the DISPATCHER to let it know that '' the prior segment is now available. The train then travels through '' the current segment and calls POINT.TO.NEXT.SEGMENT which will return '' the next location or turn off the CONTINUE.FLAG if the train has '' reached its destination. '' process train continued on next page '' process train continued from last page DEFINE I, LAST.SPEED, NEXT.SPEED, DUMMY.VAL, LIGHT, LAST.LOCATION, NEXT.LOCATION, AND SIGNAL AS INTEGER VARIABLES DEFINE START.WAIT, SEGMENT.WAIT, SEGMENT.TIME, SIDING.TRAVEL, SIDING.WAIT, ACTUAL.TRAVEL.TIME, SEGMENT.START, SEGMENT.END, SEGMENT.TRAVEL.TIME, AND DISTANCE.TO.GO AS REAL VARIABLES DEFINE CONTINUE.FLAG AND NEW.ORIGIN AS TEXT VARIABLES DEFINE CONTINUE.FLAG.SET TO MEAN CONTINUE.FLAG="SET" DEFINE CONTINUE.FLAG.OFF TO MEAN CONTINUE.FLAG="OFF" ''----------------------------------------------------'' '' SUMMARY OF ALGORITHM '' '' '' '' startup '' '' until destination reached (continue flag off) '' '' do '' '' try to get into next segment '' '' red light: wait until segment available '' '' green light: travel in siding if put there '' '' travel on main track '' '' loop '' '' clean up and write stats '' '' '' ''----------------------------------------------------'' LET CONTINUE.FLAG = "SET" CALL START.TRAIN YIELDING LOCATION, DIRECTION, AND CONTINUE.FLAG LET LAST.SPEED = 0 CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND .ENTERING YIELDING LIGHT IF DBUG = 1 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID THUS *** Entering track layout. ALWAYS WHILE CONTINUE.FLAG.SET DO LET DISTANCE.TO.GO = TRK.LENGTH(LOCATION) LET SEGMENT.WAIT = 0 LET SIDING.WAIT = 0 LET SIDING.TRAVEL = 0 '' process train continued on next page '' process train continued from last page '' RED.LIGHT WHILE LIGHT EQ .RED DO CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND .ENTERING YIELDING LIGHT IF LIGHT = .GREEN LEAVE ALWAYS IF SEGMENT.WAIT = 0 ADD 1 TO WAITING.TRAINS LET START.WAIT = (TIME.V * 24) IF DBUG = 1 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID, PRINTIT(START.WAIT), AND LOCATION THUS *** gets Red Light at time ***** location ** ALWAYS ALWAYS CALL QUEUE.TRAIN GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION IF TRN.ID = TRACE.TRN USE 13 FOR OUTPUT CALL DISPLAY.SEGMENT GIVING LOCATION PRINT 1 LINES WITH TRN.ID THUS *** has just been queued USE 10 FOR OUTPUT ALWAYS IF LOCATION = SHOW.SEGMENT CALL DISPLAY.SEGMENT GIVING LOCATION ALWAYS '' red light loop of '' process train continued on next page '' process train continued from last page IF (DOUBLE(LOCATION) = .PRESENT AND DIRECTION = .EAST) WHILE TRN.ID NOT EQUAL TO D.NEXT.TRAIN(LOCATION) DO LET SEGMENT.WAIT = SEGMENT.WAIT + 1 WAIT 0.0002778 HOURS IF ((SEGMENT.WAIT / 3600) > 12) PRINT 1 LINES WITH TRN.ID AND LOCATION THUS <<< Train *** stalled in segment ** >>> CALL DISPLAY.SEGMENT GIVING LOCATION CALL ABORT.RUN GIVING 12 ALWAYS LOOP LET D.NEXT.TRAIN(LOCATION) = 0 ELSE WHILE TRN.ID NOT EQUAL TO NEXT.TRAIN(LOCATION) DO LET SEGMENT.WAIT = SEGMENT.WAIT + 1 WAIT 0.0002778 HOURS IF ((SEGMENT.WAIT / 3600) > 12) PRINT 1 LINES WITH TRN.ID AND LOCATION THUS <<< Train *** stalled in segment ** >>> CALL DISPLAY.SEGMENT GIVING LOCATION CALL ABORT.RUN GIVING 12 ALWAYS LOOP LET NEXT.TRAIN(LOCATION) = 0 ALWAYS LET SEGMENT.WAIT = SEGMENT.WAIT / 3600 LOOP '' end of while light eq .red loop '' process train continued on next page '' process train continued from last page '' GREEN.LIGHT '' <<< tidy up departure from last segment >>> '' '' At this point, a train has got a green light from the Dispatcher '' on its first try, or has waited for the red light to go green. '' In either case, it has been placed on main track or on a siding. IF DBUG = 1 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID AND LOCATION THUS *** with Green light in location ** ALWAYS IF SUBDIVISION(LOCATION) <> TRN.ORIGIN CALL LEAVE.LAST.SEGMENT GIVING TRN.ID AND LAST.LOCATION CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LAST.LOCATION, AND .EXITING YIELDING DUMMY.VAL '' the next 2 calls to the Dispatcher are to '' wake up any other waiting trains '' (refer to 'relief of conjestion' in '' routine: empty.track.empty.siding) IF LAST.LOCATION < N.TRACK.SEG CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, (LAST.LOCATION + 1 ), AND .EXITING YIELDING DUMMY.VAL ALWAYS IF LAST.LOCATION > 1 CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, (LAST.LOCATION - 1 ), AND .EXITING YIELDING DUMMY.VAL ALWAYS ALWAYS LET SEGMENT.START = TIME.V * 24 '' process train continued on next page '' process train continued from last page '' <<< SIDING TRAVEL CODE >>> '' '' go through siding if so indicated by Dispatcher '' don't come out until main track clear IF ( SIDING.STATUS(LOCATION) = TRN.ID OR D.SIDING.STATUS(LOCATION) = TRN.ID ) IF DBUG = 1 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID AND LOCATION THUS *** going into Siding in location ** ALWAYS IF TRN.ID = TRACE.TRN USE 13 FOR OUTPUT CALL DISPLAY.SEGMENT GIVING LOCATION PRINT 1 LINES WITH TRN.ID THUS *** before travelling through siding USE 10 FOR OUTPUT ALWAYS IF LOCATION = SHOW.SEGMENT CALL DISPLAY.SEGMENT GIVING LOCATION ALWAYS CALL GET.THRU.SIDING GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING SIDING.WAIT AND SIDING.TRAVEL SUBTRACT 1.0 FROM DISTANCE.TO.GO '' presume sidings are 1 mile IF SIDING.WAIT > 0 ADD 1 TO WAITING.TRAINS ALWAYS '' train will have obtained main track after get.thru.siding ALWAYS '' process train continued on next page '' process train continued from last page '' <<< MAIN TRACK TRAVEL CODE >>> '' '' Train always travels on main track after this point IF DBUG = 1 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID AND LOCATION THUS *** on Main track in location ** ALWAYS IF TRN.ID = TRACE.TRN USE 13 FOR OUTPUT CALL DISPLAY.SEGMENT GIVING LOCATION PRINT 1 LINES WITH TRN.ID THUS *** before travelling along main track USE 10 FOR OUTPUT ALWAYS IF LOCATION = SHOW.SEGMENT CALL DISPLAY.SEGMENT GIVING LOCATION ALWAYS CALL TRAVEL.THRU.SEGMENT GIVING TRN.PTR, TRN.ID, HORSEPWR, TRN.TYPE, DISTANCE.TO.GO, LAST.SPEED, TRN.ORIGIN, DESTINATION, AND LOCATION YIELDING SEGMENT.TRAVEL.TIME, NEXT.SPEED, NEW.ORIGIN, AND SIGNAL LET LAST.SPEED = NEXT.SPEED LET LIGHT = SIGNAL '' Travel.thru called Dispatcher with '.entering' IF DBUG = 1 OR DBUG = 20 PRINT 2 LINES WITH TRN.ID, LOCATION, AND LIGHT THUS In Process Train, after call to travel.thru.segment, train *** at location ** got light = * (0=red, 1=green) for next seg ALWAYS LET SEGMENT.END = TIME.V * 24 ADD SIDING.TRAVEL TO ACTUAL.TRAVEL.TIME ADD SEGMENT.TRAVEL.TIME TO ACTUAL.TRAVEL.TIME CALL WRITE.SEGMENT.TIME GIVING TRN.ID, LOCATION, SEGMENT.WAIT, SEGMENT.TRAVEL.TIME, SIDING.WAIT, SEGMENT.START, AND SEGMENT.END LET TRN.ORIGIN = NEW.ORIGIN '' train maybe restarted if running early '' point.to.next was called in travel.thru to get the pointer to '' the next segment but is called again here so variables local '' to Process Train can be adjusted LET LAST.LOCATION = LOCATION CALL POINT.TO.NEXT.SEGMENT GIVING DESTINATION, DIRECTION, AND LOCATION YIELDING NEXT.LOCATION AND CONTINUE.FLAG LET LOCATION = NEXT.LOCATION '' wake up any waiting trains '' - they won't get main track but could get siding CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LAST.LOCATION, AND .EXITING YIELDING DUMMY.VAL '' process train finishes on next page '' process train continued from last page LOOP '' end of main loop, train has reached its destination '' <<< LEAVE TRACK; WRITE STATS >>> '' CALL LEAVE.LAST.SEGMENT GIVING TRN.ID AND LOCATION CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND .EXITING YIELDING DUMMY.VAL '' wake up any other waiting trains '' (refer to 'relief of conjestion' in '' routine: empty.track.empty.siding IF (LOCATION LE N.TRACK.SEG) AND (LOCATION > 1) CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, (LOCATION - 1 ), AND .EXITING YIELDING DUMMY.VAL ALWAYS IF (LOCATION GE 1) AND (LOCATION < N.TRACK.SEG) CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, (LOCATION + 1 ), AND .EXITING YIELDING DUMMY.VAL ALWAYS IF DBUG = 1 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID, DESTINATION, AND LOCATION THUS *** LEAVING track layout at ******* ** ALWAYS LET ELAPSED.TIME = (TIME.V * 24) - START.TIME USE 11 FOR OUTPUT PRINT 1 LINE WITH TRN.ID, PRINTIT(START.TIME), PRINTIT(TRAVEL.TIME), PRINTIT(ACTUAL.TRAVEL.TIME), PRINTIT(ELAPSED.TIME) AND PRINTDIF(TRAVEL.TIME, ELAPSED.TIME) THUS *** ***** ***** ***** ***** ****** USE 10 FOR OUTPUT '' restore default channel for other trains END '' of Process Train ''---------------------------------------------------------------- ROUTINE QUEUE.TRAIN GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION '' called by: train process '' Used for queueing trains for either single or double track. DEFINE TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION AS INTEGER VARIABLES DEFINE I AS AN INTEGER VARIABLE IF (DIRECTION = .WEST OR (DIRECTION = .EAST AND DOUBLE(LOCATION) = .ABSENT)) FOR I = 1 TO MAX.Q.SIZE UNTIL QUEUE(LOCATION,I,1) = 0 DO '' find a spot in the queue if possible LOOP IF I = MAX.Q.SIZE AND QUEUE(LOCATION,I,1) <> 0 USE 11 FOR OUTPUT PRINT 2 LINES WITH I AND SUBDIVISION(LOCATION) THUS <<<< ** trains waiting for track segment: ******** >>>> CALL ABORT.RUN GIVING 2 '' inadequate queue length ALWAYS LET QUEUE(LOCATION,I,1) = TRN.ID LET QUEUE(LOCATION,I,2) = TRN.TYPE IF DBUG = 3 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID, LOCATION, AND I THUS Train *** put in queue # ** at position ** ALWAYS ALWAYS IF DIRECTION = .EAST AND DOUBLE(LOCATION) = .PRESENT FOR I = 1 TO MAX.Q.SIZE UNTIL D.QUEUE(LOCATION,I,1) = 0 DO '' find a spot in the queue if possible LOOP IF I = MAX.Q.SIZE AND D.QUEUE(LOCATION,I,1) <> 0 USE 11 FOR OUTPUT PRINT 2 LINES WITH I AND SUBDIVISION(LOCATION) THUS <<<< ** trains waiting for double track : ******** >>>> CALL ABORT.RUN GIVING 2 '' inadequate queue length ALWAYS LET D.QUEUE(LOCATION,I,1) = TRN.ID LET D.QUEUE(LOCATION,I,2) = TRN.TYPE IF DBUG = 3 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID, LOCATION, AND I THUS Train *** put in queue # ** at position ** ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE LEAVE.LAST.SEGMENT GIVEN TRN.ID AND LOCATION '' called by: train process '' get.thru.siding routine '' NOTE: The siding checks here MUST preceed the main track checks, '' because a train can travel through a siding and then on '' main track. So in the process of moving from siding to main, '' the TRN.ID will 'momentarily' be in both siding and track status. DEFINE TRN.ID AND LOCATION AS INTEGER VARIABLES DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF SIDING.STATUS(LOCATION) = TRN.ID LET SIDING.STATUS(LOCATION) = 0 LEAVE ALWAYS IF D.SIDING.STATUS(LOCATION) = TRN.ID LET D.SIDING.STATUS(LOCATION) = 0 LEAVE ALWAYS IF DOUBLE.STATUS(LOCATION) = TRN.ID LET DOUBLE.STATUS(LOCATION) = 0 LET DOUBLE.ENTRY.PRIORITY = 0 LEAVE ALWAYS IF TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.STATUS(LOCATION) = 0 LET TRACK.ENTRY.PRIORITY = 0 LEAVE ALWAYS CALL DISPLAY.SEGMENT GIVING LOCATION PRINT 1 LINES WITH TRN.ID AND LOCATION THUS *** cannot be found in prior segment (**) - resulted in abort CALL ABORT.RUN GIVING 10 '' trn.id not found in prior seg LOOP IF MEET(LOCATION) = TRN.ID LET MEET(LOCATION) = 0 ALWAYS IF OVERTAKE(LOCATION) = TRN.ID LET OVERTAKE(LOCATION) = 0 ALWAYS IF D.OVERTAKE(LOCATION) = TRN.ID LET D.OVERTAKE(LOCATION) = 0 ALWAYS END ''---------------------------------------------------------------- ROUTINE GET.THRU.SIDING GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING SIDING.WAIT, AND SIDING.TRAVEL '' Called by: train process DEFINE TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND LIGHT AS INTEGER VARIABLES DEFINE SIDING.WAIT AND SIDING.TRAVEL AS REAL VARIABLES IF DBUG = 4 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID AND LOCATION THUS Train *** location ** at start of get.thru.siding ALWAYS LET SIDING.TRAVEL = 0.0833 '' 5 minutes WORK SIDING.TRAVEL HOURS WHILE ( (MEET(LOCATION) > 0) OR (OVERTAKE(LOCATION) > 0) OR (D.OVERTAKE(LOCATION) > 0) ) DO WAIT 0.0002778 HOURS LET SIDING.WAIT = SIDING.WAIT + 1 LOOP CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND .FROM.SIDING YIELDING LIGHT WHILE LIGHT = .RED DO IF DIRECTION = .EAST AND DOUBLE(LOCATION) = .PRESENT WHILE DOUBLE.STATUS(LOCATION) > 0 DO WAIT 0.0002778 HOURS LET SIDING.WAIT = SIDING.WAIT + 1 LOOP ELSE WHILE TRACK.STATUS(LOCATION) > 0 DO WAIT 0.0002778 HOURS LET SIDING.WAIT = SIDING.WAIT + 1 LOOP ALWAYS CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND .FROM.SIDING YIELDING LIGHT LOOP '' routine get.thru.siding continued next page '' routine get.thru.siding continued next page LET SIDING.WAIT = SIDING.WAIT / 3600 '' convert seconds to hours CALL LEAVE.LAST.SEGMENT GIVING TRN.ID AND LOCATION '' wake up any other waiting trains '' (refer to lookahead for oncoming trains in '' routine: track.without.siding IF LOCATION < N.TRACK.SEG '' avoid subscript out of range CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, (LOCATION + 1 ), AND .EXITING YIELDING LIGHT ALWAYS IF LOCATION > 1 '' avoid subscript out of range CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, DIRECTION, (LOCATION - 1 ), AND .EXITING YIELDING LIGHT ALWAYS IF DBUG = 4 OR DBUG = 20 PRINT 1 LINES WITH PRINTIT(SIDING.WAIT) THUS siding.wait ***** at end of get.thru.siding ALWAYS END ''---------------------------------------------------------------- ROUTINE TRAVEL.THRU.SEGMENT GIVEN TRN.PTR, TRN.ID, HORSEPWR, TRN.TYPE, DISTANCE.TO.GO, LAST.SPEED, TRN.ORIGIN, DESTINATION, AND LOCATION YIELDING SEGMENT.TRAVEL.TIME, NEXT.SPEED, NEW.ORIGIN AND SIGNAL '' Called by: train process '' Calls: get.to.speed '' approach.max '' speed.up.slow.down DEFINE TRN.ID, TRN.PTR, TRN.TYPE, NEXT.SEG, LOCATION, AND SLOW.VAL AS INTEGER VARIABLES DEFINE HORSEPWR AND SEGMENT.TRAVEL.TIME AS REAL VARIABLES DEFINE X, LAST.SPEED, DUMMY.VAL, SIGNAL, NEXT.SPEED, PRIOR.SPEED, I, N, END.SPEED, SPEED.SUB, AND MAX.SPEED AS INTEGER VARIABLES DEFINE DISTANCE.TO.GO, DISTANCE.LEFT, DELTA.T, DELTA.D, NEW.LENGTH, TIME.USED, DELAY.TIME, AND LOOP.END AS REAL VARIABLES DEFINE TRN.ORIGIN, NEW.ORIGIN, DESTINATION AND CONT.FLAG AS TEXT VARIABLES DEFINE SLOW AS A REAL, 2-DIMENSIONAL ARRAY RESERVE SLOW(*,*) AS MAX.SLOW.ORDERS BY 2 IF TRN.TYPE = 2 LET MAX.SPEED = SPEED.LIM(LOCATION) + 15 LET SPEED.SUB = 1 ELSE LET MAX.SPEED = SPEED.LIM(LOCATION) LET SPEED.SUB = 2 ALWAYS IF DBUG = 6 OR DBUG = 20 PRINT 1 LINES WITH DISTANCE.TO.GO THUS distance.to.go at start of travel = ***.*** miles ALWAYS LET N = 0 FOR I = 1 TO MAX.SLOW.ORDERS UNTIL (SLOW.ORDER(LOCATION, I, SPEED.SUB) = 0) DO LET N = N + 1 LET SLOW(I,1) = SLOW.ORDER(LOCATION, I, SPEED.SUB) LET SLOW(I,2) = SLOW.ORDER(LOCATION, I, 3) LOOP '' routine travel.thru.segment continued next page '' routine travel.thru.segment continued from last page LET END.SPEED = 900 '' use as a flag to indicate train can continue '' into next segment without halting CALL POINT.TO.NEXT.SEGMENT GIVING DESTINATION, FIND.DIRECTION(TRN.ID), AND LOCATION YIELDING NEXT.SEG AND CONT.FLAG IF CONT.FLAG = "OFF" '' train has reached destination segment LET END.SPEED = 0 ELSE CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), NEXT.SEG, AND .ENTERING YIELDING SIGNAL IF ( (SIGNAL = .RED) OR (SCHED.STOP(TRN.PTR,NEXT.SEG,1) > 0) OR (SIDING.STATUS(NEXT.SEG) = TRN.ID) OR (D.SIDING.STATUS(NEXT.SEG) = TRN.ID) ) LET END.SPEED = 0 ALWAYS ALWAYS IF R.DELAY = "SET" CALL RANDOM.DELAY GIVING TRN.ID AND LOCATION YIELDING DELAY.TIME IF DELAY.TIME > 0 WAIT DELAY.TIME HOURS ADD DELAY.TIME TO SEGMENT.TRAVEL.TIME LET END.SPEED = 0 ALWAYS ALWAYS LET LOOP.END = -1 WHILE LOOP.END < 0.0 '' the purpose of this loop is to determine how close the train '' can approach slow orders without exceeding track length DO LET PRIOR.SPEED = LAST.SPEED LET DISTANCE.LEFT = DISTANCE.TO.GO LET TIME.USED = 0 FOR I = 1 TO N '' go through slow orders DO IF N = 0 '' if none, leave For loop LET NEXT.SPEED = LAST.SPEED LEAVE ALWAYS IF SLOW(I,1) > 0 '' change to specified speed LET NEXT.SPEED = SLOW(I,1) CALL GET.TO.SPEED GIVING TRN.ID, HORSEPWR, PRIOR.SPEED, AND NEXT.SPEED YIELDING DELTA.T AND DELTA.D ADD DELTA.T TO TIME.USED SUBTRACT DELTA.D FROM DISTANCE.LEFT LET PRIOR.SPEED = SLOW(I,1) ALWAYS '' routine travel.thru.segment continued next page '' routine travel.thru.segment continued from last page '' go slow for specified length IF SLOW(I,2) > 0 AND END.SPEED = 0 CALL GET.TO.SPEED GIVING TRN.ID, HORSEPWR, NEXT.SPEED, AND 0 YIELDING DELTA.T AND DELTA.D IF (DELTA.D + SLOW(I,2)) GE DISTANCE.LEFT LET NEW.LENGTH = DISTANCE.LEFT - (DELTA.D + 0.5) LET DISTANCE.LEFT = DELTA.D + 0.5 LET DELTA.T = NEW.LENGTH / SLOW(I,1) '' t = d / v ADD DELTA.T TO TIME.USED LEAVE ALWAYS ALWAYS IF SLOW(I,2) > 0 AND END.SPEED = 900 IF SLOW(I,2) GE DISTANCE.LEFT LET NEW.LENGTH = DISTANCE.LEFT - 0.5 LET DISTANCE.LEFT = 0.5 LET DELTA.T = NEW.LENGTH / SLOW(I,1) '' t = d / v ADD DELTA.T TO TIME.USED LEAVE ALWAYS ALWAYS IF SLOW(I,2) > 0 DELTA.T = SLOW(I,2) / SLOW(I,1) '' t = d / v ADD DELTA.T TO TIME.USED SUBTRACT SLOW(I,2) FROM DISTANCE.LEFT ALWAYS LOOP '' end of For loop IF DBUG = 6 OR DBUG = 20 PRINT 2 LINES WITH TRN.ID, DISTANCE.LEFT AND PRINTIT(TIME.USED) THUS trn *** distance.left after slow orders = ***.*** miles; time used = ***** ALWAYS '' routine travel.thru.segment continued next page '' routine travel.thru.segment continued from last page IF (END.SPEED = 900) AND (DISTANCE.LEFT > 0) ''green light next seg LET X = NEXT.SPEED CALL APPROACH.MAX GIVING TRN.ID, HORSEPWR, DISTANCE.LEFT, X, AND MAX.SPEED YIELDING DELTA.T AND NEXT.SPEED ADD DELTA.T TO TIME.USED LEAVE ALWAYS IF (END.SPEED < 900) AND (DISTANCE.LEFT > 0) '' red light next seg LET X = NEXT.SPEED CALL SPEED.UP.SLOW.DOWN GIVING TRN.ID, HORSEPWR, DISTANCE.LEFT, X, AND MAX.SPEED YIELDING DELTA.T IF DELTA.T > 0 '' got to end.speed in distance left ? ADD DELTA.T TO TIME.USED LEAVE ALWAYS ALWAYS LET SLOW.VAL = 1 IF (DISTANCE.LEFT < 0) OR (DELTA.T < 0) '' overshot end of track ? FOR I = 1 TO N '' see if any slow orders to reduce DO IF SLOW(I,1) > SLOW.VAL LET SLOW.VAL = SLOW(I,1) ALWAYS LOOP ALWAYS IF (DISTANCE.LEFT < 0) AND (SLOW.VAL = 1) CALL ABORT.RUN GIVING 8 ''distance travelled exceeds track length ALWAYS FOR I = 1 TO N '' reduce slow orders DO IF SLOW(I,1) > 0 LET SLOW(I,1) = SLOW(I,1) - 1 ALWAYS LOOP LOOP '' end of while loop '' routine travel.thru.segment finishes on next page '' routine travel.thru.segment continues from last page WORK TIME.USED HOURS ADD TIME.USED TO SEGMENT.TRAVEL.TIME LET LAST.SPEED = NEXT.SPEED RELEASE SLOW(*,*) '' perform a scheduled stop if one present LET NEW.ORIGIN = TRN.ORIGIN '' this could be changed if train early IF SCHED.STOP(TRN.PTR, LOCATION,1) > 0 WAIT (SCHED.STOP(TRN.PTR, LOCATION,1)) HOURS ADD SCHED.STOP(TRN.PTR, LOCATION,1) TO SEGMENT.TRAVEL.TIME IF TIME.V < SCHED.STOP(TRN.PTR,LOCATION,2) CALL LEAVE.LAST.SEGMENT GIVING TRN.ID AND LOCATION CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), LOCATION, AND .EXITING YIELDING DUMMY.VAL IF LOCATION < N.TRACK.SEG CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), (LOCATION + 1), AND .EXITING YIELDING DUMMY.VAL ALWAYS IF LOCATION > 1 CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), (LOCATION - 1), AND .EXITING YIELDING DUMMY.VAL ALWAYS IF ( SIGNAL = .GREEN AND CONT.FLAG = "SET" ) CALL LEAVE.LAST.SEGMENT GIVING TRN.ID AND NEXT.SEG CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), NEXT.SEG, AND .EXITING YIELDING DUMMY.VAL IF NEXT.SEG < N.TRACK.SEG CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), (NEXT.SEG + 1), AND .EXITING YIELDING DUMMY.VAL ALWAYS IF NEXT.SEG > 1 CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), (NEXT.SEG - 1), AND .EXITING YIELDING DUMMY.VAL ALWAYS ALWAYS WAIT ((SCHED.STOP(TRN.PTR,LOCATION,2) - TIME.V) * 24) HOURS IF CONT.FLAG = "SET" LET NEW.ORIGIN = SUBDIVISION(NEXT.SEG) CALL DISPATCHER GIVING TRN.ID, TRN.TYPE, FIND.DIRECTION(TRN.ID), NEXT.SEG, AND .ENTERING YIELDING SIGNAL ALWAYS ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE GET.TO.SPEED GIVEN TRN.ID, HORSEPWR, CURRENT.SPEED, AND DESIRED.SPEED YIELDING DELTA.T AND DELTA.D '' Called by: travel.thru.segment '' speed.up.slow.down '' The constant, K, is assigned in MAIN DEFINE HORSEPWR, DELTA.T, AND DELTA.D AS REAL VARIABLES DEFINE TRN.ID, CURRENT.SPEED, AND DESIRED.SPEED AS INTEGER VARIABLES DEFINE DELTA.T.1 AND DELTA.T.2 AS REAL VARIABLES LET DELTA.T.1 = (K * (DESIRED.SPEED ** 2)) / HORSEPWR LET DELTA.T.2 = (K * (CURRENT.SPEED ** 2)) / HORSEPWR IF DELTA.T.1 > DELTA.T.2 DELTA.T = DELTA.T.1 - DELTA.T.2 ELSE DELTA.T = DELTA.T.2 - DELTA.T.1 ALWAYS LET DELTA.T = DELTA.T /3600.0 DELTA.D = (((DESIRED.SPEED + CURRENT.SPEED) / 2) * DELTA.T) IF DBUG = 6 OR DBUG = 20 PRINT 2 LINES WITH TRN.ID, CURRENT.SPEED, DESIRED.SPEED, PRINTIT(DELTA.T), AND DELTA.D THUS for trn *** current speed ***.** desired speed ***.** delta.t ***** delta.d ****.*** ALWAYS END ''---------------------------------------------------------------- ROUTINE APPROACH.MAX GIVEN TRN.ID, HORSEPWR, DISTANCE.LEFT, SLOW.SPEED, AND MAX.SPEED YIELDING TOTAL.TIME AND NEXT.SPEED '' Called by: travel.thru.segment '' This routine attempts to attain maximum speed in the distance.left. '' If it reaches maximum and there is still some distance left, '' it will travel at maximum for that length of time and return '' maximum speed and the total time taken to speed up and travel. '' If it cannot achieve maximum speed '' it returns the speed reached and the time it took to reach it. '' The constant, K, is assigned in MAIN DEFINE TRN.ID, SLOW.SPEED, MAX.SPEED, AND NEXT.SPEED AS INTEGER VARIABLES DEFINE HORSEPWR, DISTANCE.LEFT, AND DELTA.T AS REAL VARIABLES DEFINE CURRENT.SPEED AS AN INTEGER VARIABLE DEFINE X, Y, TIME.TO.SLOW, CURRENT.DISTANCE, HOLD.DISTANCE, HOLD.DELTA.T, HOLD.SPEED, DIST.AND.MIN, TOTAL.TIME, TIME.TO.CURRENT, DIST.DIFF, TIME.DIFF, AND DISTANCE AS REAL VARIABLES LET TIME.TO.SLOW = ( K * (SLOW.SPEED ** 2) ) / HORSEPWR LET CURRENT.SPEED = SLOW.SPEED LET CURRENT.DISTANCE = 0 WHILE ((CURRENT.DISTANCE < DISTANCE.LEFT) AND (CURRENT.SPEED < MAX.SPEED)) DO LET HOLD.SPEED = CURRENT.SPEED LET HOLD.DISTANCE = CURRENT.DISTANCE LET HOLD.DELTA.T = DELTA.T LET CURRENT.SPEED = CURRENT.SPEED + 1 LET TIME.TO.CURRENT = ( K * (CURRENT.SPEED ** 2) ) / HORSEPWR LET DELTA.T = (TIME.TO.CURRENT - TIME.TO.SLOW) /3600 LET X = SLOW.SPEED + ((CURRENT.SPEED - SLOW.SPEED) / 2) LET CURRENT.DISTANCE = X * DELTA.T LOOP IF CURRENT.SPEED = MAX.SPEED DISTANCE = DISTANCE.LEFT- CURRENT.DISTANCE LET TOTAL.TIME = ((DISTANCE ) / CURRENT.SPEED) + DELTA.T LET NEXT.SPEED = CURRENT.SPEED ELSE TOTAL.TIME = ((DISTANCE.LEFT- HOLD.DISTANCE) / HOLD.SPEED) + HOLD.DELTA.T LET NEXT.SPEED = HOLD.SPEED ALWAYS IF (DBUG = 6) OR (DBUG = 20) PRINT 1 LINES WITH TRN.ID, PRINTIT(TOTAL.TIME), AND NEXT.SPEED THUS trn *** : approach.max yields ***** hrs at *** mph ALWAYS END ''---------------------------------------------------------------- ROUTINE SPEED.UP.SLOW.DOWN GIVEN TRN.ID, HORSEPWR, DISTANCE.LEFT, CURRENT.SPEED, AND MAX.SPEED YIELDING DELTA.T '' Called by: travel.thru.segment '' Calls: get.to.speed '' The constant, K, is assigned in MAIN DEFINE TRN.ID, CURRENT.SPEED, AND MAX.SPEED AS INTEGER VARIABLES DEFINE HORSEPWR, DISTANCE.LEFT, AND DELTA.T AS REAL VARIABLES DEFINE CHK.FLAG AS AN INTEGER VARIABLE DEFINE SPEED AS AN INTEGER VARIABLE DEFINE TIME.TO.CURRENT, TIME.TO.MAX, DISTANCE, D.LEFT, D.LESS.TEST, DELTA.D, DIST.IN.TEST, SPEED.VAL, TIME.VAL, AND TIME.AT.MAX AS REAL VARIABLES '' In reality, a train would accelerate from the current speed and, '' if possible, reach the maximum allowable speed. It would continue '' at maximum (or as close as possible to maximum) until it had to '' decelerate in order to halt at the end of the segment. '' '' The order of these events is not of concern in this code since '' the value desired is the time taken to reach the end of the segment. '' Therefore, the order is not observed here; this simplifies computations. CALL GET.TO.SPEED GIVING TRN.ID, HORSEPWR, CURRENT.SPEED, AND 0 YIELDING DELTA.T AND DELTA.D '' routine speed.up.slow.down continued next page '' routine speed.up.slow.down continued from last page LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF DELTA.D > DISTANCE.LEFT LET DELTA.T = -1 '' negative value returned forces retry IF (DBUG = 6) OR (DBUG = 20) PRINT 1 LINES WITH TRN.ID AND DELTA.T THUS trn *** : speed.up.slow.down forces retry with delta.t ** ALWAYS LEAVE ALWAYS LET D.LEFT = DISTANCE.LEFT - DELTA.D IF D.LEFT < 0.25 '' small distance to go ? AND CURRENT.SPEED > 0 LET SPEED = CURRENT.SPEED LET TIME.VAL = D.LEFT / SPEED '' t = d / v LET DELTA.T = DELTA.T + TIME.VAL LEAVE ALWAYS LET TIME.TO.CURRENT = (K * (CURRENT.SPEED ** 2)) / HORSEPWR LET SPEED = 0 LET TIME.TO.MAX = TIME.TO.CURRENT LET DISTANCE = D.LEFT / 2 LET D.LESS.TEST = DISTANCE WHILE ( (SPEED < MAX.SPEED) AND (D.LESS.TEST > 0.1) ) DO '' calculations LET SPEED = SPEED + 1 LET TIME.TO.MAX = (K * (SPEED ** 2))/HORSEPWR LET SPEED.VAL = CURRENT.SPEED + ((SPEED - CURRENT.SPEED) / 2) LET TIME.VAL = (TIME.TO.MAX - TIME.TO.CURRENT) LET DIST.IN.TEST = ( SPEED.VAL * TIME.VAL ) / 3600 LET D.LESS.TEST = DISTANCE - DIST.IN.TEST LOOP '' end of calculations loop IF SPEED = 0 LET TIME.TO.MAX = TIME.TO.CURRENT IF CURRENT.SPEED LE 0 LET SPEED = 1 ELSE LET SPEED = CURRENT.SPEED ALWAYS ALWAYS LET D.LESS.TEST = D.LESS.TEST * 2 LET TIME.AT.MAX = D.LESS.TEST / SPEED DELTA.T = (((TIME.TO.MAX - TIME.TO.CURRENT) * 2) / 3600) + TIME.AT.MAX LEAVE LOOP '' end of main while loop IF (DBUG = 6) OR (DBUG = 20) PRINT 1 LINES WITH TRN.ID, SPEED, AND PRINTIT(DELTA.T) THUS trn *** : speed.up.slow.down achieved *** mph in ***** hours ALWAYS END ''---------------------------------------------------------------- ROUTINE RANDOM.DELAY GIVEN TRN.ID AND LOCATION YIELDING DELAY.TIME '' Hot box delays are typically 20-45 minutes '' depending on which car in the train the hot box is located. '' The delay could take up to 1 hour if a car needs to be set off '' for repair. '' The delay could take 2-3 hours if the hot box were really hot '' (the train has to travel very slowly in this case.) '' DEFINE MU, SIGMA, X AND DELAY.TIME AS REAL VARIABLES DEFINE TRN.ID, LOCATION AND CHK.FLAG AS AN INTEGER VARIABLE LET MU = 50 LET SIGMA = 12.5 LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO LET X = NORMAL.F(MU, SIGMA, 1) IF (X > (MU - (2.5 * SIGMA))) AND (X < (MU + (2.5 * SIGMA))) LET DELAY.TIME = 0.0 LEAVE '' leave if no potential hot box indicated by detector ALWAYS LET X = UNIFORM.F(20.0, 45.0, 1) '' find time to walk train LET DELAY.TIME = X / 60 '' express time in terms of hours LET X = UNIFORM.F(0.0, 1.0, 1) IF X < 0.7 LEAVE '' leave if no hot box found ALWAYS LET X = UNIFORM.F(0.0, 1.0, 1) IF X < 0.7 DELAY.TIME = DELAY.TIME + X LEAVE ALWAYS LET X = UNIFORM.F(1.0, 3.0, 1) DELAY.TIME = DELAY.TIME + X LEAVE LOOP IF DBUG = 7 OR DBUG = 20 IF DELAY.TIME > 0.0 PRINT 1 LINES WITH TRN.ID, LOCATION AND PRINTIT(DELAY.TIME) THUS Random delay for train *** at location ** is ***** ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE WRITE.SEGMENT.TIME GIVEN TRN.ID, LOCATION, SEGMENT.WAIT, SEGMENT.TRAVEL.TIME, SIDING.WAIT, SEGMENT.START, AND SEGMENT.END '' Called by: train process DEFINE TRN.ID, AND LOCATION AS INTEGER VARIABLES DEFINE SEGMENT.WAIT, SIDING.WAIT, SEGMENT.TRAVEL.TIME, SEGMENT.START, AND SEGMENT.END AS REAL VARIABLES ADD SIDING.WAIT TO SEGMENT.WAIT USE 9 FOR OUTPUT PRINT 1 LINES WITH TRN.ID, SUBDIVISION(LOCATION), PRINTIT(SEGMENT.WAIT), PRINTIT(SIDING.WAIT), PRINTIT(SEGMENT.TRAVEL.TIME), PRINTIT(SEGMENT.START), AND PRINTIT(SEGMENT.END) THUS *** ******* Waits: *****<-all *****<-sid Travel: ***** Start: ***** End: ***** USE 10 FOR OUTPUT END ''---------------------------------------------------------------- ROUTINE POINT.TO.NEXT.SEGMENT GIVEN DESTINATION, DIRECTION AND LOCATION YIELDING NEXT.LOCATION AND CONTINUE.FLAG '' Called by: train process DEFINE DESTINATION AND CONTINUE.FLAG AS TEXT VARIABLES DEFINE DIRECTION AND LOCATION AS INTEGER VARIABLES DEFINE NEXT.LOCATION AS AN INTEGER VARIABLE DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET CHK.FLAG = 1 LET CONTINUE.FLAG = "SET" WHILE CHK.FLAG = 1 DO LET NEXT.LOCATION = LOCATION IF (LOCATION + 1) > N.TRACK.SEG AND DIRECTION = .WEST IF DESTINATION = SUBDIVISION(LOCATION) LET CONTINUE.FLAG = "OFF" '' ||-<===| LEAVE OTHERWISE CALL ABORT.RUN GIVING 3 ''end of track reached before destination ALWAYS IF (LOCATION - 1) < 1 AND DIRECTION = .EAST IF DESTINATION = SUBDIVISION(LOCATION) LET CONTINUE.FLAG = "OFF" '' |-==>-|| LEAVE OTHERWISE CALL ABORT.RUN GIVING 3 ''end of track reached before destination ALWAYS IF DIRECTION = .WEST '' see if westbound has arrived IF DESTINATION = SUBDIVISION(LOCATION) LET CONTINUE.FLAG = "OFF" LEAVE OTHERWISE ALWAYS IF DIRECTION = .EAST '' see if eastbound has arrived IF DESTINATION = SUBDIVISION(LOCATION) LET CONTINUE.FLAG = "OFF" LEAVE OTHERWISE ALWAYS IF DIRECTION = .WEST LET NEXT.LOCATION = LOCATION + 1 ELSE LET NEXT.LOCATION = LOCATION - 1 ALWAYS LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE START.TRAIN YIELDING LOCATION, DIRECTION, AND CONTINUE.FLAG '' Called by: train process '' Attributes are read in from a file. '' The train ID is used to set up a variable indicating direction. '' The train's place of origin is used to set up a pointer to the '' next segment of track to be entered. '' If no place of origin or destination can be found in the track '' layout, then a warning message is printed and a flag is set so that '' the train process will be terminated. DEFINE LOCATION, I, AND DIRECTION AS INTEGER VARIABLES DEFINE CONTINUE.FLAG AS A TEXT VARIABLE LET CONTINUE.FLAG = "SET" IF PROCESS IS EXTERNAL USE 7 FOR INPUT READ TRAVEL.TIME, TRN.TYPE, TRN.PTR, TRN.ID, TRN.LENGTH, HORSEPWR, TRN.ORIGIN, AND DESTINATION ELSE CALL ABORT.RUN GIVING 5 '' train schedule file incomplete ALWAYS FOR I = 1 TO N.TRACK.SEG DO IF DATA IS NOT ENDED READ SCHED.STOP(TRN.PTR,I,1) AND SCHED.STOP(TRN.PTR,I,2) ELSE CALL ABORT.RUN GIVING 5 ''train schedule file incomplete ALWAYS LOOP LET START.TIME = (TIME.V * 24) LET DIRECTION = FIND.DIRECTION(TRN.ID) '' routine start.train continued on next page '' routine start.train continued from last page FOR EACH TRACK.SEG WITH SUBDIVISION(TRACK.SEG) = DESTINATION FIND THE FIRST CASE IF NONE USE 11 FOR OUTPUT PRINT 2 LINES WITH TRN.ID AND DESTINATION THUS <<< Error: Train number **** destined for ************* <<< cannot be placed in track network >>>>> LET CONTINUE.FLAG = "OFF" '' terminate this train process USE 10 FOR OUTPUT ALWAYS FOR EACH TRACK.SEG WITH SUBDIVISION(TRACK.SEG) = TRN.ORIGIN FIND THE FIRST CASE IF FOUND LET LOCATION = P.SEGMENT(TRACK.SEG) + 1 ELSE USE 11 FOR OUTPUT PRINT 2 LINES WITH TRN.ID AND TRN.ORIGIN THUS <<< Error: Train number **** originating from ************* <<< cannot be placed in track network >>>>> LET CONTINUE.FLAG = "OFF" '' terminate this train process USE 10 FOR OUTPUT ALWAYS END ''---------------------------------------------------------------- ROUTINE DISPATCHER GIVEN TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND STAGE YIELDING LIGHT '' Called by: train process '' Calls routines: check.tracks '' d.check.tracks '' get.next.train '' extract.train '' obtain.main.track DEFINE TRN.ID, TRN.TYPE, DIRECTION, LOCATION, STAGE, AND LIGHT AS INTEGER VARIABLES DEFINE I,J, AND CHK.FLAG AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF STAGE = .ENTERING IF DOUBLE(LOCATION) = .ABSENT CALL CHECK.TRACKS GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE ELSE CALL D.CHECK.TRACKS GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE ALWAYS IF STAGE = .FROM.SIDING CALL OBTAIN.MAIN.TRACK GIVING TRN.ID, TRN.TYPE, AND LOCATION YIELDING LIGHT IF DBUG = 2 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID AND LOCATION THUS *** called Dispatcher with 'from.siding' at location ** ALWAYS LEAVE ALWAYS '' routine dispatcher continued on next page '' routine dispatcher continued from last page IF STAGE = .EXITING IF DIRECTION = .EAST AND D.QUEUE(LOCATION,1,1) > 0 AND DOUBLE(LOCATION) = 1 CALL D.GET.NEXT.TRAIN GIVING LOCATION YIELDING I CALL EXTRACT.TRAIN GIVING DIRECTION, LOCATION, AND I LET LIGHT = .GREEN LEAVE ALWAYS IF QUEUE(LOCATION,1,1) > 0 CALL GET.NEXT.TRAIN GIVING LOCATION AND DIRECTION YIELDING I CALL EXTRACT.TRAIN GIVING DIRECTION, LOCATION, AND I LET LIGHT = .GREEN LEAVE ALWAYS LET LIGHT = .GREEN LEAVE ALWAYS CALL ABORT.RUN GIVING 7 '' invalid train stage, '' not entering or leaving LOOP IF STAGE = .EXITING IF DBUG = 2 OR DBUG = 20 PRINT 1 LINES WITH TRN.ID AND LOCATION THUS *** called Dispatcher with '.exiting' at location ** ALWAYS ALWAYS IF STAGE = .ENTERING AND (DBUG = 2 OR DBUG = 20) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID AND LOCATION THUS trn *** gets Red Light from Dispatcher at location ** ALWAYS IF LIGHT = .GREEN PRINT 1 LINES WITH TRN.ID AND LOCATION THUS trn *** gets Green Light from Dispatcher at location ** ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE GET.NEXT.TRAIN GIVEN LOCATION AND DIRECTION YIELDING I '' Called by: dispatcher DEFINE LOCATION AND DIRECTION AS INTEGER VARIABLES DEFINE I, J, K, CURRENT.LOW, AND NEXT.UP AS INTEGER VARIABLES LET CURRENT.LOW = LOWEST.PRIORITY FOR J = 1 TO MAX.Q.SIZE '' initialize temp.q DO LET TEMP.QUEUE(J,1) = 0 LET TEMP.QUEUE(J,2) = 0 LOOP FOR J = 1 TO MAX.Q.SIZE '' put all eastbound trains in temp.q DO IF (QUEUE(LOCATION,J,1) > 0 AND FIND.DIRECTION( QUEUE(LOCATION,J,1) ) = .EAST) ADD 1 TO K LET TEMP.QUEUE(K,1) = QUEUE(LOCATION,J,1) LET TEMP.QUEUE(K,2) = QUEUE(LOCATION,J,2) ALWAYS LOOP IF (DIRECTION = .EAST) AND (LOCATION > 1) IF SIDING(LOCATION - 1) = .ABSENT LET K = 0 '' overide east priority to avoid deadlock in this case ALWAYS ALWAYS IF K < 1 '' no eastbound trains waiting so fill temp.q with westb FOR J = 1 TO MAX.Q.SIZE DO LET TEMP.QUEUE(J,1) = QUEUE(LOCATION,J,1) LET TEMP.QUEUE(J,2) = QUEUE(LOCATION,J,2) LOOP ALWAYS '' routine get.next.train continued on next page '' routine get.next.train continued from last page FOR J BACK FROM MAX.Q.SIZE TO 1 BY 1 '' if 2 trains had the same priority the one DO '' which arrived first should be picked IF TEMP.QUEUE(J,2) > 0 IF TEMP.QUEUE(J,2) LE CURRENT.LOW LET CURRENT.LOW = TEMP.QUEUE(J,2) LET NEXT.UP = TEMP.QUEUE(J,1) ALWAYS ALWAYS LOOP FOR J = 1 TO MAX.Q.SIZE DO IF QUEUE(LOCATION,J,1) = NEXT.UP LET I = J ALWAYS LOOP IF NEXT.UP = 0 CALL ABORT.RUN GIVING 9 '' cannot find train in queue ALWAYS END ''---------------------------------------------------------------- ROUTINE D.GET.NEXT.TRAIN GIVEN LOCATION YIELDING I '' Called by: dispatcher DEFINE LOCATION AS AN INTEGER VARIABLE DEFINE I, J, K, CURRENT.LOW AND NEXT.UP AS INTEGER VARIABLES LET CURRENT.LOW = LOWEST.PRIORITY FOR J = 1 TO MAX.Q.SIZE '' initialize temp.q DO LET D.TEMP.QUEUE(J,1) = 0 LET D.TEMP.QUEUE(J,2) = 0 LOOP FOR J = 1 TO MAX.Q.SIZE DO LET D.TEMP.QUEUE(J,1) = D.QUEUE(LOCATION,J,1) LET D.TEMP.QUEUE(J,2) = D.QUEUE(LOCATION,J,2) LOOP FOR J BACK FROM MAX.Q.SIZE TO 1 BY 1 '' if 2 trains had the same priority the one DO '' which arrived first should be picked IF D.TEMP.QUEUE(J,2) > 0 IF D.TEMP.QUEUE(J,2) LE CURRENT.LOW LET CURRENT.LOW = D.TEMP.QUEUE(J,2) LET NEXT.UP = D.TEMP.QUEUE(J,1) ALWAYS ALWAYS LOOP FOR J = 1 TO MAX.Q.SIZE DO IF D.QUEUE(LOCATION,J,1) = NEXT.UP LET I = J ALWAYS LOOP IF NEXT.UP = 0 CALL ABORT.RUN GIVING 4 '' cannot find train in double queue ALWAYS END ''---------------------------------------------------------------- ROUTINE EXTRACT.TRAIN GIVEN DIRECTION, LOCATION, AND I DEFINE DIRECTION, LOCATION, I, J, AND CHK.FLAG AS INTEGER VARIABLES '' Called by: Dispatcher '' Used for getting a train from either single or double track. LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (DIRECTION = .EAST AND DOUBLE(LOCATION) = .PRESENT) IF D.NEXT.TRAIN(LOCATION) > 0 LEAVE '' allow the other train time to restart ELSE LET D.NEXT.TRAIN(LOCATION) = D.QUEUE(LOCATION,I,1) FOR J = I TO (MAX.Q.SIZE - 1) DO LET D.QUEUE(LOCATION,J,1) = D.QUEUE(LOCATION,J+1,1) LET D.QUEUE(LOCATION,J,2) = D.QUEUE(LOCATION,J+1,2) LOOP LET D.QUEUE(LOCATION,MAX.Q.SIZE,1) = 0 LET D.QUEUE(LOCATION,MAX.Q.SIZE,2) = 0 LEAVE ALWAYS IF NEXT.TRAIN(LOCATION) > 0 LEAVE '' allow the other train time to restart ALWAYS LET NEXT.TRAIN(LOCATION) = QUEUE(LOCATION,I,1) FOR J = I TO (MAX.Q.SIZE - 1) DO LET QUEUE(LOCATION,J,1) = QUEUE(LOCATION,J+1,1) LET QUEUE(LOCATION,J,2) = QUEUE(LOCATION,J+1,2) LOOP LET QUEUE(LOCATION,MAX.Q.SIZE,1) = 0 LET QUEUE(LOCATION,MAX.Q.SIZE,2) = 0 LEAVE LOOP IF DBUG = 3 OR DBUG = 20 PRINT 1 LINES WITH NEXT.TRAIN(LOCATION) AND LOCATION THUS train *** signalled next in extract routine, location ** ALWAYS END ''---------------------------------------------------------------- ROUTINE CHECK.TRACKS GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: dispatcher '' Calls routines: track.without.siding '' last.track.segment '' empty.track.empty.siding '' empty.track.full.siding '' full.track.empty.siding DEFINE TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND LIGHT AS INTEGER VARIABLES DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET CHK.FLAG = 1 LET LIGHT = .RED WHILE CHK.FLAG = 1 DO '' track.without.siding '' --->-|--??--| or |--??--|-<--- IF SIDING(LOCATION) = .ABSENT CALL TRACK.WITHOUT.SIDING GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE OTHERWISE '' last.track.segment '' |------| |------| '' --->-|--??--|| or ||--??--|-<--- IF (DIRECTION = .WEST AND LOCATION = N.TRACK.SEG) OR (DIRECTION = .EAST AND LOCATION = 1) CALL LAST.TRACK.SEGMENT GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE OTHERWISE '' empty.track.empty.siding '' |------| |------| '' --->-|------| or |------|-<--- IF TRACK.STATUS(LOCATION) = 0 AND SIDING.STATUS(LOCATION) = 0 CALL EMPTY.TRACK.EMPTY.SIDING GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE OTHERWISE '' routine check.tracks continued on next page '' routine check.tracks continued from last page '' empty.track.full.siding - eastbound '' |--==--| '' --->-|------| IF TRACK.STATUS(LOCATION) = 0 AND SIDING.STATUS(LOCATION) > 0 IF DIRECTION = .EAST CALL EMPTY.TRACK.FULL.SIDING GIVING TRN.ID, TRN.TYPE, AND LOCATION YIELDING LIGHT LEAVE ELSE LET LIGHT = .RED LEAVE OTHERWISE '' full.track.empty.siding '' |------| |------| '' --->-|--==--| or |--==--|-<--- IF TRACK.STATUS(LOCATION) > 0 AND SIDING.STATUS(LOCATION) = 0 CALL FULL.TRACK.EMPTY.SIDING GIVING TRN.ID, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE OTHERWISE '' full.track.full.siding '' |--==--| |--==--| '' --->-|--==--| or |--==--|-<--- IF TRACK.STATUS(LOCATION) > 0 AND SIDING.STATUS(LOCATION) > 0 LET LIGHT = .RED LEAVE OTHERWISE LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE LAST.TRACK.SEGMENT GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: check.tracks '' check.tracks takes care of the possibility of no siding before this point DEFINE TRN.ID, TRN.TYPE, DIRECTION, LOCATION, LIGHT, AND CHK.FLAG AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF TRACK.STATUS(LOCATION) = 0 AND SIDING.STATUS(LOCATION) = 0 LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN '' |------| |------| '' --->-|------| or |------|-<--- LEAVE ALWAYS IF TRACK.STATUS(LOCATION) > 0 AND SIDING.STATUS(LOCATION) = 0 LET SIDING.STATUS(LOCATION) = TRN.ID LET LIGHT = .GREEN '' |------| |------| '' --->-|--==--| or |--==--|-<--- LEAVE ALWAYS IF TRACK.STATUS(LOCATION) = 0 AND SIDING.STATUS(LOCATION) > 0 AND ( DIRECTION <> FIND.DIRECTION(SIDING.STATUS(LOCATION)) ) LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN '' |-<==--| |--==>-| '' --->-|------| or |------|-<--- LEAVE ALWAYS LET LIGHT = .RED '' wait for track or siding to clear LEAVE '' in all other cases LOOP IF ( (DBUG = 5) OR (DBUG = 20) ) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID THUS *** gets Red Light from R - last.track.segment ELSE PRINT 1 LINES WITH TRN.ID THUS *** gets Green Light from R - last.track.segment ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE TRACK.WITHOUT.SIDING GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: check.tracks DEFINE TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND LIGHT AS INTEGER VARIABLES DEFINE GO.FLAG AS A TEXT VARIABLE DEFINE GO.FLAG.SET TO MEAN GO.FLAG = "SET" DEFINE GO.FLAG.OFF TO MEAN GO.FLAG = "OFF" DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET CHK.FLAG = 1 LET GO.FLAG = "OFF" WHILE CHK.FLAG = 1 DO IF TRACK.STATUS(LOCATION) > 0 '' track occupied ? LEAVE '' |--==--| ALWAYS IF DIRECTION = .EAST '' track clear ahead? IF (TRACK.STATUS(LOCATION - 1) = 0 '' |-----| AND SIDING.STATUS(LOCATION - 1) = 0 ) '' |---->|-----|-----| LET GO.FLAG = "SET" LEAVE ALWAYS ALWAYS IF DIRECTION = .WEST '' track clear ahead? IF (TRACK.STATUS(LOCATION + 1) = 0 '' |-----| AND SIDING.STATUS(LOCATION + 1) = 0 ) '' |-----|-----|<----| LET GO.FLAG = "SET" LEAVE ALWAYS ALWAYS IF (DIRECTION = .WEST) AND (LOCATION > 1) '' is westbound stalling east in last seg? '' |--==>--| '' |--<==--|---------|----<---| IF SIDING(LOCATION - 1) > 0 AND SIDING.STATUS(LOCATION - 1) > 0 AND FIND.DIRECTION(SIDING.STATUS(LOCATION - 1)) = .EAST LET GO.FLAG = "SET" LEAVE ALWAYS ALWAYS '' routine track.without.siding continued on next page '' routine track.without.siding continued from last page IF DIRECTION = .WEST '' oncoming trains for westbound? IF ( (TRACK.STATUS(LOCATION + 1) > 0) AND ( FIND.DIRECTION(TRACK.STATUS(LOCATION + 1)) = .EAST) ) OR ( (SIDING.STATUS(LOCATION + 1) > 0) ) '' |---==--| |---------| '' |---==>-|---------|----<----| LEAVE ELSE LET GO.FLAG = "SET" LEAVE ALWAYS IF DIRECTION = .EAST '' let eastbound go ahead to avoid deadlock IF ( (SIDING.STATUS(LOCATION - 1) > 0) ) LEAVE ELSE LET GO.FLAG = "SET" LEAVE ALWAYS LEAVE LOOP IF GO.FLAG.SET LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN ELSE LET LIGHT = .RED ALWAYS IF ( (DBUG = 5) OR (DBUG = 20) ) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID THUS *** gets Red Light from R - track.without.siding ELSE PRINT 1 LINES WITH TRN.ID THUS *** gets Green Light from R - track.without.siding ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE EMPTY.TRACK.EMPTY.SIDING GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: check.tracks DEFINE TRN.ID, TRN.TYPE, DIRECTION, LOCATION, AND LIGHT AS INTEGER VARIABLES DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET CHK.FLAG = 1 LET LIGHT = .RED WHILE CHK.FLAG = 1 DO IF (DIRECTION = .EAST) AND (SIDING(LOCATION - 1) = .ABSENT) AND (QUEUE(LOCATION - 1, 1,1) > 0 ) '' --|-------| |----==>---| '' --->---|-------|-------|----<==---| '' == LEAVE '' let the dust settle ahead ALWAYS IF DIRECTION = .WEST AND TRACK.STATUS(LOCATION + 1) > 0 IF FIND.DIRECTION(TRACK.STATUS(LOCATION + 1)) = .EAST LET SIDING.STATUS(LOCATION) = TRN.ID LET LIGHT = .GREEN LET MEET(LOCATION) = TRACK.STATUS(LOCATION + 1) LEAVE '' |-------| '' |--==>--|-------|-<-----| '' Eastbound train has superiority so put '' westbound in siding. '' The train can leave the siding when eastbound '' trips the MEET flag on exiting. ALWAYS ALWAYS '' routine empty.track.empty siding continued on next page '' routine empty.track.empty siding continued from last page '' avoid conjestion IF DIRECTION = .WEST AND TRACK.STATUS(LOCATION + 1) > 0 AND FIND.DIRECTION(TRACK.STATUS(LOCATION + 1)) =.WEST '' |-------| '' |--<==--|-------|-<-----| LEAVE ALWAYS IF DIRECTION = .EAST AND TRACK.STATUS(LOCATION - 1) > 0 AND FIND.DIRECTION(TRACK.STATUS(LOCATION - 1)) =.EAST '' |-------| '' |---->--|-------|--==>--| LEAVE ALWAYS LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE '' |-------| '' |---->--|-------|-------| '' |-------| '' |-------|-------|-<-----| LOOP IF ( (DBUG = 5) OR (DBUG = 20) ) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID THUS *** gets Red Light from R - empty.track.empty.siding ELSE PRINT 1 LINES WITH TRN.ID THUS *** gets Green Light from R - empty.track.empty.siding ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE EMPTY.TRACK.FULL.SIDING GIVEN TRN.ID, TRN.TYPE, AND LOCATION YIELDING LIGHT '' Called by: check.tracks '' NOTE: **** called only for eastbound trains **** DEFINE TRN.ID, TRN.TYPE, LOCATION, LIGHT, AND CHK.FLAG AS INTEGER VARIABLES DEFINE GO.FLAG AS A TEXT VARIABLE LET GO.FLAG = "OFF" LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (FIND.DIRECTION(SIDING.STATUS(LOCATION)) = .WEST) AND (LOCATION > 1) IF (SIDING(LOCATION - 1) = .ABSENT) AND (TRACK.STATUS(LOCATION - 1) > 0) LEAVE ALWAYS '' |--<==--| '' |---->--|-------|--<==--| '' prevent deadlock '' |--<==--| '' |---->--|-------|--==>--| ALWAYS IF FIND.DIRECTION(SIDING.STATUS(LOCATION)) = .WEST LET GO.FLAG = "SET" LEAVE '' |--<==--| '' |---->--|-------| ALWAYS LEAVE LOOP IF GO.FLAG = "SET" LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN ELSE LET LIGHT = .RED ALWAYS IF ( (DBUG = 5) OR (DBUG = 20) ) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID THUS *** gets Red Light from R - empty.track.full.siding ELSE PRINT 1 LINES WITH TRN.ID THUS *** gets Green Light from R - empty.track.full.siding ALWAYS ALWAYS END ''---------------------------------------------------------------- ROUTINE FULL.TRACK.EMPTY.SIDING GIVEN TRN.ID, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: check.tracks DEFINE TRN.ID, DIRECTION, LOCATION, AND LIGHT AS INTEGER VARIABLES DEFINE GO.FLAG AS A TEXT VARIABLE DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET GO.FLAG = "OFF" LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF DIRECTION = FIND.DIRECTION(TRACK.STATUS(LOCATION)) IF ( DIRECTION = .EAST AND TRACK.STATUS(LOCATION - 1) = 0 ) LEAVE '' |-------| '' |---->--|--==>--|-------| ELSE IF DIRECTION = .WEST AND TRACK.STATUS(LOCATION + 1) = 0 LEAVE '' |-------| '' |-------|--<==--|--<----| ALWAYS ALWAYS IF (DIRECTION = .EAST) AND (LOCATION < N.TRACK.SEG) IF (QUEUE(LOCATION + 1,1,1) > 0) AND FIND.DIRECTION(QUEUE(LOCATION + 1,1,1)) = .WEST LET GO.FLAG = "SET" LEAVE ALWAYS ALWAYS IF (DIRECTION = .EAST) AND (LOCATION < N.TRACK.SEG) IF MEET(LOCATION + 1) = TRN.ID LET GO.FLAG = "SET" LEAVE ALWAYS ALWAYS IF (DIRECTION = .EAST) AND (LOCATION < N.TRACK.SEG) IF FIND.DIRECTION(TRACK.STATUS(LOCATION)) = .WEST AND SIDING(LOCATION + 1) = .ABSENT LET GO.FLAG = "SET" LEAVE '' |-------| '' |---->--|--<==--|-------| '' |-------| '' |---->--|--<==--|--==>--| '' |-------| '' |---->--|--<==--|--<==--| ELSE LEAVE '' |-------| '' |---->--|--==>--|--==>--| '' |-------| '' |---->--|--==>--|--<==--| ALWAYS IF DIRECTION = .WEST IF FIND.DIRECTION(TRACK.STATUS(LOCATION)) = .EAST LET GO.FLAG = "SET" LEAVE '' |-------| '' |-------|--==>--|--<----| '' |-------| '' |--==>--|--==>--|--<----| '' |-------| '' |--<==--|--==>--|--<----| ELSE LEAVE '' |-------| '' |--<==--|--<==--|--<----| '' |-------| '' |--==>--|--<==--|--<----| ALWAYS LEAVE LOOP IF GO.FLAG = "SET" LET SIDING.STATUS(LOCATION) = TRN.ID LET LIGHT = .GREEN ELSE LET LIGHT = .RED ALWAYS IF ( (DBUG = 5) OR (DBUG = 20) ) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID THUS *** gets Red Light from R - full.track.empty.siding ELSE PRINT 1 LINES WITH TRN.ID THUS *** gets Green Light from R - full.track.empty.siding ALWAYS ALWAYS END ''-------------------------------------------------------------------- ROUTINE D.CHECK.TRACKS GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: dispatcher '' Calls routines: d.track.westbound '' d.track.eastbound DEFINE TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION AS INTEGER VARIABLES DEFINE LIGHT, AND CHK.FLAG AS INTEGER VARIABLES LET LIGHT = .RED '' default value LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO '' First check to see if train is passed the first 2 segments '' of its journey because the double routines look back and '' put the train in a siding (if available) to allow a train '' of higher priority to overtake. IF DIRECTION = .WEST IF LOCATION > 2 CALL D.TRACK.WESTBOUND GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE ALWAYS ELSE '' eastbound IF LOCATION < (N.TRACK.SEG - 1) CALL D.TRACK.EASTBOUND GIVING TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT LEAVE ALWAYS ALWAYS '' routine d.check.tracks continued on next page '' routine d.check.tracks continued from last page '' Start train off from either terminus IF DIRECTION = .EAST IF DOUBLE.STATUS(LOCATION) = 0 LET DOUBLE.STATUS(LOCATION) = TRN.ID LET DOUBLE.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS ELSE '' westbound IF TRACK.STATUS(LOCATION) = 0 LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS ALWAYS LEAVE LOOP IF ( (DBUG = 5) OR (DBUG = 20) ) IF LIGHT = .RED PRINT 1 LINES WITH TRN.ID THUS *** gets Red Light from R - d.check.tracks ELSE PRINT 1 LINES WITH TRN.ID THUS *** gets Green Light from R - d.check.tracks ALWAYS ALWAYS END ''-------------------------------------------------------------------- ROUTINE D.TRACK.WESTBOUND GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: d.check.tracks DEFINE TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION AS INTEGER VARIABLES DEFINE LIGHT, AND CHK.FLAG AS INTEGER VARIABLES LET LIGHT = .RED '' default value LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF SIDING(LOCATION) = 0 '' no siding ? IF TRACK.STATUS(LOCATION) = 0 LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS LEAVE ALWAYS '' train following has greater priority; go into siding if possible IF (TRACK.STATUS(LOCATION - 2) > 0 AND (TRACK.ENTRY.PRIORITY(LOCATION - 2) < TRN.TYPE) AND (FIND.DIRECTION(TRACK.STATUS(LOCATION - 2)) = DIRECTION)) IF SIDING.STATUS(LOCATION) = 0 LET SIDING.STATUS(LOCATION) = TRN.ID LET LIGHT = .GREEN LET OVERTAKE(LOCATION) = TRACK.STATUS(LOCATION - 2) '' overtake flag will be tripped when train passes LEAVE ALWAYS ALWAYS '' otherwise carry on if possible IF TRACK.STATUS(LOCATION) = 0 LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS LEAVE LOOP IF ( (DBUG = 5) OR (DBUG = 20) ) PRINT 1 LINES WITH TRN.ID THUS *** in R - d.track.westbound ALWAYS END ''-------------------------------------------------------------------- ROUTINE D.TRACK.EASTBOUND GIVEN TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION YIELDING LIGHT '' Called by: d.check.tracks DEFINE TRN.ID, TRN.TYPE, DIRECTION, AND LOCATION AS INTEGER VARIABLES DEFINE LIGHT, AND CHK.FLAG AS INTEGER VARIABLES LET CHK.FLAG = 1 LET LIGHT = .RED '' default value WHILE CHK.FLAG = 1 DO IF D.SIDING(LOCATION) = 0 '' no siding ? IF DOUBLE.STATUS(LOCATION) = 0 LET DOUBLE.STATUS(LOCATION) = TRN.ID LET DOUBLE.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS LEAVE ALWAYS '' Case of double track present in prior segment '' train following has greater priority; go into siding if possible IF DOUBLE(LOCATION + 2) = 1 AND DOUBLE.STATUS(LOCATION + 2) > 0 AND DOUBLE.ENTRY.PRIORITY(LOCATION + 2) < TRN.TYPE AND D.SIDING.STATUS(LOCATION) = 0 LET D.SIDING.STATUS(LOCATION) = TRN.ID LET LIGHT = .GREEN LET D.OVERTAKE(LOCATION) = DOUBLE.STATUS(LOCATION + 2) '' overtake flag will be tripped when train passes LEAVE ALWAYS '' Case of single track in prior segment '' train following has greater priority; go into siding if possible IF DOUBLE(LOCATION + 2) = 0 AND TRACK.STATUS(LOCATION + 2) > 0 AND (TRACK.ENTRY.PRIORITY(LOCATION + 2) < TRN.TYPE) AND (FIND.DIRECTION(TRACK.STATUS(LOCATION + 2)) = DIRECTION) AND D.SIDING.STATUS(LOCATION) = 0 LET D.SIDING.STATUS(LOCATION) = TRN.ID LET LIGHT = .GREEN LET D.OVERTAKE(LOCATION) = DOUBLE.STATUS(LOCATION + 2) '' overtake flag will be tripped when train passes LEAVE ALWAYS '' In other cases, carry on when possible IF DOUBLE.STATUS(LOCATION) = 0 LET DOUBLE.STATUS(LOCATION) = TRN.ID LET DOUBLE.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS LEAVE LOOP IF ( (DBUG = 5) OR (DBUG = 20) ) PRINT 1 LINES WITH TRN.ID THUS *** in R - d.track.eastbound ALWAYS END ''---------------------------------------------------------------- ROUTINE OBTAIN.MAIN.TRACK GIVEN TRN.ID, TRN.TYPE, AND LOCATION YIELDING LIGHT DEFINE TRN.ID, TRN.TYPE, LOCATION, AND LIGHT AS INTEGER VARIABLES DEFINE CHK.FLAG AS AN INTEGER VARIABLE LET CHK.FLAG = 1 LET LIGHT = .RED WHILE CHK.FLAG = 1 DO IF ( (DOUBLE(LOCATION) = .PRESENT) AND (DIRECTION = .EAST) ) '' eastbound on double ? IF DOUBLE.STATUS(LOCATION) = 0 LET DOUBLE.STATUS(LOCATION) = TRN.ID LET DOUBLE.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS LEAVE ALWAYS IF TRACK.STATUS(LOCATION) = 0 LET TRACK.STATUS(LOCATION) = TRN.ID LET TRACK.ENTRY.PRIORITY(LOCATION) = TRN.TYPE LET LIGHT = .GREEN LEAVE ALWAYS LEAVE LOOP IF LIGHT = .RED AND (DBUG = 4 OR DBUG = 20) PRINT 1 LINES WITH TRN.ID AND LOCATION THUS in obtain.main.track trn *** location ** gets Red Light ALWAYS IF LIGHT = .GREEN AND (DBUG = 4 OR DBUG = 20) PRINT 1 LINES WITH TRN.ID AND LOCATION THUS in obtain.main.track trn *** location ** gets Green Light ALWAYS IF DBUG = 4 OR DBUG = 20 PRINT 1 LINES WITH TRACK.STATUS(LOCATION) THUS Train on main track is *** ALWAYS END ''---------------------------------------------------------------- ROUTINE DISPLAY.SEGMENT GIVEN LOCATION DEFINE LOCATION, CHK.FLAG, AND I AS INTEGER VARIABLES DEFINE DISP.START AS A REAL VARIABLE LET DISP.START = TIME.V * 24 LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO PRINT 2 LINES WITH LOCATION AND PRINTIT(DISP.START) THUS Track Segment ** at time ***** CALL D.DISPLAY.SEGMENT GIVING LOCATION '' any double to display ? PRINT 3 LINES WITH LOCATION THUS ^^^ ** vvv IF LOCATION = 1 CALL DISPLAY.EAST.END GIVING LOCATION LEAVE ALWAYS IF LOCATION = N.TRACK.SEG CALL DISPLAY.WEST.END GIVING LOCATION LEAVE ALWAYS CALL DISPLAY.SIDINGS GIVING LOCATION PRINT 1 LINES WITH TRACK.STATUS(LOCATION + 1), TRACK.STATUS(LOCATION), AND TRACK.STATUS(LOCATION - 1) THUS < West -------- *** -------- ========= *** ========= ------- *** ------ IF QUEUE(LOCATION + 1,1,1) = 0 AND QUEUE(LOCATION,1,1) = 0 AND QUEUE(LOCATION - 1,1,1) = 0 LEAVE ALWAYS PRINT 1 LINES THUS Queued Trains: FOR I = 1 TO MAX.Q.SIZE UNTIL QUEUE(LOCATION - 1,I,1) = 0 AND QUEUE(LOCATION,I,1) = 0 AND QUEUE(LOCATION + 1,I,1) = 0 DO PRINT 1 LINES WITH QUEUE(LOCATION + 1, I,1), QUEUE(LOCATION, I,1), AND QUEUE(LOCATION - 1,I,1) THUS *** *** *** LOOP LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE DISPLAY.SIDINGS GIVEN LOCATION DEFINE LOCATION AND CHK.FLAG AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (SIDING(LOCATION + 1) = .ABSENT) AND (SIDING(LOCATION) = .ABSENT) AND (SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION - 1) THUS --- *** --- LEAVE ALWAYS IF (SIDING(LOCATION + 1) = .ABSENT) AND (SIDING(LOCATION) = .PRESENT) AND (SIDING(LOCATION - 1) = .ABSENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION) THUS --- *** --- LEAVE ALWAYS IF (SIDING(LOCATION + 1) = .ABSENT) AND (SIDING(LOCATION) = .PRESENT) AND (SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION) AND SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- LEAVE ALWAYS IF (SIDING(LOCATION - 1 ) = .ABSENT) AND (SIDING(LOCATION) = .ABSENT) AND (SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION + 1) THUS --- *** --- LEAVE ALWAYS IF (SIDING(LOCATION - 1 ) = .PRESENT) AND (SIDING(LOCATION) = .ABSENT) AND (SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION + 1) AND SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- LEAVE ALWAYS '' routine display.sidings continued on next page '' routine display.sidings continued from last page IF (SIDING(LOCATION + 1 ) = .PRESENT) AND (SIDING(LOCATION) = .PRESENT) AND (SIDING(LOCATION - 1) = .ABSENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION + 1) AND SIDING.STATUS(LOCATION) THUS --- *** --- --- *** --- LEAVE ALWAYS IF (SIDING(LOCATION + 1 ) = .PRESENT) AND (SIDING(LOCATION) = .PRESENT) AND (SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION + 1), SIDING.STATUS(LOCATION), AND SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- --- *** --- LEAVE ALWAYS LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE DISPLAY.EAST.END GIVEN LOCATION DEFINE LOCATION, CHK.FLAG, AND I AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (SIDING(LOCATION) = .ABSENT) AND (SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION + 1) THUS --- *** --- ELSE IF (SIDING(LOCATION + 1) = .ABSENT) AND (SIDING(LOCATION) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION) THUS --- *** --- ELSE IF (SIDING(LOCATION) = .PRESENT) AND (SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION + 1) AND SIDING.STATUS(LOCATION) THUS --- *** --- --- *** --- ALWAYS ALWAYS ALWAYS PRINT 1 LINES WITH TRACK.STATUS(LOCATION + 1) AND TRACK.STATUS(LOCATION) THUS --------- *** --------- ========= *** ========== East > IF QUEUE(LOCATION + 1,1,1) = 0 AND QUEUE(LOCATION,1,1) = 0 LEAVE ALWAYS PRINT 1 LINES THUS Queued Trains: FOR I = 1 TO MAX.Q.SIZE UNTIL QUEUE(LOCATION + 1,I,1) = 0 AND QUEUE(LOCATION,I,1) = 0 DO PRINT 1 LINES WITH QUEUE(LOCATION + 1, I,1) AND QUEUE(LOCATION, I,1) THUS *** *** LOOP LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE DISPLAY.WEST.END GIVEN LOCATION DEFINE LOCATION, CHK.FLAG, AND I AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (SIDING(LOCATION) = .ABSENT) AND (SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION - 1) THUS --- *** --- ELSE IF (SIDING(LOCATION - 1) = .ABSENT) AND (SIDING(LOCATION) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION) THUS --- *** --- ELSE IF (SIDING(LOCATION) = .PRESENT) AND (SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH SIDING.STATUS(LOCATION) AND SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- ALWAYS ALWAYS ALWAYS PRINT 1 LINES WITH TRACK.STATUS(LOCATION) AND TRACK.STATUS(LOCATION - 1) THUS < West ========= *** ========= --------- *** ---- IF QUEUE(LOCATION - 1,1,1) = 0 AND QUEUE(LOCATION,1,1) = 0 LEAVE ALWAYS PRINT 1 LINES THUS Queued Trains: FOR I = 1 TO MAX.Q.SIZE UNTIL QUEUE(LOCATION - 1,I,1) = 0 AND QUEUE(LOCATION,I,1) = 0 DO PRINT 1 LINES WITH QUEUE(LOCATION, I,1) AND QUEUE(LOCATION - 1, I,1) THUS *** *** LOOP LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE D.DISPLAY.SEGMENT GIVEN LOCATION DEFINE LOCATION, CHK.FLAG, AND I AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF LOCATION = 1 IF (DOUBLE(1) = .PRESENT OR DOUBLE(2) = .PRESENT) CALL D.DISPLAY.EAST.END GIVING LOCATION LEAVE ALWAYS LEAVE ALWAYS IF LOCATION = N.TRACK.SEG IF (DOUBLE(N.TRACK.SEG) = .PRESENT OR DOUBLE(N.TRACK.SEG - 1) = .PRESENT) CALL D.DISPLAY.WEST.END GIVING LOCATION LEAVE ALWAYS LEAVE ALWAYS IF (DOUBLE(LOCATION - 1) = .PRESENT OR DOUBLE(LOCATION) = .PRESENT OR DOUBLE(LOCATION + 1) = .PRESENT) CALL D.DISPLAY.SIDINGS GIVING LOCATION ELSE LEAVE ALWAYS CALL D.DISPLAY.TRACK GIVING LOCATION IF (D.QUEUE(LOCATION + 1,1,1) = 0 AND D.QUEUE(LOCATION,1,1) = 0 AND D.QUEUE(LOCATION - 1,1,1) = 0) LEAVE ALWAYS PRINT 1 LINES THUS Queued Trains for eastbound double track: FOR I = 1 TO MAX.Q.SIZE UNTIL (D.QUEUE(LOCATION - 1,I,1) = 0 AND D.QUEUE(LOCATION,I,1) = 0 AND D.QUEUE(LOCATION + 1,I,1) = 0) DO PRINT 1 LINES WITH D.QUEUE(LOCATION + 1, I,1), D.QUEUE(LOCATION, I,1), AND D.QUEUE(LOCATION - 1,I,1) THUS *** *** *** LOOP LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE D.DISPLAY.TRACK GIVEN LOCATION DEFINE LOCATION AND CHK.FLAG AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (DOUBLE(LOCATION + 1) = .ABSENT) AND (DOUBLE(LOCATION) = .ABSENT) AND (DOUBLE(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION - 1) THUS ~~~~~~~ *** ~~~~~~ LEAVE ALWAYS IF (DOUBLE(LOCATION - 1) = .ABSENT) AND (DOUBLE(LOCATION) = .PRESENT) AND (DOUBLE(LOCATION + 1) = .ABSENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION) THUS +++++++++ *** +++++++++ LEAVE ALWAYS IF (DOUBLE(LOCATION + 1) = .ABSENT) AND (DOUBLE(LOCATION) = .PRESENT) AND (DOUBLE(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION) AND DOUBLE.STATUS(LOCATION - 1) THUS +++++++++ *** +++++++++ ~~~~~~~ *** ~~~~~~ LEAVE ALWAYS IF (DOUBLE(LOCATION - 1 ) = .ABSENT) AND (DOUBLE(LOCATION) = .ABSENT) AND (DOUBLE(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION + 1) THUS ~~~~~~~~ *** ~~~~~~~~ LEAVE ALWAYS IF (DOUBLE(LOCATION - 1 ) = .PRESENT) AND (DOUBLE(LOCATION) = .ABSENT) AND (DOUBLE(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION + 1) AND DOUBLE.STATUS(LOCATION - 1) THUS ~~~~~~~~ *** ~~~~~~~~ ~~~~~~~ *** ~~~~~~ LEAVE ALWAYS '' routine d.display.track continued on next page '' routine d.display.track continued from last page IF (DOUBLE(LOCATION + 1 ) = .PRESENT) AND (DOUBLE(LOCATION) = .PRESENT) AND (DOUBLE(LOCATION - 1) = .ABSENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION + 1) AND DOUBLE.STATUS(LOCATION) THUS ~~~~~~~~ *** ~~~~~~~~ +++++++++ *** +++++++++ LEAVE ALWAYS IF (DOUBLE(LOCATION + 1 ) = .PRESENT) AND (DOUBLE(LOCATION) = .PRESENT) AND (DOUBLE(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION + 1), DOUBLE.STATUS(LOCATION), AND DOUBLE.STATUS(LOCATION - 1) THUS ~~~~~~~~ *** ~~~~~~~~ +++++++++ *** +++++++++ ~~~~~~~ *** ~~~~~~ LEAVE ALWAYS LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE D.DISPLAY.SIDINGS GIVEN LOCATION DEFINE LOCATION AND CHK.FLAG AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (D.SIDING(LOCATION + 1) = .ABSENT) AND (D.SIDING(LOCATION) = .ABSENT) AND (D.SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION - 1) THUS --- *** --- LEAVE ALWAYS IF (D.SIDING(LOCATION - 1) = .ABSENT) AND (D.SIDING(LOCATION) = .PRESENT) AND (D.SIDING(LOCATION + 1) = .ABSENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION) THUS --- *** --- LEAVE ALWAYS IF (D.SIDING(LOCATION + 1) = .ABSENT) AND (D.SIDING(LOCATION) = .PRESENT) AND (D.SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION) AND D.SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- LEAVE ALWAYS IF (D.SIDING(LOCATION - 1 ) = .ABSENT) AND (D.SIDING(LOCATION) = .ABSENT) AND (D.SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION + 1) THUS --- *** --- LEAVE ALWAYS IF (D.SIDING(LOCATION - 1 ) = .PRESENT) AND (D.SIDING(LOCATION) = .ABSENT) AND (D.SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION + 1) AND D.SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- LEAVE ALWAYS '' routine d.display.sidings continued on next page '' routine d.display.sidings continued from last page IF (D.SIDING(LOCATION + 1 ) = .PRESENT) AND (D.SIDING(LOCATION) = .PRESENT) AND (D.SIDING(LOCATION - 1) = .ABSENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION + 1) AND D.SIDING.STATUS(LOCATION) THUS --- *** --- --- *** --- LEAVE ALWAYS IF (D.SIDING(LOCATION + 1 ) = .PRESENT) AND (D.SIDING(LOCATION) = .PRESENT) AND (D.SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION + 1), D.SIDING.STATUS(LOCATION), AND D.SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- --- *** --- LEAVE ALWAYS LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE D.DISPLAY.EAST.END GIVEN LOCATION DEFINE LOCATION, CHK.FLAG, AND I AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (D.SIDING(LOCATION) = .ABSENT) AND (D.SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION + 1) THUS --- *** --- ELSE IF (D.SIDING(LOCATION + 1) = .ABSENT) AND (D.SIDING(LOCATION) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION) THUS --- *** --- ELSE IF (D.SIDING(LOCATION) = .PRESENT) AND (D.SIDING(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION + 1) AND D.SIDING.STATUS(LOCATION) THUS --- *** --- --- *** --- ALWAYS ALWAYS ALWAYS IF (DOUBLE(LOCATION) = .ABSENT) AND (DOUBLE(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION + 1) THUS ~~~~~~~~ *** ~~~~~~~~ ELSE IF (DOUBLE(LOCATION + 1) = .ABSENT) AND (DOUBLE(LOCATION) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION) THUS +++++++++ *** +++++++++ ELSE IF (DOUBLE(LOCATION) = .PRESENT) AND (DOUBLE(LOCATION + 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION + 1) AND DOUBLE.STATUS(LOCATION) THUS ~~~~~~~~ *** ~~~~~~~~ +++++++++ *** +++++++++ ALWAYS ALWAYS ALWAYS IF D.QUEUE(LOCATION + 1,1,1) = 0 AND D.QUEUE(LOCATION,1,1) = 0 LEAVE ALWAYS '' routine d.display.east.end continued on next page '' routine d.display.east.end continued from last page PRINT 1 LINES THUS Queued Trains for eastbound double track: FOR I = 1 TO MAX.Q.SIZE UNTIL (D.QUEUE(LOCATION + 1,I,1) = 0 AND D.QUEUE(LOCATION,I,1) = 0) DO PRINT 1 LINES WITH D.QUEUE(LOCATION + 1, I,1) AND D.QUEUE(LOCATION, I,1) THUS *** *** LOOP LEAVE LOOP END ''---------------------------------------------------------------- ROUTINE D.DISPLAY.WEST.END GIVEN LOCATION DEFINE LOCATION, CHK.FLAG, AND I AS INTEGER VARIABLES LET CHK.FLAG = 1 WHILE CHK.FLAG = 1 DO IF (D.SIDING(LOCATION) = .ABSENT) AND (D.SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION - 1) THUS --- *** --- ELSE IF (D.SIDING(LOCATION - 1) = .ABSENT) AND (D.SIDING(LOCATION) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION) THUS --- *** --- ELSE IF (D.SIDING(LOCATION) = .PRESENT) AND (D.SIDING(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH D.SIDING.STATUS(LOCATION) AND D.SIDING.STATUS(LOCATION - 1) THUS --- *** --- --- *** --- ALWAYS ALWAYS ALWAYS IF (DOUBLE(LOCATION) = .ABSENT) AND (DOUBLE(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION - 1) THUS ~~~~~~~ *** ~~~~~~ ELSE IF (DOUBLE(LOCATION - 1) = .ABSENT) AND (DOUBLE(LOCATION) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION) THUS +++++++++ *** +++++++++ ELSE IF (DOUBLE(LOCATION) = .PRESENT) AND (DOUBLE(LOCATION - 1) = .PRESENT) PRINT 1 LINES WITH DOUBLE.STATUS(LOCATION) AND DOUBLE.STATUS(LOCATION - 1) THUS +++++++++ *** +++++++++ ~~~~~~~ *** ~~~~~~ ALWAYS ALWAYS ALWAYS IF (D.QUEUE(LOCATION - 1,1,1) = 0 AND D.QUEUE(LOCATION,1,1) = 0) LEAVE ALWAYS '' routine d.display.west.end continued on next page '' routine d.display.west.end continued from last page PRINT 1 LINES THUS Queued Trains for eastbound double track: FOR I = 1 TO MAX.Q.SIZE UNTIL D.QUEUE(LOCATION - 1,I,1) = 0 AND D.QUEUE(LOCATION,I,1) = 0 DO PRINT 1 LINES WITH D.QUEUE(LOCATION, I,1) AND D.QUEUE(LOCATION - 1, I,1) THUS *** *** LOOP LEAVE LOOP END ''---------------------------------------------------------------- PROCESS WINDUP DEFINE I AS AN INTEGER VARIABLE FOR I = 1 TO N.TRACK.SEG IF (QUEUE(I,1,1) > 0) OR (D.QUEUE(I,1,1) > 0) CALL ABORT.RUN GIVING 6 '' trains in queue after end of run ALWAYS USE 11 FOR OUTPUT PRINT 3 LINE THUS TRAIN SIMULATION RUN STATISTICS PRINT 5 LINES WITH PRINTIT(MEAN.ELAPSED), PRINTIT(MIN.ELAPSED), AND PRINTIT(MAX.ELAPSED) THUS The average elapsed time was ***** hours The shortest elapsed time was ***** hours The longest elapsed time was ***** hours PRINT 4 LINES WITH N.TRACK.SEG, NUM.TRAINS, AND WAITING.TRAINS THUS The total number of track segments was *** The number of trains in this run was *** The number of trains waiting was *** STOP END ''---------------------------------------------------------------- ROUTINE ABORT.RUN GIVEN ERROR.CODE DEFINE ERROR.CODE AS AN INTEGER VARIABLE '' Error | From Routine | Message '' Code | or Process | '' ------|---------------------|-------------------------------------------- '' 1 | STARTUP | train track file empty or incomplete '' 2 | QUEUE.TRAIN | inadequate queue length '' 3 |POINT.TO.NEXT.SEGMENT| end of track reached before destination '' 4 | D.GET.NEXT.TRAIN | train not found in double track queue '' 5 | START.TRAIN | train schedule file incomplete '' 6 | WINDUP | trains in queue after end of run '' 7 | DISPATCHER | invalid train stage '' 8 | TRAVEL.THRU.SEGMENT | distance travelled exceeds track length '' 9 | GET.NEXT.TRAIN | cannot find train in queue '' 10 | LEAVE.LAST.SEGMENT | train id not found in prior segment '' 11 | STARTUP | file containing number of trains not found '' 12 | TRAIN | inordinate delay for waiting train USE 11 FOR OUTPUT GO TO PRINT.MESSAGE(ERROR.CODE) '' print message indicated by '' passed parameter '' ABORT.RUN routine continued on next page ... '' ... ABORT.RUN routine continued from last page 'PRINT.MESSAGE(1)' PRINT 3 LINES THUS <<<< Simulation aborted - train track file empty or incomplete >>>> GO TO STOP.RUN 'PRINT.MESSAGE(2)' PRINT 2 LINES THUS <<<< Simulation aborted - unacceptable queue length. >>>> GO TO STOP.RUN 'PRINT.MESSAGE(3)' PRINT 3 LINES THUS <<<< Simulation aborted - end of track reached before destination.>>>> GO TO STOP.RUN 'PRINT.MESSAGE(4)' PRINT 4 LINES THUS <<<< Simulation aborted - train not found in double track queue >>>> <<<< >>>> GO TO STOP.RUN 'PRINT.MESSAGE(5)' PRINT 3 LINE THUS <<<< Simulation aborted - train schedule file incomplete >>>> GO TO STOP.RUN 'PRINT.MESSAGE(6)' PRINT 3 LINES THUS <<<< Simulation aborted - trains in queue after end of run >>>> GO TO STOP.RUN '' ABORT.RUN routine continued on next page ... '' ... ABORT.RUN routine continued from last page 'PRINT.MESSAGE(7)' PRINT 3 LINES THUS <<<< Simulation aborted - invalid train stage in DISPATCHER >>>> GO TO STOP.RUN 'PRINT.MESSAGE(8)' PRINT 3 LINES THUS <<<< Simulation aborted - distance travelled exceeds track length >>>> GO TO STOP.RUN 'PRINT.MESSAGE(9)' PRINT 3 LINES THUS <<<< Simulation aborted - cannot find train in queue >>>> GO TO STOP.RUN 'PRINT.MESSAGE(10)' PRINT 3 LINES THUS <<<< Simulation aborted - train id not found in prior segment >>>> <<<< - refer to 'viewtrack' file for location >>>> GO TO STOP.RUN 'PRINT.MESSAGE(11)' PRINT 3 LINES THUS <<<< Simulation aborted - file containing number of records not found > GO TO STOP.RUN 'PRINT.MESSAGE(12)' PRINT 4 LINES THUS <<<< Simulation aborted - inordinate delay for train specified in >>>> <<<< 'viewtrack' file >>>> <<<< Check train schedule for conflicts. >>>> GO TO STOP.RUN '' ABORT.RUN routine continued on next page ... '' ... ABORT.RUN routine continued from last page 'STOP.RUN' PRINT 5 LINES WITH ERROR.CODE THUS << ERROR CODE: *** >> << Refer to table in Abort.Run Routine >> STOP END