home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / modula2 / library / queuem2 / queuexam.mod < prev    next >
Text File  |  1989-08-30  |  31KB  |  875 lines

  1. (* source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22
  2.    author: G.Greene, AGCS D/429 (NS/TS), 312/681-7783       created: 88.07.22
  3.  
  4.    function:
  5.     This is a test program for module QueueADT, and of the random variate
  6.     generators used.  Because it makes use of many Modula-2 language features,
  7.     this program can also be used as part of an evaluation of Modula-2
  8.     systems.  This version is customized for the JPI TopSpeed compiler and
  9.     module libraries.
  10.  
  11.    history:
  12.     88.07.22  1.0a  initial release.
  13. *)
  14.  
  15. MODULE  QueuExample;
  16.  
  17.  
  18. FROM  IO        IMPORT  (*PROC*)  WrLn,  WrStr,  RdStr,  WrCard,  RdCard,
  19.                                   WrLngCard,  RdReal,  WrReal,
  20.                                   RedirectInput,  KeyPressed,  RdKey;
  21.  
  22. FROM  Str       IMPORT  (*PROC*)  FixRealToStr;
  23.  
  24. FROM  GetRange  IMPORT  (*PROC*)  GetLimitedCARDINAL, GetPositiveREAL;
  25.  
  26. FROM  QueueADT  IMPORT  (*TYPE*)  Queues,
  27.                         (*PROC*)  CreateQueue,  EmptyTheQueue,  DestroyQueue,
  28.                                   isEmpty, QueueSize,
  29.                                   FIFOEnqueue,  PriorityEnqueue,
  30.                                   Dequeue, ReadElement;
  31.  
  32. FROM  MakeSeed  IMPORT  (*PROC*)  MakeSeedValue;
  33.  
  34. FROM  UnifRNG   IMPORT  (*PROC*)  SetSeedValue;
  35.  
  36. FROM  NormRNG   IMPORT  (*PROC*)  NormalVariate,
  37.                                   LognormalVariate, LognormalParameters;
  38.  
  39. FROM  XpnlRNG   IMPORT  (*PROC*)  ExponentialVariate;
  40.  
  41. FROM  QueData   IMPORT  (*CONST*) MaxQueues,   MaxServers,
  42.                         (*TYPE*)  ServiceFunctions,  ProcessTimes,
  43.                                   QueueEntryData,
  44.                                   QueueListIndices,  ServerListIndices,
  45.                                   QueueInfo,   ServerInfo,
  46.                                   QueueLists,  ServerLists;
  47.  
  48. FROM  QueDsply  IMPORT  (*TYPE*)  DisplayActions,  MessageTypes,
  49.                         (*PROC*)  DisplayStatusMatrix,
  50.                                   ChangeQueueDisplay,  ChangeServerDisplay,
  51.                                   DisplayMessage,  DisplayCurrentTime,
  52.                                   DisplaySummary;
  53.  
  54. FROM  Window    IMPORT  (*PROC*)  Clear,  GotoXY;
  55.  
  56. FROM  Lib       IMPORT  (*PROC*)  Delay;
  57.  
  58.  
  59. TYPE
  60.   ServerNumbers     = [ 0 .. MaxServers ];
  61.  
  62. (*                                                                         [2]
  63.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  64.  
  65.  
  66. VAR
  67.   EventQueue:       Queues;            (* priority queue of pending events *)
  68.   QueueList:        QueueLists;        (* information about each queue *)
  69.   ServerList:       ServerLists;       (* information about each server *)
  70.  
  71.   NumberOfQueues:   QueueListIndices;  (* number of queues actually allocated *)
  72.  
  73.   OverloadedQueues: BITSET;            (* set of overloaded queue numbers *)
  74.  
  75.   IsIndependentRun: BOOLEAN;           (* true, if we start at random *)
  76.  
  77.   (* the following are process parameters in arbitrary time units *)
  78.   SimulationPeriod:  ProcessTimes;     (* time limit for simulation *)
  79.  
  80.   ArrivalIntervalEV: REAL;             (* expected inter-arrival time value *)
  81.  
  82.  
  83.  
  84. (* Ask the operator for process parameters (whether this should be independent
  85.    of other runs, the arrival and service rates, and the number of time units
  86.    to run the process).
  87. *)
  88.  
  89. PROCEDURE  GetProcessParameters (
  90.                    (*out*)  VAR  IsIndependentRun:  BOOLEAN;
  91.                    (*out*)  VAR  SimulationPeriod:  ProcessTimes;
  92.                    (*out*)  VAR  ArrivalIntervalEV: REAL );
  93.   VAR
  94.     YesOrNo: ARRAY [ 0 .. 0 ]  OF CHAR;   (* one-byte string: "Y" or "N" *)
  95.  
  96.   BEGIN
  97.     WrLn;
  98.     WrStr ( "Do you want this run to be independent (Y/[N])? " );
  99.     RdStr ( YesOrNo );
  100.     IsIndependentRun := CAP ( YesOrNo [ 0 ] ) = "Y";
  101.     WrLn;
  102.     WrStr ( "The following questions assume a common time unit." );
  103.     WrLn;
  104.     WrStr ( "You may use any reasonable unit (seconds, minutes, etc.)," );
  105.     WrLn;
  106.     WrStr ( "but you must use that unit consistently." );
  107.     WrLn;    WrLn;
  108.  
  109.     WrStr ( "How long (in your time units) do you want this to run?" );
  110.     WrLn;
  111.     WrStr ( "Your answer must be a positive integer value: " );
  112.     SimulationPeriod := RdCard ( );
  113.     WrLn;
  114.  
  115.     ArrivalIntervalEV := GetPositiveREAL (
  116.       "What is the mean customer arrival rate (time units between customers)?",
  117.        FALSE (* zero is not allowed *) );
  118.   END  GetProcessParameters;
  119.  
  120. (*                                                                         [3]
  121.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  122.  
  123.  
  124. (* Get a valid number of queues to simulate from the operator, and return
  125.    that value in the parameter.  Allocate that many queues, and get the
  126.    queue-related information from the operator for each of the allocated
  127.    queues.  Also allocate the (priority) event queue for internal use in
  128.    this program.
  129. *)
  130.  
  131. PROCEDURE  SpecifyQueues (
  132.             (*out*)  VAR  QueueCount: QueueListIndices );
  133.  
  134.   VAR
  135.     QueueIndex:     QueueListIndices;
  136.     CustomerSink:   CARDINAL;
  137.  
  138. (*                                                                         [4]
  139.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  140.  
  141.  
  142. (* Define the service time probability distribution for the queue specified
  143.    by the parameter.  In addition to choosing the type of distribution, the
  144.    operator is prompted for the parameters of the chosen distribution.
  145. *)
  146.  
  147.   PROCEDURE  GetServiceParameters (
  148.                            (*in*)  inQueue: QueueListIndices );
  149.  
  150.     VAR
  151.       Answer:         ARRAY [ 0 .. 0 ]  OF CHAR;
  152.       FunctionCode:   CHAR;
  153.       ServiceStdDev:  REAL;
  154.  
  155.  
  156.     BEGIN
  157.       QueueList [ inQueue ] .ServiceTimeEV := GetPositiveREAL (
  158.            "  What is the mean customer service time (time units/customer)?",
  159.             FALSE  (* zero is not allowed *) );
  160.  
  161.       WrStr ( "  What is the service time probability distribution?" );
  162.       WrLn;
  163.       WrStr ( "  ... specify eXponential, Normal, or [L]ognormal: " );
  164.       RdStr  ( Answer );   WrLn;
  165.       FunctionCode := CAP ( Answer [ 0 ] );
  166.  
  167.       WITH  QueueList [ inQueue ]  DO
  168.         IF  ( FunctionCode = 'X' )  OR  ( FunctionCode = 'E' )  THEN
  169.           ServiceFcn   := Exponential;
  170.           ServiceParm1 := ServiceTimeEV;
  171.  
  172.         ELSE
  173.           ServiceStdDev := GetPositiveREAL (
  174.                            "  What is the standard deviation of service time?",
  175.                             TRUE  (* zero allowed *) );
  176.  
  177.           IF  FunctionCode = 'N'  THEN
  178.             ServiceFcn   := Normal;
  179.             ServiceParm1 := ServiceTimeEV;
  180.             ServiceParm2 := ServiceStdDev;
  181.  
  182.           ELSE   (* assume default: lognormal *)
  183.             ServiceFcn   := Lognormal;
  184.             LognormalParameters ( ServiceTimeEV,  ServiceStdDev,
  185.                                   ServiceParm1,   ServiceParm2 );
  186.           END;
  187.         END;   (* IF exponential *)
  188.       END;   (* WITH *)
  189.     END  GetServiceParameters;
  190.  
  191. (*                                                                         [5]
  192.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  193.  
  194.  
  195.   BEGIN    (* SpecifyQueues *)
  196.     CreateQueue ( EventQueue );
  197.     QueueCount := GetLimitedCARDINAL ( 1,  MaxQueues,
  198.                                        "Enter number of queues to allocate, " );
  199.  
  200.     FOR  QueueIndex := 1  TO  QueueCount  DO
  201.       QueueList [ QueueIndex ] .ExternalInput := TRUE;
  202.     END;
  203.  
  204.     FOR  QueueIndex := 1  TO  QueueCount  DO
  205.       WITH  QueueList [ QueueIndex ]  DO
  206.         CreateQueue ( theQueue );
  207.         CurrentSize := 0;    TotalWait := 0;    SinkOutputTo := 0;
  208.  
  209.         WrLn;   WrStr ( "Please enter information for queue #" );
  210.         WrCard ( QueueIndex, 1 );   WrStr ( ": " );   WrLn;
  211.  
  212.         ServerCount := GetLimitedCARDINAL ( 1, MaxServers,
  213.                                             "  Number of servers " );
  214.  
  215.         GetServiceParameters ( QueueIndex );
  216.  
  217.         IF  QueueIndex = QueueCount  THEN
  218.           SinkOutputTo := 0;   (* last queue must release customer *)
  219.  
  220.         ELSE                   (* ask operator where customers go *)
  221.           REPEAT
  222.             WrStr  ( "  What is the customer sink (zero, or queue # " );
  223.             WrCard ( QueueIndex + 1, 1 );   WrStr ( "-" );
  224.             WrCard ( QueueCount, 1 );       WrStr ( ")? " );
  225.             CustomerSink := RdCard ( );     WrLn;
  226.           UNTIL  ( CustomerSink = 0 )  OR
  227.                  ( CustomerSink <= QueueCount )  AND
  228.                                                ( CustomerSink > QueueIndex );
  229.           SinkOutputTo := CustomerSink;
  230.  
  231.           IF  CustomerSink # 0  THEN
  232.             QueueList [ CustomerSink ] .ExternalInput := FALSE;
  233.           END;   (* IF sink to later queue *)
  234.         END;   (* IF last queue *)
  235.       END;   (* WITH queue info *)
  236.     END;   (* FOR each queue *)
  237.   END  SpecifyQueues;
  238.  
  239. (*                                                                         [6]
  240.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  241.  
  242.  
  243. (* Initialize the statistics for every server in each allocated queue.  The
  244.    number of queues is specified by the parameter.
  245. *)
  246.  
  247. PROCEDURE  InitializeServers (
  248.                       (*in*)  QueueCount:  QueueListIndices );
  249.  
  250.   VAR
  251.     QueueIndex:     QueueListIndices;
  252.     ServerIndex:    ServerListIndices;
  253.  
  254.   BEGIN
  255.     FOR  QueueIndex := 1  TO  QueueCount  DO
  256.       FOR  ServerIndex := 1  TO  QueueList [ QueueIndex ] .ServerCount  DO
  257.         WITH  ServerList [ QueueIndex, ServerIndex ]  DO
  258.           TotIdleTime := 0;
  259.           CustomerCnt := 0;
  260.           IdleNow     := TRUE;
  261.           IdleSince   := 0;
  262.         END      (*WITH ServerList[QueueIndex,ServerIndex] *)
  263.       END;    (* FOR each server *)
  264.     END;    (* FOR each queue *)
  265.   END  InitializeServers;
  266.  
  267. (*                                                                         [7]
  268.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  269.  
  270.  
  271. (* Display the parameters specified by the user, and check for consistency,
  272.    etc.  At the moment, we only warn if one or more of the queues are
  273.    overloaded (we expect customers to arrive faster than they can be served).
  274. *)
  275.  
  276. PROCEDURE  TestParameters ( );
  277.  
  278.  
  279.   VAR
  280.     QueueIndex:  QueueListIndices;
  281.     ArrivalRate: ARRAY QueueListIndices  OF REAL;  (* customers/time unit *)
  282.     ServiceRate: REAL;                             (* customers/time unit *)
  283.     LoadRatio:   REAL;                             (* arrival/service rates *)
  284.     StringOfReal: ARRAY [ 0 .. 7 ]  OF CHAR;
  285.     OK:           BOOLEAN;
  286.  
  287.  
  288. (* Like the math MIN function, return the lesser of the two parameter values.
  289. *)
  290.  
  291.   PROCEDURE  MinREAL (
  292.               (*in*)  Value1, Value2: REAL ): REAL;
  293.  
  294.     BEGIN
  295.       IF  Value1 < Value2
  296.         THEN  RETURN  Value1;
  297.         ELSE  RETURN  Value2;
  298.       END;
  299.     END  MinREAL;
  300.  
  301. (*                                                                         [8]
  302.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  303.  
  304.  
  305.   BEGIN  (* TestParameters *)
  306.     WrStr ( "The parameters for this run are:" );   WrLn;
  307.     WrStr ( "  Number of queues: " );    WrCard ( NumberOfQueues, 1 );
  308.     WrLn;
  309.  
  310.     WrStr ( "  Mean arrival rate: " );
  311.     FixRealToStr ( LONGREAL ( ArrivalIntervalEV ), 0, StringOfReal, OK );
  312.     WrStr ( StringOfReal );
  313.     WrLn;
  314.  
  315.     WrStr ( "  The simulation will proceed for " );
  316.     WrCard   ( SimulationPeriod, 1 );    WrStr ( " time units." );
  317.     WrLn;
  318.  
  319.     WrLn;
  320.  
  321.     (* initially set arrival rates for just external customer arrivals *)
  322.     FOR  QueueIndex := 1  TO  NumberOfQueues  DO
  323.       IF  QueueList [ QueueIndex ] .ExternalInput
  324.         THEN  ArrivalRate [ QueueIndex ] := 1.0 / ArrivalIntervalEV;
  325.         ELSE  ArrivalRate [ QueueIndex ] := 0.0;
  326.       END;   (* IF external input *)
  327.     END;   (* FOR each queue *)
  328.  
  329.     (* evaluate service rate vs. expected input rate *)
  330.     OverloadedQueues := { };
  331.  
  332.     FOR  QueueIndex := 1  TO  NumberOfQueues  DO
  333.       WITH  QueueList [ QueueIndex ]  DO
  334.         ServiceRate := FLOAT ( ServerCount ) / ServiceTimeEV;
  335.  
  336.         IF  SinkOutputTo # 0  THEN
  337.           ArrivalRate [ SinkOutputTo ] := ArrivalRate [ SinkOutputTo ]  +
  338.                         MinREAL ( ArrivalRate [ QueueIndex ], ServiceRate );
  339.         END;   (* IF *)
  340.  
  341.         (* warn if we expect to serve customers no quicker than they arrive *)
  342.         LoadRatio := ArrivalRate [ QueueIndex ] / ServiceRate;
  343.  
  344.         IF  LoadRatio  >=  1.0  THEN
  345.           WrStr ( "WARNING:  queue #" );   WrCard   ( QueueIndex, 2 );
  346.           WrStr ( " is overloaded, ratio: " );
  347.           FixRealToStr ( LONGREAL ( LoadRatio ), 2, StringOfReal, OK );
  348.           WrStr ( StringOfReal );   WrLn;
  349.  
  350.           INCL ( OverloadedQueues, QueueIndex );
  351.         END;   (* IF *)
  352.       END;   (* WITH queue info *)
  353.     END;   (* FOR each queue *)
  354.   END  TestParameters;
  355.  
  356. (*                                                                         [9]
  357.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  358.  
  359.  
  360. (* Return TRUE if the user of this program responds positively to the question
  361.    posed below.  A positive response is to press the "Y" key (upper- or lower-
  362.    case).
  363. *)
  364.  
  365. PROCEDURE  OperatorApproves ( ): BOOLEAN;
  366.  
  367.   VAR
  368.     YesOrNo:  ARRAY [ 0 .. 0 ]  OF CHAR;
  369.     Answer:   CHAR;
  370.  
  371.   BEGIN
  372.     WrStr ( "Do you want to proceed with the emulation (Y/N)? " );
  373.     RdStr ( YesOrNo );
  374.     Answer := CAP ( YesOrNo [ 0 ] );
  375.  
  376.     RETURN  Answer = 'Y';
  377.   END  OperatorApproves;
  378.  
  379. (*                                                                        [10]
  380.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  381.  
  382.  
  383. (* This is the main processing procedure of this program.  It runs a queue
  384.    simulation in accordance with the user-provided specifications.
  385. *)
  386.  
  387. PROCEDURE  RunTheSimulation ( );
  388.  
  389.  
  390.   CONST
  391.     SentinelValue  = 32769;   (* used to check priority queue integrity *)
  392.  
  393.  
  394.   TYPE
  395.     EventTypes     = ( CustomerArrives, ServerFinishes );
  396.  
  397.     Events =
  398.       RECORD
  399.         Time:    ProcessTimes;       (* this field MUST be first *)
  400.         inQueue: QueueListIndices;
  401.  
  402.         CASE  Type: EventTypes  OF
  403.           CustomerArrives:
  404.               ServiceTime:  ProcessTimes;  (* interval, NOT starting clock *)
  405.         |
  406.           ServerFinishes:
  407.               Server:       ServerListIndices;
  408.         END;
  409.  
  410.         Sentinel: CARDINAL;
  411.       END;   (* RECORD Events *)
  412.  
  413.  
  414.   VAR
  415.     CurrentTime:  ProcessTimes;      (* elapsed (clock) time since we began *)
  416.  
  417.     NextQueue:    QueueListIndices;
  418.  
  419.     NextEvent:    Events;
  420.     DataIsValid:  BOOLEAN;
  421.  
  422.     ServiceParm1,
  423.     ServiceParm2: REAL;       (* parameters for lognormal variate generator *)
  424.  
  425. (*                                                                        [11]
  426.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  427.  
  428.  
  429. (* Return as the function value a time interval randomly chosen for the
  430.    service time distribution specified for the queue designated by the
  431.    parameter (whew!).  Note that for the normal distribution, we have to
  432.    provide for the possibility that a negative value will be generated.
  433.    We do this by truncating the distribution at zero.
  434. *)
  435.  
  436.   PROCEDURE  ServiceTimeFor (
  437.                      (*in*)  QueueNumber: QueueListIndices ):  CARDINAL;
  438.  
  439.     VAR
  440.       Value:  LONGREAL;
  441.  
  442.     BEGIN
  443.       WITH  QueueList [ QueueNumber ]  DO
  444.         CASE  ServiceFcn  OF
  445.           Exponential:
  446.             RETURN  TRUNC ( 0.5  +  ExponentialVariate ( ServiceParm1 ) );
  447.         |
  448.           Lognormal:
  449.             RETURN  TRUNC ( 0.5  +
  450.                             LognormalVariate ( ServiceParm1, ServiceParm2 ) );
  451.         |
  452.           Normal:
  453.             Value := NormalVariate ( ServiceParm1, ServiceParm2 );
  454.  
  455.             IF  Value <= 0.0
  456.               THEN  RETURN  0;
  457.               ELSE  RETURN  TRUNC ( 0.5 + Value );
  458.             END;   (* IF *)
  459.         END;   (* CASE ServiceFcn *)
  460.       END;   (* WITH queue info *)
  461.     END  ServiceTimeFor;
  462.  
  463.  
  464.  
  465. (* Return as the function value a time interval randomly chosen for the
  466.    arrival time distribution (forced to be exponential).
  467. *)
  468.  
  469.   PROCEDURE  ExternalArrivalInterval ( ):  ProcessTimes;
  470.  
  471.     VAR
  472.       Value:  REAL;
  473.  
  474.     BEGIN
  475.       RETURN  TRUNC ( 0.5  +  ExponentialVariate ( ArrivalIntervalEV ) );
  476.     END  ExternalArrivalInterval;
  477.  
  478. (*                                                                        [12]
  479.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  480.  
  481.  
  482. (* Set the simulation starting time to zero.  We first compute the parameters
  483.    to be used by the service-time distribution.  Then, for each queue, we set
  484.    its length to zero.  We determine when the first customer will arrive, and
  485.    set the queue information record accordingly.  We also create event queue
  486.    entries for each of the arrivals.  Along the way, we compute values of the
  487.    parameters for the service interval distribution.
  488. *)
  489.  
  490.   PROCEDURE  SetInitialState ( );
  491.  
  492.     VAR
  493.       QueueIndex: QueueListIndices;
  494.       Server:     ServerListIndices;
  495.       event:      Events;
  496.  
  497.     BEGIN
  498.       IF  IsIndependentRun  THEN
  499.         SetSeedValue ( MakeSeedValue ( ) );
  500.       END;
  501.  
  502.       CurrentTime     := 0;
  503.       event .Type     := CustomerArrives;
  504.       event .Sentinel := SentinelValue;
  505.  
  506.       FOR  QueueIndex := 1  TO  NumberOfQueues  DO
  507.         FOR  Server := 1  TO  QueueList [ QueueIndex ] .ServerCount  DO
  508.           ChangeServerDisplay ( QueueIndex, Server, Unmark );
  509.         END;
  510.  
  511.         IF  QueueList [ QueueIndex ] .ExternalInput  THEN
  512.           WITH  event  DO
  513.             Time        := ExternalArrivalInterval ( );
  514.             inQueue     := QueueIndex;
  515.             ServiceTime := ServiceTimeFor ( QueueIndex );
  516.           END;   (* WITH event *)
  517.  
  518.           PriorityEnqueue ( event, EventQueue );
  519.         END;    (* IF customers originate outside *)
  520.       END;    (* FOR each queue *)
  521.  
  522.       IF  OverloadedQueues # { }  THEN
  523.         DisplayMessage ( WarningMessage, "Overloads:" );
  524.  
  525.         FOR  QueueIndex := 1  TO  NumberOfQueues  DO
  526.           IF  QueueIndex IN OverloadedQueues  THEN
  527.             WrCard ( QueueIndex, 3 );
  528.           END;    (* IF overloaded *)
  529.         END;    (* FOR each queue *)
  530.       END;    (* IF any queue is overloaded *)
  531.     END  SetInitialState;
  532.  
  533. (*                                                                        [13]
  534.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  535.  
  536.  
  537. (* Examine all servers in the queue specfied by the first parameter.  If any
  538.    of them are idle, return the index to the server that has been idle longest
  539.    in the second parameter, and return TRUE as the function value.  If there
  540.    are no idle servers, return FALSE.
  541. *)
  542.  
  543.   PROCEDURE  FoundIdleServer (
  544.                  (*in*)       inQueue:      QueueListIndices;
  545.                 (*out*)  VAR  ServerToUse:  ServerListIndices ): BOOLEAN;
  546.  
  547.     VAR
  548.       EarliestIdle:  CARDINAL;
  549.       ServerIndex:   ServerListIndices;
  550.       HaveServer:    BOOLEAN;
  551.  
  552.     BEGIN
  553.       EarliestIdle := MAX ( CARDINAL );      (* actual values will be <= this *)
  554.       HaveServer   := FALSE; (* set TRUE if any server for this queue is idle *)
  555.  
  556.       FOR  ServerIndex := 1  TO  QueueList [ inQueue ] .ServerCount  DO
  557.         WITH  ServerList [ inQueue, ServerIndex ]  DO
  558.           IF  IdleNow  AND  ( IdleSince <  EarliestIdle )  THEN
  559.             EarliestIdle := IdleSince;
  560.             ServerToUse  := ServerIndex;
  561.             HaveServer   := TRUE;
  562.           END;
  563.         END;   (* WITH ServerList[inQueue,ServerIndex] *)
  564.       END;   (* FOR ServerIndex *)
  565.  
  566.       RETURN  HaveServer;
  567.     END  FoundIdleServer;
  568.  
  569. (*                                                                        [14]
  570.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  571.  
  572.  
  573. (*  Set the server specified by the second parameter in the queue specified
  574.     by the first parameter active for the time interval specified by the
  575.     third parameter.  Then enqueue an event to signal the server's completion.
  576. *)
  577.  
  578.   PROCEDURE  ActivateServer (
  579.                      (*in*)  QueueNumber:   QueueListIndices;
  580.                      (*in*)  ServerNumber:  ServerListIndices;
  581.                      (*in*)  ServiceTime:   ProcessTimes );
  582.  
  583.     VAR
  584.       event:  Events;  (* priority queue event: server becomes free *)
  585.  
  586.  
  587.     BEGIN   (* ActivateServer *)
  588.       WITH  ServerList [ QueueNumber, ServerNumber ]  DO
  589.         INC ( TotIdleTime, CurrentTime - IdleSince);
  590.  
  591.         INC ( CustomerCnt );
  592.  
  593.         IdleNow    := FALSE;
  594.         myCustomer .ServiceTime := ServiceTime;
  595.         myCustomer .StartTime   := CurrentTime;
  596.       END;   (* WITH Server *)
  597.  
  598.       event .Time     := CurrentTime + ServiceTime;
  599.       event .inQueue  := QueueNumber;
  600.       event .Type     := ServerFinishes;
  601.       event .Server   := ServerNumber;
  602.       event .Sentinel := SentinelValue;
  603.  
  604.       PriorityEnqueue ( event, EventQueue );
  605.     END  ActivateServer;
  606.  
  607. (*                                                                        [15]
  608.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  609.  
  610.  
  611. (* The first parameter contains the event queue entry for a customer arrival.
  612.    If there is any idle server for the queue associated with this event, we
  613.    immediately assign the customer to that server;  otherwise, we must add it
  614.    to the queue.  It will then be processed after n ServerFinishes events
  615.    occur for that queue, where n is the queue length.  The second parameter
  616.    has the starting time to assign to the customer (CurrentTime for external
  617.    customers, or the original start time for those transferred from another
  618.    queue).
  619. *)
  620.  
  621.   PROCEDURE  AssignCustomer (
  622.                      (*in*)  NextEvent: Events;
  623.                      (*in*)  StartTime: ProcessTimes );
  624.  
  625.     VAR
  626.       QueueEntry:   QueueEntryData;
  627.       QueueToUse:   QueueListIndices;
  628.       ServerToUse:  ServerListIndices;
  629.  
  630.     BEGIN
  631.       QueueToUse := NextEvent .inQueue;
  632.  
  633.       QueueEntry .StartTime   := StartTime;
  634.       QueueEntry .ServiceTime := NextEvent .ServiceTime;
  635.  
  636.       IF  FoundIdleServer ( NextEvent .inQueue, ServerToUse )  THEN
  637.                                             (* process customer immediately *)
  638.         ServerList [ QueueToUse, ServerToUse ] .myCustomer := QueueEntry;
  639.         ActivateServer ( QueueToUse, ServerToUse, NextEvent .ServiceTime );
  640.         ChangeServerDisplay ( QueueToUse, ServerToUse, Mark );
  641.  
  642.       ELSE                                  (* add it to the queue *)
  643.         WITH  QueueList [ QueueToUse ]  DO
  644.           FIFOEnqueue ( QueueEntry, theQueue );
  645.           INC ( CurrentSize );
  646.  
  647.           ChangeQueueDisplay ( QueueToUse, CurrentSize,  Mark );
  648.         END;   (* WITH QueueList[QueueToUse] *)
  649.       END;   (* IF *)
  650.     END  AssignCustomer;
  651.  
  652. (*                                                                        [16]
  653.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  654.  
  655.  
  656. (*  Set the server specified by the parameter to idle.
  657. *)
  658.  
  659.   PROCEDURE  IdleServer (
  660.                  (*in*)  inQueue:  QueueListIndices;
  661.                  (*in*)  Server:   ServerListIndices );
  662.  
  663.     VAR
  664.       event: Events;
  665.  
  666.     BEGIN
  667.       WITH  ServerList [ inQueue, Server ]  DO
  668.         IdleNow   := TRUE;
  669.         IdleSince := CurrentTime;
  670.  
  671.         IF  QueueList [ inQueue ] .SinkOutputTo # 0  THEN
  672.           event .Time        := CurrentTime;
  673.           event .inQueue     := QueueList [ inQueue ] .SinkOutputTo;
  674.           event .Type        := CustomerArrives;
  675.           event .ServiceTime := ServiceTimeFor (
  676.                                         QueueList [ inQueue ] .SinkOutputTo );
  677.           event .Sentinel    := SentinelValue;
  678.  
  679.           AssignCustomer ( event,
  680.                       ServerList [ inQueue, Server ] .myCustomer .StartTime );
  681.         END;   (*IF*)
  682.       END;   (*WITH*)
  683.     END  IdleServer;
  684.  
  685. (*                                                                        [17]
  686.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  687.  
  688.  
  689. (* Assign the front customer in the queue specified by the first parameter
  690.    to the server which has been idle the longest.  Log the waiting time for
  691.    this customer.  Place an entry in the event queue for the time the server
  692.    will finish.
  693. *)
  694.  
  695.   PROCEDURE  ServeWaitingCustomer (
  696.                            (*in*)  inQueue: QueueListIndices );
  697.  
  698.     VAR
  699.       ServerToUse:  ServerListIndices;
  700.       QueueEntry:   QueueEntryData;
  701.       DataIsValid:  BOOLEAN;
  702.  
  703.     BEGIN
  704.  
  705.       IF  FoundIdleServer ( inQueue, ServerToUse )  THEN
  706.         Dequeue ( QueueEntry, QueueList [ inQueue ] .theQueue, DataIsValid );
  707.  
  708.         IF  DataIsValid  THEN
  709.           WITH  QueueList [ inQueue ]  DO
  710.             ChangeQueueDisplay ( inQueue, CurrentSize, Unmark );
  711.             DEC ( CurrentSize );
  712.  
  713.             ActivateServer ( inQueue, ServerToUse, QueueEntry .ServiceTime );
  714.             ChangeServerDisplay ( inQueue, ServerToUse, Mark );
  715.  
  716.             TotalWait := TotalWait  +
  717.                                LONGCARD ( CurrentTime - QueueEntry .StartTime );
  718.           END;   (* WITH QueueList[inQueue] *)
  719.  
  720.         ELSE
  721.           DisplayMessage ( ErrorMessage, "dequeue failed in ServeCustomer" );
  722.         END;
  723.  
  724.       ELSE
  725.         DisplayMessage ( ErrorMessage, "ServeCustomer called, no idle server" );
  726.       END;
  727.     END  ServeWaitingCustomer;
  728.  
  729. (*                                                                        [18]
  730.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  731.  
  732.  
  733. (* The operator has signalled that s/he wants the program stopped, and a
  734.    screen dump of the current event (priority) queue displayed.  The dump is
  735.    displayed one screen's worth at a time.  This procedure would normally be
  736.    invoked only if something has gone awry with the simulation.
  737. *)
  738.  
  739. PROCEDURE  DumpEventQueueAndAbort ( );
  740.  
  741.   VAR
  742.     element:  CARDINAL;
  743.  
  744.   BEGIN
  745.     GotoXY ( 0, 0 );  Clear;  element := 0;
  746.  
  747.     LOOP
  748.       ReadElement ( NextEvent, EventQueue, element, DataIsValid );
  749.  
  750.       IF  NOT DataIsValid  THEN
  751.         EXIT;                     (* probably indicates end of queue *)
  752.       END;
  753.  
  754.       WITH  NextEvent  DO
  755.         WrCard ( Time, 6 );  WrCard ( inQueue, 4 );
  756.  
  757.         CASE  Type  OF
  758.           CustomerArrives:
  759.             WrStr ( ' customer arrival: ' );
  760.             WrCard ( ServiceTime, 1 );
  761.         |
  762.           ServerFinishes:
  763.             WrStr ( ' server finishes:  ' );
  764.             WrCard ( Server, 1 );
  765.         END;  (* CASE Type *)
  766.       END;  (* WITH NextEvent *)
  767.  
  768.       WrLn;
  769.       INC ( element );
  770.  
  771.       IF  ( element MOD 20 ) = 0  THEN
  772.         Delay ( 20000 );
  773.       END;
  774.     END;  (* LOOP *)
  775.  
  776.     HALT;
  777.   END  DumpEventQueueAndAbort;
  778.  
  779. (*                                                                        [19]
  780.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  781.  
  782.  
  783.   BEGIN  (* RunTheSimulation *)
  784.     SetInitialState;
  785.  
  786.     WHILE  CurrentTime <= SimulationPeriod  DO
  787.       IF  KeyPressed ( )  AND  ( RdKey ( ) = 'X' )  THEN
  788.         DumpEventQueueAndAbort;
  789.       END;
  790.  
  791.       Dequeue ( NextEvent, EventQueue, DataIsValid );
  792.  
  793.       IF  NextEvent .Sentinel # SentinelValue  THEN
  794.         DisplayMessage ( ErrorMessage, "invalid sentinel: " );
  795.         WrCard ( NextEvent .Sentinel, 1 );
  796.         WrStr  ( " at time " );  WrCard ( CurrentTime, 1 );
  797.         WrCard ( NextEvent .inQueue, 3 );
  798.         WrCard ( ORD ( DataIsValid ), 2 );
  799.       END;   (* IF invalid sentinel *)
  800.  
  801.       IF  DataIsValid  THEN
  802.         NextQueue   := NextEvent .inQueue;
  803.         CurrentTime := NextEvent .Time;
  804.         DisplayCurrentTime ( NextEvent .Time );
  805.  
  806.         CASE  NextEvent .Type  OF
  807.           CustomerArrives:
  808.             AssignCustomer ( NextEvent, CurrentTime );
  809.  
  810.             IF  QueueList [ NextEvent .inQueue ] .ExternalInput  THEN
  811.               WITH  NextEvent  DO
  812.                 (* note that NextEvent .inQueue and NextEvent .Type are
  813.                    already set correctly from the entry dequeued above *)
  814.                 Time        := CurrentTime + ExternalArrivalInterval ( );
  815.                 ServiceTime := ServiceTimeFor ( inQueue );
  816.                 Sentinel    := SentinelValue;
  817.  
  818.                 PriorityEnqueue ( NextEvent, EventQueue );
  819.               END;   (* WITH *)
  820.             END;   (* IF not an internal customer *)
  821.         |
  822.           ServerFinishes:
  823.             IdleServer ( NextQueue, NextEvent .Server );
  824.             ChangeServerDisplay ( NextQueue, NextEvent .Server, Unmark );
  825.  
  826.             IF  NOT isEmpty ( QueueList [ NextQueue ] .theQueue )  THEN
  827.               ServeWaitingCustomer ( NextQueue );
  828.             END;
  829.         END;    (* CASE NextEventType *)
  830.  
  831.       ELSE
  832.         DisplayMessage ( ErrorMessage, "event dequeue failed" );
  833.       END;    (* IF DataIsValid *)
  834.     END;    (* WHILE within simulation period *)
  835.  
  836.     DestroyQueue ( EventQueue );
  837.   END  RunTheSimulation;
  838.  
  839. (*                                                                        [20]
  840.  source: h:\modula\code\queues\QueuExam.MOD  v1.0a        revised: 88.07.22 *)
  841.  
  842.  
  843. (*  Clean up storage allocations for queues.
  844. *)
  845.  
  846. PROCEDURE  CleanUp ( );
  847.  
  848.   VAR
  849.     QueueIndex: QueueListIndices;
  850.  
  851.   BEGIN
  852.     FOR  QueueIndex := 1  TO  NumberOfQueues  DO
  853.       DestroyQueue ( QueueList [ QueueIndex ] .theQueue );
  854.     END;
  855.   END  CleanUp;
  856.  
  857.  
  858.  
  859. BEGIN
  860.   GetProcessParameters ( IsIndependentRun,
  861.                          SimulationPeriod,  ArrivalIntervalEV );
  862.  
  863.   SpecifyQueues     ( NumberOfQueues );
  864.   InitializeServers ( NumberOfQueues );
  865.   RedirectInput     ( "CON" );         (* allow further input from keyboard *)
  866.   TestParameters    ( );
  867.  
  868.   IF  OperatorApproves ( )  THEN
  869.     RunTheSimulation ( );
  870.     DisplaySummary ( QueueList, ServerList, NumberOfQueues );
  871.   END;
  872.  
  873.   CleanUp ( );
  874. END  QueuExample.
  875.