// Chap 7, pp 334 - 339 // See also files C07P339.CPP and C07P341.CPP // ******************************************************* // Event-driven simulation of a single waiting line of // people. // // Input: // A text file of arrival and transaction times. // Each line contains the arrival time and required // transaction time for a customer. // // Assumption: // The arrival times in the input file are ordered by // increasing time. // // Output: // A trace of the events executed and a summary of // statistics (total number of arrivals and average time // spent waiting in line). // // ADTs: // Line of people - a queue of arrival events // Event list - a list of arrival events and // departure events that are sorted // by the time of the event. // Functions: // GetArrival - reads arrival event from input file // and inserts it into event list // ProcessArrival - executes an arrival event // ProcessDeparture - executes a departure event // Simulate - performs the simulation // ******************************************************* char* const ARRIVAL_FILE_NAME = "BANK.DAT"; const int FIELD_WIDTH = 3; // output field width for time const int MAX_STRING = 12; typedef char nameType[MAX_STRING]; #include // file operations #include // enables setw() #include // enables exit() #include "EL.h" // ADT event list operations // for simplicity, queue items and event list items // are the same structures; both indicate the kind of // event and contain arrival and transaction times #ifndef QTYPE typedef event queueItemType; // desired-type-of-queue-item #define QTYPE #endif // omit declaration of queueItemType from Queue.h //#include "Queue.h" // ADT queue operations // statistics of simulation struct stats { int TotalNum; // total no. of customers int TotalWait; // cumulative waiting time stats(); // constructor }; // end struct stats::stats() : TotalNum(0), TotalWait(0) { } // end constructor void GetArrival(ifstream& ArrivalFile, eventListClass& EL, boolean& Success) // -------------------------------------------------------- // Reads data for the next arrival event, and inserts the // new arrival node into the event list. // Precondition: ArrivalFile is a text file, which is open // for input, of arrival and transaction times. EL is the // current event list. // Postcondition: The next event is read from ArrivalFile // and is inserted into EL and Success is TRUE. At end-of- // file, EL is unchanged. If insertion is impossible, // Success is FALSE. ArrivalFile is open for input. // Calls: InsertEvent. // -------------------------------------------------------- { int NextTime; // time of next arrival event int TTime; // event's transaction time // (i.e. transaction length) if (ArrivalFile >> NextTime >> TTime) // read next event { // create a new arrival event event NewEvent(A, NextTime, TTime); // insert the event into the event list EL.InsertEvent(NewEvent, Success); } // end if } // end GetArrival void ProcessArrival(event ArrivalEvent, ifstream& ArrivalFile, eventListClass& EL, queueClass& Line, stats& Statistics, boolean& Success) // -------------------------------------------------------- // Executes an arrival event. // Precondition: ArrivalEvent contains the arrival event, // ArrivalFile of arrival events is open for input, // EL is the event list, the queue Line has been created, // and Statistics.TotalNum is the total number of arrivals. // Postcondition: If Line is empty, a departure event is // created for ArrivalEvent and inserted into event list EL. // ArrivalEvent is deleted from event list and added to // Line (the queue). A new arrival event is read from // ArrivalFile and is inserted into EL. Statistics.TotalNum // (total no. of arrivals) is updated. ArrivalFile is open // for input. Success is TRUE unless an insertion into the // event list is impossible. // Calls: InsertEvent, DeleteEvent, GetArrival, // QueueIsEmpty, and QueueAdd. // -------------------------------------------------------- { // update the total number of arrivals ++Statistics.TotalNum; int CurrentTime = ArrivalEvent.Time; // arrival time cout << "Processing an arrival event at time: " << setw(FIELD_WIDTH) << CurrentTime << endl; // if queue is empty, arrival will be at head of line boolean AtHead = Line.QueueIsEmpty(); // person arrives and enters the waiting line (queue) Line.QueueAdd(ArrivalEvent, Success); // update event list EL.DeleteEvent(Success); // delete event from event list // if at head of line, the person starts transaction if (AtHead) { // create a departure event event DepartureEvent(D, CurrentTime + ArrivalEvent.TransTime, 0); // insert the event into the event list EL.InsertEvent(DepartureEvent, Success); } // end if if (Success) // get the next arrival event, if any, from input file GetArrival(ArrivalFile, EL, Success); } // end ProcessArrival void ProcessDeparture(event DepartureEvent, eventListClass& EL, queueClass& Line, stats& Statistics, boolean& Success) // -------------------------------------------------------- // Executes a departure event. // Precondition: DepartureEvent contains the departure // event, EL is the event list, the queue Line has been // created, Statistics.TotalWait is the total waiting time. // Postcondition: A person is removed from the queue. The // event list is updated by deleting the current event and // adding a departure event for the next customer, if any. // Statistics.TotalWait (total waiting time) is updated. // Success is TRUE unless an insertion into the event list // was impossible. // Calls: InsertEvent, DeleteEvent, QueueIsEmpty, // QueueRemove, and GetQueueFront. // -------------------------------------------------------- { queueItemType PersonInLine; // data for person in queue int CurrentTime = DepartureEvent.Time; // arrival time cout << "Processing a departure event at time: " << setw(FIELD_WIDTH) << CurrentTime << endl; // person departs - update the line (queue) Line.QueueRemove(Success); // remove person from queue // update the event list: EL.DeleteEvent(Success); // delete event from event list // if the line is not empty, the next person starts // a transaction if (!Line.QueueIsEmpty()) { // create a departure event Line.GetQueueFront(PersonInLine, Success); event DepartureEvent(D, CurrentTime + PersonInLine.TransTime,0); // insert the event into the event list EL.InsertEvent(DepartureEvent, Success); if (Success) // update the statistics Statistics.TotalWait += (CurrentTime - PersonInLine.Time); } // end if } // end ProcessDeparture void Simulate(nameType FileName, stats& Statistics) // -------------------------------------------------------- // Simulates a line of people. // Precondition: FileName is the name of the text file // of arrival events. Data members of Statistics are 0. // Postcondition: Statistics.TotalWait is the total // cumulative waiting time of all customers. // Statistics.TotalNum is the total number of customers. // ArrivalFile is closed. // Calls: GetArrival, ProcessArrival, ProcessDeparture, // EventListIsEmpty, and RetrieveEvent. // -------------------------------------------------------- { event NewEvent; // current event eventListClass EL; // event list queueClass Line; // waiting line ifstream ArrivalFile(FileName); // file of times boolean Success; cout << "Simulation begins:\n"; // get the first arrival event and place on event list GetArrival(ArrivalFile, EL, Success); // process events until the event list is empty while ( Success && !EL.EventListIsEmpty() ) { // get next event from beginning of event list EL.RetrieveEvent(NewEvent, Success); if (NewEvent.WhichEvent == A) // process arrival event ProcessArrival(NewEvent, ArrivalFile, EL, Line, Statistics, Success); else // process departure event ProcessDeparture(NewEvent, EL, Line, Statistics, Success); } // end while if (!Success) { cerr << "Error in event list. " << "Execution aborted.\n"; exit(1); // abort } // end if cout << "Simulation ends.\n"; } // end Simulate main() { stats Statistics; // summary statistics initialized to 0 // perform the simulation Simulate(ARRIVAL_FILE_NAME, Statistics); // write out the final statistics cout << "\nFinal Statistics:\n" << " Total number of people processed: " << Statistics.TotalNum << "\n" << " Average amount of time spent waiting: "; if (Statistics.TotalNum == 0) cout << " 0.0\n"; else cout << float(Statistics.TotalWait)/ float(Statistics.TotalNum) << endl; return 0; } // end program