chapter 8 class resource and its sub-classes class resource is not meant to be used in its own right; it defines the common portion to the rather similar classes bin and res (this has not been pushed to extremes). resource object : new resource("r", 4); ------------------- ! resource ! ------------------- ! title "r" ! ! virtual:report ! ! virtual:reset ! ! obs 0 ! ! resetat 0.0 ! ! next none ! ! join(r) ! ! writetrn ! tab level ------------------- ! length 0 ! ! zeros 0 ! ! maxlength 0 ! ! qint 0.0 ! ! cum 0.0 ! ! lastqtime 0.0 ! ! first none ! ! last none ! ! list ! queue level ------------------- ! avail 4 ! ! extreme 4 ! ! initial 4 ! ! users 0 ! ! sint 0.0 ! ! lastrtime 0.0 ! ! report ! ! reset ! resource level ------------------- 1class resource and its sub-classes page 8-2 outline tab class resource(avail); integer avail; begin integer extreme, initial, users; real sint, lastrtime; procedure report; procedure reset; actions: initial := avail; end***resource***; actions on generation, a waitq object first executes the actions of its tab prefix (which curtail title to 12 characters, if longer, and then call reset); and then those of its queue prefix (nil). the main class body actions set initial to the actual parameter value of avail. attributes (for title, obs, resetat, next, join, writetrn, see tab, page 2-4; for length, zeros, maxlength, qint, cum, lastqtime, first, last, list, report, reset, see queue, page 7-2.) integer avail reflects the currently available amount of the modelled resource. the invariants: res: 0 <= avail <= initial bin: 0 <= avail are maintained by the acquire/release and take/give routines respectively. integer extreme records the minimum value of avail attained since resetat for a res; and the maximum value of avail attained since resetat for a bin. extreme is maintained by calls on acquire and give respectively. integer initial retains the initial value of the res or bin (for reporting purposes). integer users records the number of completed calls on release (res) or give (bin). obs records the number of successful calls on acquire and take respectively. 1class resource and its sub-classes page 8-3 real sint is used to maintain a measure of the resource usage since resetat. it holds the time integral of avail. in the case of a res object, this enables speedy computation of the res usage since resetat as a % of the maximum usage (time-resetat) * initial. in the case of a bin object, this enables computation of the average number of bin units available: remember that there is no upper limit here. real lastrtime holds the time at which the last change to avail was made; or resetat if no changes have been made since. procedure report (which is shared by res and bin) sends one line to outf consisting of: title in columns 1-12 resetat in columns 14-23 users (number of completed calls on release if res, or give if bin) in columns 24-30 initial value of the resource in columns 31-35 extreme value of the resource in columns 36-40 (= maximum reached since resetat if res; minimum value reached since resetat if bin) avail, the currently available number of resource units in columns 41-45 if res, the res usage (% of the maximum possible usage = (time-resetat) * initial) since resetat; if bin, the average number of units available since resetat; in columns 46-55 the average wait time of waiting entities since resetat in columns 56-65 the maximum queue length for waiting entities since resetat in columns 66-70 n.b. columns 46-55 are skipped if (time-resetat) < epsilon; columns 56-65 are skipped if obs = 0. notice also that certain portions of the queue statistics are not used in this report. but they can be made use of should you wish to write your own report routines. procedure reset sets obs and zeros to zero; maxlength to length; lastqtime, lastrtime, and resetat to time; qint, sint, and cum to zero; and extreme to avail. 1class resource and its sub-classes page 8-4 8.1 class res res objects model the mutual exclusion synchronisation. entities acquire a portion of a res if sufficient of the res is available, and they have highest priority amongst entities waiting. otherwise, they wait passively in the blocked entity queue. when a resource (or portions of it) are returned, avail is updated and the blocked queue is tested to see who can go. res object : new res("tugs", 2); ------------------- ! res ! ------------------- ! title "tugs" ! ! virtual:report ! ! virtual:reset ! ! obs 0 ! ! resetat 0.0 ! ! next none ! ! join(r) ! ! writetrn ! tab level ------------------- ! length 0 ! ! zeros 0 ! ! maxlength 0 ! ! qint 0.0 ! ! cum 0.0 ! ! lastqtime 0.0 ! ! first none ! ! last none ! ! list ! queue level ------------------- ! avail 2 ! ! extreme 2 ! ! initial 2 ! ! users 0 ! ! sint 0.0 ! ! lastrtime 0.0 ! ! report ! ! reset ! resource level ------------------- ! acquire(n) ! ! release(n) ! res level ------------------- 1class resource and its sub-classes page 8-5 outline resource class res; begin procedure acquire(n); integer n; procedure release(n); integer n; actions: if avail < 1 then error; into(resq); end***res***; actions on generation, a res object first executes the actions of its tab prefix (which curtail title to 12 characters, if longer, then call reset); then those of its queue prefix (nil); and finally those of its resource prefix (which initialise initial to avail). the main class body actions check that avail is initialised to a sensible value (>= 1); if not a demos error is reported. then the res object is entered into resq. attributes (for title, obs, resetat, next, join, writetrn, see tab, page 2-4; for length, zeros, maxlength, qint, cum, lastqtime, first, last, list, report, reset, see queue, page 7-2; for avail, extreme, initial, users, sint, lastrtime, report, reset, see resource, page 8-2.) procedure acquire(n); integer n; sees to it that a request for n units by an entity e is not granted until e == first and avail >= n the routine checks that n is sensible (1 <= n <= initial) giving a demos error if not. then current (== e) is entered into the res queue and passivated if it is not both first and its request can be granted. (this is different from a gpss capacity where only avail >= n is tested.) there it remains until tested (by a later call on release) and the above condition becomes true. when tested and the condition is true, e leaves the blocked queue and does some book keeping on avail. then (this is only relevant for a delayed item) if the first blocked entity can now go, it is activated behind e. thus if an unblocking releases several entities they are peeled off one at a time, not interrupting current, but placed behind it in the event list. 1class resource and its sub-classes page 8-6 acquire(n); current.into(this res); while current =/= first or n > avail do passivate; current.out; avail := avail - n; first.insertdelay0; procedure release(n); integer n; checks to see that n is sensible (1 <= n <= initial - avail) and gives a demos error if not. (n.b. a full implementation could do better than this and check that n <= current's share of this res .) then avail is updated. if the first entity in the event list can now go, it is entered into the event list delay 0. in the intervening time it takes before it becomes current, its intended share is taken by some other entity, it will be passivated again. release(n); avail := avail + n; first.insertaftercurrent; example of use customers enter a corner shop and look for a basket. if none can be found they quit in disgust. otherwise they take a basket and shop. when leaving, they queue to pay at a single checkout. ref(count)disgusted; ref(res)baskets, checkout; entity class customer; if baskets.avail = 0 then disgusted.update(1) else begin baskets.acquire(1); shop; checkout.acquire(1); pay; checkout.release(1); baskets.release(1); end***customer***; typical report c o u n t s *********** title / (re)set/ obs disgusted 8.000 4 1class resource and its sub-classes page 8-7 r e s o u r c e s ***************** title / (re)set/ obs/ lim/ min/ now/ % usage/ av. wait/qmax baskets 8.000 25 20 0 1 81.347 0.000 1 check out 8.000 25 1 0 1 41.793 0.812 4 1class resource and its sub-classes page 8-8 8.2 class bin bin objects are used to model the producer consumer synchronisation. producers make items which are to be used by consumers. a consumer is blocked if no item is available when requested. classes bin and res are rather similar but are worth implementing separately as bins have no upper limit. thus slightly different reports are in order, and since the same entity returns acquired resources to a res, a tight check can be made on res usage (not yet implemented). bin object : new bin("widgets", 4); ------------------- ! bin ! ------------------- ! title "widgets" ! ! virtual:report ! ! virtual:reset ! ! obs 0 ! ! resetat 0.0 ! ! next none ! ! join(r) ! ! writetrn ! tab level ------------------- ! length 0 ! ! zeros 0 ! ! maxlength 0 ! ! qint 0.0 ! ! cum 0.0 ! ! lastqtime 0.0 ! ! first none ! ! last none ! ! list ! queue level ------------------- ! avail 4 ! ! extreme 4 ! ! initial 4 ! ! users 0 ! ! sint 0.0 ! ! lastrtime 0.0 ! ! report ! ! reset ! resource level ------------------- ! take(n) ! ! give(n) ! bin level ------------------- 1class resource and its sub-classes page 8-9 outline resource class bin; begin procedure take(n); integer n; procedure give(n); integer n; actions: if avail < 0 then error; into(binq); end***bin***; actions on generation, a bin object first executes the actions of its tab prefix (which curtail title to 12 characters, if longer, then call reset); then those of its queue prefix (nil); and finally those of its resource prefix (which initialise initial to avail). the main class body actions check to see that avail >= 0 and give a demos entity if not. then the object is placed into binq. attributes (for title, obs, resetat, next, join, writetrn, see tab, page 2-4; for length, zeros, maxlength, qint, cum, lastqtime, first, last, list, report, reset, see queue, page 7-2; for avail, extreme, initial, users, sint, lastrtime, report, reset, see resource, page 8-2.) procedure take(n); integer n; sees to it that a request for n units by an entity e is not granted until e == first and avail >= n the routine checks that n is sensible (n > 0) giving a demos error if not. then current (== e) is entered into the bin queue and passivated if it is not both first and its request can be granted. there it remains until tested (by a later call on give) and the above condition becomes true. when tested and the condition is true, e leaves the blocked queue and does some book keeping on avail. then (this is only relevant for a delayed item) if the first blocked entity can now go, it is activated behind e. thus if an unblocking releases several entities they are peeled off one at a time, not interrupting current, but placed behind it in the event list. 1class resource and its sub-classes page 8-10 take(n); current.into(this bin); while current =/= first or n > avail do passivate; current.out; avail := avail - n; first.insertdelay0; procedure give(n); integer n; checks to see that m is sensible (n >= 0) and gives a demos error if not. then avail is updated. if the first entity in the event list can now go, it is entered into the event list delay 0. in the intervening time it takes before it becomes current, its intended share is taken by some other entity, it will be passivated again. give(n); avail := avail + n; first.insertaftercurrent; example of use a small production line involves two processes: assembly and then packing. packers pack two previously assembled items per box. initially three items are assembled ready for packing (left over from the previous day. ref(count)done; ref(bin)assembled; ]assembled :- new bin("assembled", 3); done :- new count("items packed");[ entity class assembler; begin hold(assemble time); assembled.give(1); repeat; end***assembler***; entity class packer; begin assembled.take(1); hold(pack time); done.update(1); repeat; end***packer***; 1class resource and its sub-classes page 8-11 typical report c o u n t s *********** title / (re)set/ obs done 0.000 167 b i n s ******* title / (re)set/ obs/init/ max/ now/ av. free/ av. wait/qmax assembled 0.000 166 2 4 0 1.083 5.344e-02 1 1 chapter 9 odds and ends 9.1 some primitive reporting aids the demos report and trace routines repeatedly use certain more primitive routines. these are: procedure clocktime which sends the current clocktime (centred) on a fresh line to outf. procedure box(t); value t; text t; which sends the text value t centred inside a rectangular frame of asterisks (5 rows by 70 columns) and then two blank lines to outf. procedure printreal(x); real x; which sends to outf a field of width 10 characters containing the real value x. fixed point (3 decimal places) where possible; otherwise floating point. if the fixed point representation admits only 2 significant figures x is printed floating point. procedure edit(t, n); value t; text t; integer n; concatenates t with blanks(2).putint(n), where n = abs(n) modulo 100. if t.length > 10, then t is first subbed down to length 10. 1odds and ends page 9-2 9.2 tracing demos traces are sent to outf. tracing in demos is controlled by an integer flag zyqtrace, initially zero. every time a resource is requested, acquired, every time an entity holds, etc., etc., code inside the controlling routine tests zyqtrace. if it is positive, a one line print out of the current event is sent to outf, e.g. 1.237 furnace 1 acquires 3 of power if zyqtrace is zero, the printout is skipped. zyqtrace is incremented by each call on the global routine trace; and is decremented (if positive) by each call on the global routine notrace. outline procedure note(index, action, e, l, t1, n); value action; text action; integer index, n; real t1; ref(entity)e; ref(tab)l; begin print time in columns 1-10; print current.title in columns 12-23 (left justified); print action in columns 25-35; branchforrestofline: case index of begin 1: e.coopt; 2: e.schedule(t); 3: current terminates; 4: res.acquire(n) - blocked; 5: res.acquire(n) - seizes; 6: res.release(n); 7: bin.take(n) - blocked; 8: bin.take(n) - seizes; 9: bin.give(n); 10-13: not in use; 14: q.wait - blocked; 15: q.find - blocked; 16: q.find - finds; 17: q.coopt - blocked; 18: q.coopt - coopts; 19: q.waituntil - blocked; 20: q.waituntil - leaves; 21: hold(t); 22: passivate; 23: e.cancel; 24: e.interrupt(n); 25: q.signal; end***case***; outf.outimage; end***note***; 1odds and ends page 9-3 procedure trace; begin if zyqtrace = 0 then print boxed heading - trace starts; zyqtrace := zyqtrace + 1; end***trace***; procedure notrace; begin zyqtrace := zyqtrace - 1; if zyqtrace = 0 then print boxed heading - trace off; end***notrace***; integer zyqtrace; real zyqnotelastt; ref(entity)zyqnotelaste; trace related quantities integer zyqtrace is the controlling flag for event tracing. it is incremented by trace and decremented by notrace (when positive). each call on note is of the form if zyqtrace > 0 then note(......); real zyqnotelastt holds -15.0 (arbitrary choice) if tracing is off; otherwise it holds the clock time at which the last trace line was printed. the time columns of the trace line (1-10) are skipped if the time of this note is the same as the time of the last note (to 3 decimal places). ref(entity)zyqnotelaste references none if tracing is off; otherwise the perpetrator of the last event. if zyqnotelaste == current, columns 12-23 of the trace line are skipped. procedure trace increments zyqtrace by 1, and prints a boxed message stating that tracing commences if zyqtrace is now 1. procedure notrace has no effect if zyqtrace is negative. otherwise, zyqtrace is decremented by 1. if zyqtrace is now zero, a boxed message is printed stating that tracing has been switched off. procedure note sends one line of trace output to outf. 1odds and ends page 9-4 typical trace (switched on at time = 0.35, and off at 0.40) clock time = 0.350 ********************************************************************** * * * t r a c i n g c o m m e n c e s * * * ********************************************************************** time/ current and its action(s) 0.350 demos holds for 0.050, until 0.400 0.354 l 8 schedules l 9 at 0.400 signals sq waits in l truck q 0.361 s digger 1 schedules s 4 now coopts l 8 from l truck q holds for 0.333, until 0.694 s 4 ***terminates 0.370 l digger 2 schedules l 6 now interrupts s digger 1, with n = 2 cancels s digger 1 waits in l truck q l 6 ***terminates s digger 1 schedules l 8 now w'until in sq l 8 signals sq waits in l truck q l digger 2 coopts l 8 from l truck q holds for 0.081, until 0.451 0.376 s 5 schedules s 6 at 0.452 signals sq waits in s truck q s digger 1 leaves sq coopts s 5 from s truck q holds for 0.083, until 0.459 clock time = 0.400 ********************************************************************** * * * t r a c i n g s w i t c h e d o f f * * * ********************************************************************** 1odds and ends page 9-5 9.3 procedure error all errors and warnings generated by demos are recorded on sysout. n.b. this includes errors and warnings from dist objects generation and calls on readdist too (see chapter 4) as well as from the procedure error herein outlined. outline procedure error(no, e, q, n, call); value call; text call; integer no, n; ref(entity)e; ref(tab)q; begin print clock time; print "error in demos program"; print cause of error (given by parameter call); print who is current; case n of begin e1: e.coopt; w2: e.into(q); - q == none. w3: e.into(q); - e is already in a queue. e4: r.acquire(n); - n < 0. e5: r.acquire(n); - n > r.limit. e6: b.take(n); - n < 0. e7: r.release(n); - n < 0. e8: r.release(n); - n > r.limit. e9: b.give(n); - n < 0. e10: new res(title, l); - l < 1. e11: new bin(title, l); - l < 1. w12: x.cancel; - x is idle. e13: x.cancel; - x==current==root, i.e. x only entity. e14: e.schedule(t); - e is terminated. e15: ****implementation error****; w16: e.schedule(t); - e already scheduled. w17: e.cancel; - e is idle. w18: q.join(r); - r == none. w19: new histo(t,l,u,n);- l > u. e20: new empirical(t,s);- s = 1. end; if fatal then begin close inf, outf if redirected; print "deliberately induced rte"; n := 0; n := 1/n; end; end***error***; case labels marked e induce fatal errors; case labels marked w induce warnings from which recoveries are made. 1odds and ends page 9-6 examples of error messages given ref(res)r and the initialisation r :- new res("harbour", 5); a subsequent call 'r.acquire(6);' would generate something like ---------------------------------------------------------------------- ----------------------------serious error---------------------------- ---------------------------------------------------------------------- **cause : call on 'r.acquire(n); ref(res)r; integer n;'. current == boat 8 n = 6 request for too many units from res 'harbour'. set 0 < n <= r.limit (= 5) ---------------------------------------------------------------------- ---------------------------program aborted--------------------------- ---------------------------------------------------------------------- as a second example, consider an attempt to cancel an idle entity. this does not cause the program to stop but does generate a warning on sysout. ---------------------------------------------------------------------- ----------------------------serious error---------------------------- ---------------------------------------------------------------------- **cause : call on 'e.cancel; ref(entity)e;'. current == battleship18 attempt to cancel non-scheduled entity e == 'sub 4'. statement ignored. 1odds and ends page 9-7 9.4 global report and reset all reports are sent to outf. report is called automatically at the end of each demos program unless switched off. report can be switched off by a call on the global routine noreport. should you wish to report only selected facilities, say the res and bin objects only, you may do so by such coding as ref(reportq)r; for r :- resq, binq do r.report; similarly for reset. procedure reset is a global routine which cycles down the demos reportqs and calls the reset routine local to every user-generated demos facility therein. outline boolean zyqreport; (= true initially) procedure noreport; zyqreport := true; procedure report; begin ref(reportq)r; clocktime; box("r e p o r t"); for r :- distq, empq, accumq, countq, tallyq, histoq, resq, binq, queueq, condq, waitq do begin if r =/= none then begin 2 newlines; r.report; end; end; end***report***; procedure reset; begin ref(reportq)r; for r :- distq, empq, accumq, countq, tallyq, histoq, resq, binq, queueq, condq, waitq do begin r.reset; end; end***reset***; 1odds and ends page 9-8 report and reset related quantities boolean zyqreport controls the final simulation report. if it is true, then such a report is issued; if it is false (set by a call on noreport) then the automatic final report is skipped. procedure noreport sets zyqreport to false. procedure report is a global procedure which may be called at any time and sends a report on the current status of all user-created demos facilities to outf. procedure reset resets each user-created demos facility. example of use suppose a simulation is to last for 1000 time units, takes 100 units to settle down from a cold start and we wish reports every 300 time units thereafter. instead of a hold(1000.0) in the demos block, we put hold(100.0); reset; for k := 1 step 1 until 3 do ]integer k;[ begin report; reset; end; noreport; the call on noreport stops the now unwanted automatic report which would duplicate (more or less - they are separated by a hold(0.0)) the third explicit report call. 1odds and ends page 9-9 9.5 queue and event list snapping both these routines send snapshots to outf. snapqueues lists out all entities currently waiting in user generated queues, waitqs, and condqs (in that order). snapsqs traverses the event list and prints out the scheduled entities in order. outline proceure snapqueues; begin ref(reportq)r; clocktime; box("list of all passive objects"); for q :- queueq.first, condq.first, waitq.first do begin if q =/= none then begin q.list; q :- q.next; end; end; end***snapqueues***; procedure snapsqs; begin clocktime; box("event list"); traverse the event list from current to root and print on one line for each entity e: e's ordinal number in the event list in columns 1-5; e's event time in columns 6-15; e's title in columns 18-29; e.ll.title in columns 31-42; e.bl.title in columns 45-56; e.rl.title in columns 59-70; end***snapsqs***; 1odds and ends page 9-10 typical calls a call on snapqueues: clock time = 10.000 ********************************************************************** * * * l i s t o f p a s s i v e o b j e c t s * * * ********************************************************************** entities waiting in s truck q ***************************** no object priority entry in q 1 s98 0 9.940 2 s99 0 9.943 3 s 0 0 9.993 entities waiting in l truck q ***************************** no object priority entry in q **** the queue is empty. **** a call on snapsqs: clock time = 10.000 ********************************************************************** * * * e v e n t l i s t * * * ********************************************************************** n/ ev.time/obj. title / ll bl rl 1 10.000 demos l16 2 10.033 l digger 1 l16 3 10.038 l16 demos l digger 2 l digger 1 4 10.038 l digger 2 l16 s digger 1 5 10.046 s digger 1 l digger 2 s 1 6 10.064 s 1 s digger 1 1odds and ends page 9-11 9.6 epsilon - the grain of time real epsilon, set to 0.00001 by the actions of the demos prefix, is used in several routines to represent the smallest grain of time, e.g. if time - resetat < epsilon then count as zero else update; in simulations with very small or very large time scales, a different grain of time may be more appropriate. epsilon may be changed directly by assignment, for example epsilon := 0.00000005 1 chapter 10 the prefix demos the demos prefix contains the declarations of all the classes, procedures, variables documented in chapters 2 through 9. in addition, it contains certain initialising actions (setting up the event list, creating the standard reportq's, etc) and finalising actions (closing the files inf and outf perhaps, and issuing the final report). outline class demos; begin comment data collection devices; class tab(title); value title; text title; virtual: procedure reset, report; tab class count; tab class tally; tab class accumulate; tab class histogram; tab class regression; tally class notally; comment the random number generators; integer procedure zyqnextseed; procedure setseed(n); integer n; tab class dist; class reportq; dist class rdist; rdist class constant; rdist class empirical; rdist class erlang; rdist class negexp; rdist class normal; rdist class uniform; 1the prefix demos page 10-2 dist class idist; idist class poisson; idist class randint; dist class bdist; bdist class draw; procedure readdist; comment reporting aids; procedure clocktime; procedure box(t); value t; text t; text procedure edit(t, k); value t; text t; integer k; procedure printreal; comment the central declaration - entity; class entity; entity class mainprogram; class zyqenttitle; comment event list routines; real procedure time; procedure zyqpassivate; procedure passivate; procedure hold(t); real t; comment queue and its subclasses; tab class queue; queue class noqueue; queue class resource; resource class res; resource class bin; queue class waitq; queue class condq; comment the tracing routines; real zyqnotelastt; ref(entity)zyqnotelaste; integer zyqtrace; procedure trace; procedure notrace; procedure note(index, action, e, l, t1, n); value action; text action; integer index, n; 1the prefix demos page 10-3 real t1; ref(entity)e; ref(tab)l; procedure error(no, e, q, n, call); value call; integer no, n; text call; ref(entity)e; ref(tab)q; procedure abort; comment the snapping routines; procedure report; procedure noreport; procedure reset; procedure snapqueues; procedure snapsqs; comment local variables; ref(reportq)empq, tallyq, accumq, histoq, countq, regressq, distq, resq, binq, queueq, condqq, waitqq; text tallyheading, accumheading, distheading; text headingrtn, stars, minuses, zyqreason, zyqrecvry; text resheading, binheading, qheading; text array disttype(0:9); integer zyqseed, zyqmodulo; ref(infile)inf; ref(outfile)outf; ref(zyqenttitle)zyqentnames; ref(mainprogram)demos; ref(entity)root, current, zyqp; real now, simperiod, epsilon; boolean zyqreport; zyqreport := true; epsilon := 0.00001; headingrtn :-copy("title / (re)set/ obs"); accumheading:-copy("/ average/est.st.dv/ minimum/ maximum"); distheading :-copy("/type / a/ b/ seed"); tallyheading:-accumheading; resheading :-copy("/ lim/ min/ now/ % usage/ av. wait/qmax"); binheading :-copy("/init/ max/ now/ av. free/ av. wait/qmax"); qheading :-copy("/ qmax/ qnow/ q average/zeros/ av. wait"); disttype(0) :-copy("undefined"); disttype(1) :-copy("normal"); disttype(2) :-copy("uniform"); disttype(3) :-copy("erlang"); disttype(4) :-copy("randint"); disttype(5) :-copy("negexp"); disttype(6) :-copy("poisson"); disttype(7) :-copy("draw"); disttype(8) :-copy("constant"); disttype(9) :-copy("empirical"); 1the prefix demos page 10-4 accumq :- new reportq("a c c u m u l a t e s", headingrtn, accumheading); regressq:-new reportq("r e g r e s s i o n s", notext, notext); countq :- new reportq("c o u n t s", blanks(20), headingrtn); distq :- new reportq("d i s t r i b u t i o n s", headingrtn, distheading ); empq :- new reportq("e m p i r i c a l s", notext, notext); histoq :- new reportq("h i s t o g r a m s", notext, notext); tallyq :- new reportq("t a l l i e s",headingrtn,tallyheading); resq :- new reportq("r e s o u r c e s",headingrtn,resheading); binq :- new reportq("b i n s",headingrtn,binheading); queueq :- new reportq("q u e u e s",headingrtn,qheading); condqq :- new reportq("c o n d i t i o n q u e u e s", headingrtn, qheading); waitqq :- new reportq("w a i t q u e u e s", headingrtn, qheading); stars :- blanks(70); while stars.more do stars.putchar('*'); minuses :- blanks(70); while minuses.more do minuses.putchar('-'); inf :- sysin; outf :- sysout; zyqreason :- copy("**reason : "); zyqrecvry :- copy("**recovery : "); zyqmodulo := 67099547; zyqseed := 907; now := -10&20; zyqnotelastt := -15.0; comment initialise the event list; current :- root :- demos :- new mainprogram("demos"); demos.evtime := 0.0; demos.title :- demos.title.sub(1, 5); inner; comment program executed - now tidy up and report; zyqp :- demos.nextev; while (if zyqp == none then false else zyqp.evtime = time) do begin hold(0.0); zyqp :- demos.nextev; end; if zyqreport then report; if inf =/= sysin and inf.image =/= notext then inf.close; if outf =/= sysout and outf.image =/= notext then outf.close; end***demos***; 1the prefix demos page 10-5 actions the actions of the demos prefix establish two texts - stars a row of asterisks and minuses a row of minus signs. they are used in the various reports and error messages. inf and outf are set to their default values, inf == sysin and outf == sysout. two subsidiary texts, zyqreason and zyqrecvry are then created - they are used in zyqfail (see chapter 4). zyqmodulo is set to 67099547 (the base for our random number routines) and the start value for the well spread seeds, zyqseed, is set to 907. now is initialised to a large negative value, and zyqnotelastt to an 'impossible' value for time. then the event list is set up. the impersonating entity demos is installed as the first (current) and last (root) entity in the event list at time 0.0. (since demos.title is set by its class actions to "demos 1", it is set back again to just "demos". on meeting inner; the user written program is executed. on return, the while loop ensures that all actions scheduled for the current clock time are executed. note that if these actions cause further actions to be executed at this time (by releasing resources os signalling a condq, for example) then these subordinate actions are also allowed to complete (recursive). a hold(0.0); is thus not good enough. then a report is automatically issued if not switched off and finally, inf and outf are closed if they are open. attributes ref(reportq)empq is the standard reportq for holding empirical objects. ref(reportq)tallyq is the standard reportq for holding tally objects. ref(reportq)accumq is the standard reportq for holding accumulate objects. ref(reportq)histoq is the standard reportq for holding histogram objects. ref(reportq)regressq is the standard reportq for holding regression objects. ref(reportq)countq is the standard reportq for holding count objects. ref(reportq)distq is the standard reportq for holding dist objects; i.e. objects of the classes erlang, constant, normal, negexp, uniform, randint, poisson, draw. 1the prefix demos page 10-6 ref(reportq)resq is the standard reportq for holding res objects. ref(reportq)binq is the standard reportq for holding bin objects. ref(reportq)queueq is the standard reportq for holding queue objects. ref(reportq)condqq is the standard reportq for holding condq objects. ref(reportq)waitqq is the standard reportq for holding waitq objects. text headingrtn holds the common portion of most of the report headings, namely "title / (re)set/ obs" it is used in the routine writetrn local to class tab. text accumheading is used in reports on accumulate objects. the heading for these reports consists of a single line, first headingrtn (columns 1 -30) and then accumheading (columns 31 - 70). the text value is: "/ average/est.st.dev./ minimum/ maximum" the text object is also referenced by tallyheading (i.e. if you change one, you change them both). text tallyheading is used in reports on tallys. the heading for these reports consists of a single line, first headingrtn (columns 1 - 30) and then tallyheading (columns 31 - 70). tallyheading references he same text object as accumheading. text distheading is used in reports on dists (but not empiricals). the heading for these reports consists of a single line, first headingrtn (columns 1 - 30) and then distheading (columns 31 - 70). the text value is: "/type / a/ b/ seed" text resheading is used in reports on ress. the heading for these reports consists of a single line, first headingrtn (columns 1 - 30) and then resheading (columns 31 - 70). the text value is: "/ lim/ min/ now/ % usage/ av. wait/qmax" text qheading is used in reports on queues (all types: queue, waitq, condq). the heading for these reports consists of a single line, first headingrtn (columns 1 - 30) and then qheading (columns 31 - 70). the text value is: "/ qmax/ qnow/ q average/zeros/ av. wait" 1the prefix demos page 10-7 text reason is used in the dist failure routines. text zyqrecvry is used in the routine zyqfail local to class empirical. text stars is a text of length 70 asterisks. it is used frequently in reports and snapping routines (often subbed). text minuses is a text of length 70 minus signs. it is used in the error routines (to make their headings and tails stand out), and also in reports should any fields be null (no readings made, for example). text array disttype(0:9) is used in dist reports and error messages. each dist subclass is given its own identifying type. using this as index into disttype is a cheap way of returning the class identifier. disttype is initialised by the actions of the demos prefix to: disttype(0) = "undefined" disttype(1) = "normal" disttype(2) = "uniform" disttype(3) = "erlang" disttype(4) = "randint" disttype(5) = "negexp" disttype(6) = "poisson" disttype(7) = "draw" disttype(8) = "constant" disttype(9) = "empirical" remember to extend disttype should you add in your own extra sub-classes to dist. integer zyqseed is the base for the well spread seeds. it is set to 907. integer zyqmodulo is the divisor in the demos random number generator (see chapter 4). it is initialised to 67099547. ref(infile)inf is the input file for reading in distributions. it is initialised to == sysin and automatically closed by demos. if you wish to read in integers, reals, etc. from inf then do so by inf.inint, inf.inreal, etc. or use inspect. see also chapter 11. ref(outfile)outf is the output file for traces and reports, but not errrors (which go to sysout). outf is initialised to sysout, but may be reassigned by the user. it is closed automatically by demos. see also chapter 11. ref(zyqenttitle)zyqentnames is a behind the scenes reference to a list of entity names valid for this program. each time an entity object is generated, its title is compared with the list of names in this list. if found, title is concatenated with the appropriate serial number for this class, if not found, a fresh entry is made into the list, and title is concatenated with " 1". 1the prefix demos page 10-8 ref(mainprogram)demos is a reference to the prefix block impersonating entity. see further in chapter 5.2. ref(entity)root is a reference to the last entity in the event list. it is initialised to demos (see immediately above) and thereafter maintained by the event list routines. ref(entity)current is a reference to the first entity in the event list. it is initialised to demos (see above) and thereafter maintained by the event list routines. ref(entity)zyqp is used by the system at the end of a simulation run to locate demos.nextev. if its evtime = time, then demos is held for 0.0, thus ensuring that all events that should be carried out at the final clock time, are caried out. real now is a constant set to -10&20. it is used in scheduling when one wishes to preempt current instead of entering the event list delay 0 (write schedule(now)). real epsilon is the finest grain of time recognised by the report routines. for example, an entity which leaves a queue less than epsilon after having joined it, is counted as a zero. it may be reset from its initial value of 0.00001. see also chapter 9.6. boolean zyqreport is the switch, initially true, which decides whether or not a final report should automatically be given. see chapter 9.5. 1 chapter 11 redirecting input and output in demos the only input routine defined in demos is readdist which reads dist descriptions from inf. the many output routines (report, trace) output sequentially to outf (warnings and errors go straight to sysout). initially, inf == sysin and outf == sysout. inf and outf may be dynamically reassigned by the programmer as often as is required. the best demos can do is to automatically close inf and outf at program end should they still be open. (this includes when the program has been deliberately aborted due to demos detected errors.) but if several such files have been created, you should explicitly close all but the last yourself. similarly for a sequence of infiles. class demos; begin comment***definition of i/o files***; ref(infile)inf; ref(outfile)outf; inf :- sysin; outf :- sysout; inner; (program is executed) closefiles: if inf =/= sysin and inf.image =/= notext then inf.close; if outf =/= sysout and outf.image =/= notext then outf.close; end***demos***; examples of use reading from sysin: demos begin integer n; ref(idist)r; n := inint; readdist(r, "r"); ................. end; 1redirecting input and output in demos page 11-2 reading from inf: demos begin integer n; ref(rdist)r; inf :- new infile("data"); inf.open(blanks(80)); ............... n := inf.inint; readdist(r, "r"); end; or demos begin integer n; ref(rdist)r; inf :- new infile("data"); inf.open(blanks(80)); ............. inspect inf do begin n := inint; readdist(r, "r"); end; end; keeping the report on sysout and diverting the trace to another file: demos begin ............. outf :- new outfile("trace file"); outf.open(blanks(70)); trace; start the simulation off; hold(simulation period); outf.close; outf :- sysout; end writing the trace to one file, the report to another: demos begin ............. outf :- new outfile("trace file"); outf.open(blanks(70)); trace; start the simulation off; hold(simulation period); outf.close; outf :- new outfile("report file"); outf.open(blanks(70)); end