1
2
3 """SimulationTrace 2.0 Traces execution of SimPy models.
4 Implements SimPy Processes, Resources, Buffers, and the backbone simulation
5 scheduling by coroutine calls. Provides data collection through classes
6 Monitor and Tally.
7 Based on generators (Python 2.3 and later; not 3.0)
8
9 LICENSE:
10 Copyright (C) 2002, 2005, 2006, 2007, 2008 Klaus G. Muller, Tony Vignaux
11 mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz
12
13 This library is free software; you can redistribute it and / or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 - 1307 USA
26 END OF LICENSE
27
28
29 **Change history:**
30 9 May 03: SimulationTrace module based on SimPy 1.3
31
32 12 / 5/2003: Changed eventlist handling from dictionary to bisect
33
34 9 / 6/2003: - Changed eventlist handling from pure dictionary to bisect-
35 sorted 'timestamps' list of keys, resulting in greatly
36 improved performance for models with large
37 numbers of event notices with differing event times.
38 =========================================================
39 This great change was suggested by Prof. Simon Frost.
40 Thank you, Simon! This version 1.3 is dedicated to you!
41 =========================================================
42 - Added import of Lister which supports well - structured
43 printing of all attributes of Process and Resource instances.
44
45 November 03: Brought up to Simulation 1.4alpha
46
47 13 Dec 2003: Merged in Monitor and Histogram
48
49 27 Feb 2004: Repaired bug in activeQ monitor of class Resource. Now actMon
50 correctly records departures from activeQ.
51
52 19 May 2004: Added erroneously omitted Histogram class.
53
54 5 Sep 2004: Added SimEvents synchronization constructs
55
56 17 Sep 2004: Added waituntil synchronization construct
57
58 01 Dec 2004: SimPy version 1.5
59 Changes in this module: Repaired SimEvents bug re proc.eventsFired
60
61 12 Jan 2005: SimPy version 1.5.1
62 Changes in this module: Monitor objects now have a default name
63 'a_Monitor'
64
65 29 Mar 2005: Start SimPy 1.6: compound 'yield request' statements
66
67 05 Jun 2005: Fixed bug in _request method -- waitMon did not work properly in
68 preemption case
69
70 09 Jun 2005: Added test in 'activate' to see whether 'initialize()' was called first.
71
72 23 Aug 2005: - Added Tally data collection class
73 - Adjusted Resource to work with Tally
74 - Redid function allEventNotices() (returns prettyprinted string with event
75 times and names of process instances
76 - Added function allEventTimes (returns event times of all scheduled events)
77
78 16 Mar 2006: - Added Store and Level classes
79 - Added 'yield get' and 'yield put'
80
81 10 May 2006: - Repaired bug in Store._get method
82 - Repaired Level to allow initialBuffered have float value
83 - Added type test for Level get parameter 'nrToGet'
84
85 06 Jun 2006: - To improve pretty - printed output of 'Level' objects, changed attribute
86 _nrBuffered to nrBuffered (synonym for amount property)
87 - To improve pretty - printed output of 'Store' objects, added attribute
88 buffered (which refers to _theBuffer)
89
90 25 Aug 2006: - Start of version 1.8
91 - made 'version' public
92 - corrected condQ initialization bug
93
94 30 Sep 2006: - Introduced checks to ensure capacity of a Buffer > 0
95 - Removed from __future__ import (so Python 2.3 or later needed)
96
97 15 Oct 2006: - Added code to register all Monitors and all Tallies in variables
98 'allMonitors' and 'allTallies'
99 - Added function 'startCollection' to activate Monitors and Tallies at a
100 specified time (e.g. after warmup period)
101 - Moved all test / demo programs to after 'if __name__ == '__main__':'.
102
103 17 Oct 2006: - Added compound 'put' and 'get' statements for Level and Store.
104
105 18 Oct 2006: - Repaired bug: self.eventsFired now gets set after an event fires
106 in a compound yield get / put with a waitevent clause (reneging case).
107
108 21 Oct 2006: - Introduced Store 'yield get' with a filter function.
109
110 22 Oct 2006: - Repaired bug in prettyprinting of Store objects (the buffer
111 content==._theBuffer was not shown) by changing ._theBuffer
112 to .theBuffer.
113
114 04 Dec 2006: - Added printHistogram method to Tally and Monitor (generates
115 table - form histogram)
116
117 07 Dec 2006: - Changed the __str__ method of Histogram to print a table
118 (like printHistogram).
119
120 18 Dec 2006: - Added trace printing of Buffers' 'unitName' for yield get and put.
121
122 09 Jun 2007: - Enabled tracing of 'activate' and 'passivate'.
123 - Cleaned out all uses of 'object' to prevent name clash.
124 18 Nov 2007: - Start of 1.9 development
125 - Added 'start' method (alternative to activate) to Process
126
127 22 Nov 2007: - Major change to event list handling to speed up larger models:
128 * Drop dictionary
129 * Replace bisect by heapq
130 * Mark cancelled event notices in unpost and skip them in
131 nextev (great idea of Tony Vignaux))
132
133 4 Dec 2007: - Added twVariance calculation for both Monitor and Tally (gav)
134
135 5 Dec 2007: - Changed name back to timeVariance (gav)
136
137 1 Mar 2008: - Start of 1.9.1 bugfix release
138 - Delete circular reference in Process instances when event
139 notice has been processed (caused much circular garbage)
140 - Added capability for multiple preempts of a process
141
142 14 Aug 2008: - Removed most classes / methods and imported them from
143 Simulation.py instead (Stefan Scherfke)
144 - Moved remaining functions to SimulationTrace and added some
145 methods for backward compatibility
146
147 """
148
149 from SimPy.Lister import *
150 from SimPy.Simulation import *
151
152
153 __TESTING = False
154 version = __version__ = '2.0 $Revision: 163 $ $Date: 2008-12-15 12:47:44 +0100 (Mo, 15 Dez 2008) $'
155 if __TESTING:
156 print 'SimPy.SimulationTrace %s' %__version__,
157 if __debug__:
158 print '__debug__ on'
159 else:
160 print
161
163
167
171
172 - def activate(self, obj, process, at = 'undefined', delay = 'undefined', prior = False):
173 """Application function to activate passive process."""
174 if self._e is None:
175 raise FatalSimerror\
176 ('Fatal error: simulation is not initialized (call initialize() first)')
177 if not (type(process) == types.GeneratorType):
178 raise FatalSimerror('Activating function which'+
179 ' is not a generator (contains no \'yield\')')
180 if not obj._terminated and not obj._nextTime:
181
182 obj._nextpoint = process
183 if at == 'undefined':
184 at = self._t
185 if delay == 'undefined':
186 zeit = max(self._t, at)
187 else:
188 zeit = max(self._t, self._t + delay)
189 self.trace.recordActivate(who = obj, when = zeit, prior = prior)
190 self._e._post(obj, at = zeit, prior = prior)
191
192 - def reactivate(self, obj, at = 'undefined', delay = 'undefined', prior = False):
193 """Application function to reactivate a process which is active,
194 suspended or passive."""
195
196 if not obj._terminated:
197 a = Process('SimPysystem',sim=self)
198 a.cancel(obj)
199
200 if at == 'undefined':
201 at = self._t
202 if delay == 'undefined':
203 zeit = max(self._t, at)
204 else:
205 zeit = max(self._t, self._t + delay)
206 self.trace.recordReactivate(who = obj, when = zeit, prior = prior)
207 self._e._post(obj, at = zeit, prior = prior)
208
210 """Schedules Processes / semi - coroutines until time 'until'"""
211
212 """Gets called once. Afterwards, co - routines (generators) return by
213 'yield' with a cargo:
214 yield hold, self, <delay>: schedules the 'self' process for activation
215 after < delay > time units.If <,delay > missing,
216 same as 'yield hold, self, 0'
217
218 yield passivate, self : makes the 'self' process wait to be re - activated
219
220 yield request, self,<Resource > [,<priority>]: request 1 unit from < Resource>
221 with < priority > pos integer (default = 0)
222
223 yield release, self,<Resource> : release 1 unit to < Resource>
224
225 yield waitevent, self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]:
226 wait for one or more of several events
227
228
229 yield queueevent, self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]:
230 queue for one or more of several events
231
232 yield waituntil, self, cond : wait for arbitrary condition
233
234 yield get, self,<buffer > [,<WhatToGet > [,<priority>]]
235 get < WhatToGet > items from buffer (default = 1);
236 <WhatToGet > can be a pos integer or a filter function
237 (Store only)
238
239 yield put, self,<buffer > [,<WhatToPut > [,priority]]
240 put < WhatToPut > items into buffer (default = 1);
241 <WhatToPut > can be a pos integer (Level) or a list of objects
242 (Store)
243
244 EXTENSIONS:
245 Request with timeout reneging:
246 yield (request, self,<Resource>),(hold, self,<patience>) :
247 requests 1 unit from < Resource>. If unit not acquired in time period
248 <patience>, self leaves waitQ (reneges).
249
250 Request with event - based reneging:
251 yield (request, self,<Resource>),(waitevent, self,<eventlist>):
252 requests 1 unit from < Resource>. If one of the events in < eventlist > occurs before unit
253 acquired, self leaves waitQ (reneges).
254
255 Get with timeout reneging (for Store and Level):
256 yield (get, self,<buffer>,nrToGet etc.),(hold, self,<patience>)
257 requests < nrToGet > items / units from < buffer>. If not acquired < nrToGet > in time period
258 <patience>, self leaves < buffer>.getQ (reneges).
259
260 Get with event - based reneging (for Store and Level):
261 yield (get, self,<buffer>,nrToGet etc.),(waitevent, self,<eventlist>)
262 requests < nrToGet > items / units from < buffer>. If not acquired < nrToGet > before one of
263 the events in < eventlist > occurs, self leaves < buffer>.getQ (reneges).
264
265
266
267 Event notices get posted in event - list by scheduler after 'yield' or by
268 'activate' / 'reactivate' functions.
269
270 """
271 self._stop = False
272
273 if self._e is None:
274 raise FatalSimerror('Simulation not initialized')
275 if self._e._isEmpty():
276 message = 'SimPy: No activities scheduled'
277 return message
278
279 self._endtime = until
280 message = 'SimPy: Normal exit'
281 dispatch={hold:holdfunc, request:requestfunc, release:releasefunc,
282 passivate:passivatefunc, waitevent:waitevfunc, queueevent:queueevfunc,
283 waituntil:waituntilfunc, get:getfunc, put:putfunc}
284 commandcodes = dispatch.keys()
285 commandwords={hold:'hold', request:'request', release:'release', passivate:'passivate',
286 waitevent:'waitevent', queueevent:'queueevent', waituntil:'waituntil',
287 get:'get', put:'put'}
288 nextev = self._e._nextev
289 while not self._stop and self._t <= self._endtime:
290 try:
291 a = nextev()
292 if not a[0] is None:
293
294 if type(a[0][0]) == tuple:
295
296 command = a[0][0][0]
297 else:
298 command = a[0][0]
299 if __debug__:
300 if not command in commandcodes:
301 raise FatalSimerror('Illegal command: yield %s'%command)
302 dispatch[command](a)
303 self.trace.recordEvent(command, a)
304 else:
305 if not a == (None,):
306 self.trace.tterminated(a[1])
307 except FatalSimerror, error:
308 print 'SimPy: ' + error.value
309 sys.exit(1)
310 except Simerror, error:
311 message = 'SimPy: ' + error.value
312 self._stop = True
313 if self._wustep:
314 self._test()
315 self._stopWUStepping()
316 self._e = None
317 if not(self.trace.outfile is sys.stdout):
318 self.trace.outfile.close()
319 return message
320
322 """Handles 'yield request, self, res' and 'yield (request, self, res),(<code>,self, par)'.
323 <code > can be 'hold' or 'waitevent'.
324 """
325 if type(a[0][0]) == tuple:
326
327
328 b = a[0][0]
329
330
331
332 b[2]._request(arg = (b, a[1]))
333
334
335 class _Holder(Process):
336 """Provides timeout process"""
337 def __init__(self,name,sim=None):
338 Process.__init__(self,name=name,sim=sim)
339 def trigger(self, delay):
340 yield hold, self, delay
341 if not proc in b[2].activeQ:
342 proc.sim.reactivate(proc)
343
344 class _EventWait(Process):
345 """Provides event waiting process"""
346 def __init__(self,name,sim=None):
347 Process.__init__(self,name=name,sim=sim)
348 def trigger(self, event):
349 yield waitevent, self, event
350 if not proc in b[2].activeQ:
351 proc.eventsFired = self.eventsFired
352 proc.sim.reactivate(proc)
353
354
355 proc = a[0][0][1]
356 actCode = a[0][1][0]
357 trace.tstop()
358 if actCode == hold:
359 proc._holder = _Holder(name = 'RENEGE - hold for %s'%proc.name,
360 sim=proc.sim)
361
362 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
363 elif actCode == waituntil:
364 raise FatalSimerror('Illegal code for reneging: waituntil')
365 elif actCode == waitevent:
366 proc._holder = _EventWait(name = 'RENEGE - waitevent for %s'\
367 %proc.name,sim=proc.sim)
368
369 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
370 elif actCode == queueevent:
371 raise FatalSimerror('Illegal code for reneging: queueevent')
372 else:
373 raise FatalSimerror('Illegal code for reneging %s'%actCode)
374 trace.tstart()
375 else:
376
377 a[0][2]._request(a)
378
380 commands={hold:'hold', passivate:'passivate', request:'request', release:'release',
381 waitevent:'waitevent', queueevent:'queueevent', waituntil:'waituntil',
382 get:'get', put:'put'}
383
384 - def __init__(self, start = 0, end = 10000000000L, toTrace=\
385 ['hold', 'activate', 'cancel', 'reactivate', 'passivate', 'request',
386 'release', 'interrupt', 'terminated', 'waitevent', 'queueevent',
387 'signal', 'waituntil', 'put', 'get'
388 ],outfile = sys.stdout,sim=None):
404
406 Trace.commandsproc={hold:Trace.thold, passivatre:Trace.tpassivate,
407 request:Trace.trequest, release:Trace.trelease,
408 waitevent:Trace.twaitevent,
409 queueevent:Trace.tqueueevent,
410 waituntil:Trace.twaituntil,
411 get:Trace.tget, put:Trace.tput}
412 self.start = 0
413 self.end = 10000000000L
414 self.toTrace = ['hold', 'activate', 'cancel', 'reactivate', 'passivate', 'request',
415 'release', 'interrupt', 'terminated', 'waitevent', 'queueevent',
416 'signal', 'waituntil', 'put', 'get']
417 self.tracego = True
418 self.outfile = sys.stdout
419 self._comment = None
420
422 for v in kmvar.keys():
423 if v == 'start':
424 self.start = kmvar[v]
425 elif v == 'end':
426 self.end = kmvar[v]
427 elif v == 'toTrace':
428 self.toTrace = kmvar[v]
429 elif v == 'outfile':
430 self.outfile = kmvar[v]
431
434
437
439 if self.tracego and (self.start <= self.sim.now() <= self.end)\
440 and cond:
441 return True
442
444 try:
445 return 'delay: %s'%par[0][2]
446 except:
447 return 0
448 thold = classmethod(thold)
449
451 res = par[0][2]
452 if len(par[0]) == 4:
453 priority = ' priority: ' + str(par[0][3])
454 else:
455 priority = ' priority: default'
456 wQ = [x.name for x in res.waitQ]
457 aQ = [x.name for x in res.activeQ]
458 return '<%s> %s \n. . .waitQ: %s \n. . .activeQ: %s' % (res.name, priority, wQ, aQ)
459 trequest = classmethod(trequest)
460
462 res = par[0][2]
463 wQ = [x.name for x in res.waitQ]
464 aQ = [x.name for x in res.activeQ]
465 return '<%s> \n. . .waitQ: %s \n. . .activeQ: %s' % (res.name, wQ, aQ)
466 trelease = classmethod(trelease)
467
470 tpassivate = classmethod(tpassivate)
471
474 tactivate = classmethod(tactivate)
475
477 evt = par[0][2]
478 if type(evt) == list or type(evt) == tuple:
479 enames = [x.name for x in evt]
480 return 'waits for events <%s > '%enames
481 else:
482 return 'waits for event <%s > '%evt.name
483 twaitevent = classmethod(twaitevent)
484
486 evt = par[0][2]
487 if type(evt) == list or type(evt) == tuple:
488 enames = [x.name for x in evt]
489 return 'queues for events <%s > '%enames
490 else:
491 return 'queues for event <%s > '%evt.name
492 tqueueevent = classmethod(tqueueevent)
493
495 wQ = [x.name for x in evt.waits]
496 qQ = [x.name for x in evt.queues]
497 return '<%s> \n. . . occurred: %s\n. . . waiting: %s\n. . . queueing: %s'\
498 %(evt.name, evt.occurred, wQ, qQ)
499 pass
500 tsignal = classmethod(tsignal)
501
503 condition = par[0][2]
504 return 'for condition <%s > '%condition.func_name
505 twaituntil = classmethod(twaituntil)
506
507 - def tget(self, par):
508 buff = par[0][2]
509 if len(par[0]) == 5:
510 priority = ' priority: ' + str(par[0][4])
511 else:
512 priority = ' priority: default'
513 if len(par[0]) == 3:
514 nrToGet = 1
515 else:
516 nrToGet = par[0][3]
517 toGet = 'to get: %s %s from' % (nrToGet, buff.unitName)
518 getQ = [x.name for x in buff.getQ]
519 putQ = [x.name for x in buff.putQ]
520 try:
521 inBuffer = buff.amount
522 except:
523 inBuffer = buff.nrBuffered
524 return '%s <%s> %s \n. . .getQ: %s \n. . .putQ: %s \n. . .in buffer: %s'\
525 %(toGet, buff.name, priority, getQ, putQ, inBuffer)
526 tget = classmethod(tget)
527
528 - def tput(self, par):
529 buff = par[0][2]
530 if len(par[0]) == 5:
531 priority = ' priority: ' + str(par[0][4])
532 else:
533 priority = ' priority: default'
534 if len(par[0]) == 3:
535 nrToPut = 1
536 else:
537 if type(par[0][3]) == type([]):
538 nrToPut = len(par[0][3])
539 else:
540 nrToPut = par[0][3]
541 getQ = [x.name for x in buff.getQ]
542 putQ = [x.name for x in buff.putQ]
543 toPut = 'to put: %s %s into' % (nrToPut, buff.unitName)
544 try:
545 inBuffer = buff.amount
546 except:
547 inBuffer = buff.nrBuffered
548 return '%s <%s> %s \n. . .getQ: %s \n. . .putQ: %s \n. . .in buffer: %s'\
549 %(toPut, buff.name, priority, getQ, putQ, inBuffer)
550 tput = classmethod(tput)
551
553 if self.ifTrace(Trace.commands[command] in self.toTrace):
554 if not type(whole[0][0]) == tuple:
555 try:
556 print >> self.outfile, whole[0][1].sim.now(),\
557 Trace.commands[command],\
558 ' < ' + whole[0][1].name + ' > ',\
559 Trace.commandsproc[command](whole)
560 except TypeError:
561 print 'l.1649: whole[0][1].name', whole[0][1].name,\
562 Trace.commands[command],Trace.commandsproc[command]
563 Trace.commands[command],Trace.commandsproc[command]
564 if self._comment:
565 print >> self.outfile, '----', self._comment
566 else:
567
568 print >> self.outfile, whole[0][0][1].sim.now(),\
569 Trace.commands[command],\
570 ' < ' + whole[0][0][1].name + ' > '+\
571 Trace.commandsproc[command](whole[0])
572 print >> self.outfile, '|| RENEGE COMMAND:'
573 command1 = whole[0][1][0]
574 print >> self.outfile, '||\t', Trace.commands[command1],\
575 ' < ' + whole[0][1][1].name + ' > ',\
576 Trace.commandsproc[command1]((whole[0][1],))
577 if self._comment:
578 print >> self.outfile, '----', self._comment
579
580 self._comment = None
581
583 if self.ifTrace('interrupt' in self.toTrace):
584 print >> self.outfile, '%s interrupt by: <%s > of: <%s >'\
585 %(who.sim.now(),who.name, victim.name)
586 if self._comment:
587 print >> self.outfile, '----', self._comment
588 self._comment = None
589
591 if self.ifTrace('cancel' in self.toTrace):
592 print >> self.outfile, '%s cancel by: <%s > of: <%s > '\
593 %(who.sim.now(),who.name, victim.name)
594 if self._comment:
595 print >> self.outfile, '----', self._comment
596 self._comment = None
597
599 if self.ifTrace('activate' in self.toTrace):
600 print >> self.outfile, '%s activate <%s > at time: %s prior: %s'\
601 %(who.sim.now(),who.name,when, prior)
602 if self._comment:
603 print >> self.outfile, '----', self._comment
604 self._comment = None
605
607 if self.ifTrace('reactivate' in self.toTrace):
608 print >> self.outfile, '%s reactivate <%s > time: %s prior: %s'\
609 %(who.sim.now(),who.name,when, prior)
610 if self._comment:
611 print >> self.outfile, '----', self._comment
612 self._comment = None
613
615 if self.ifTrace('signal' in self.toTrace):
616 print >> self.outfile, '%s event <%s > is signalled' \
617 %(evt.sim.now(),evt.name)
618 if self._comment:
619 print >> self.outfile, '----', self._comment
620 self._comment = None
621
623 if self.ifTrace('terminated' in self.toTrace):
624 print >> self.outfile, '%s <%s > terminated'\
625 %(who.sim.now(),who.name)
626 if self._comment:
627 print >> self.outfile, '----', self._comment
628 self._comment = None
629
630 - def ttext(self, par):
632
633
634 Globals.sim = SimulationTrace()
635 trace = Globals.sim.trace
636
637
638 if __name__ == '__main__':
639 print 'SimPy.SimulationTrace %s' %__version__
640
642 class Aa(Process):
643 sequIn = []
644 sequOut = []
645 def __init__(self, holdtime, name,sim=None):
646 Process.__init__(self, name,sim=sim)
647 self.holdtime = holdtime
648
649 def life(self, priority):
650 for i in range(1):
651 Aa.sequIn.append(self.name)
652 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
653 len(rrr.activeQ)
654 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
655 print 'activeQ: ',[(k.name, k._priority[rrr]) \
656 for k in rrr.activeQ]
657 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
658 'Inconsistent resource unit numbers'
659 print self.sim.now(),self.name, 'requests 1 ', rrr.unitName
660 yield request, self, rrr, priority
661 print self.sim.now(),self.name, 'has 1 ', rrr.unitName
662 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
663 len(rrr.activeQ)
664 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
665 len(rrr.activeQ)
666 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
667 'Inconsistent resource unit numbers'
668 yield hold, self, self.holdtime
669 print self.sim.now(),self.name, 'gives up 1', rrr.unitName
670 yield release, self, rrr
671 Aa.sequOut.append(self.name)
672 print self.sim.now(),self.name, 'has released 1 ', rrr.unitName
673 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
674 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
675 len(rrr.activeQ)
676 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
677 'Inconsistent resource unit numbers'
678
679 class Observer(Process):
680 def __init__(self,**vars):
681 Process.__init__(self,**vars)
682
683 def observe(self, step, processes, res):
684 while self.sim.now() < 11:
685 for i in processes:
686 print '++ %s process: %s: active:%s, passive:%s, terminated: %s, interrupted:%s, queuing:%s'\
687 %(self.sim.now(),i.name, i.active(),i.passive(),\
688 i.terminated(),i.interrupted(),i.queuing(res))
689 print
690 yield hold, self, step
691
692 print'\n+++test_demo output'
693 print '****First case == priority queue, resource service not preemptable'
694 s=SimulationTrace()
695 s.initialize()
696 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
697 preemptable = 0,sim=s)
698 procs = []
699 for i in range(10):
700 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s)
701 procs.append(z)
702 s.activate(z, z.life(priority = i))
703 o = Observer(sim=s)
704 s.activate(o, o.observe(1, procs, rrr))
705 a = s.simulate(until = 10000)
706 print a
707 print 'Input sequence: ', Aa.sequIn
708 print 'Output sequence: ', Aa.sequOut
709
710 print '\n****Second case == priority queue, resource service preemptable'
711 s=SimulationTrace()
712 s.initialize()
713 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
714 preemptable = 1,sim=s)
715 procs = []
716 for i in range(10):
717 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s)
718 procs.append(z)
719 s.activate(z, z.life(priority = i))
720 o = Observer(sim=s)
721 s.activate(o, o.observe(1, procs, rrr))
722 Aa.sequIn = []
723 Aa.sequOut = []
724 a = s.simulate(until = 10000)
725 print a
726 print 'Input sequence: ', Aa.sequIn
727 print 'Output sequence: ', Aa.sequOut
728
730 class Bus(Process):
731 def __init__(self, **vars):
732 Process.__init__(self, **vars)
733
734 def operate(self, repairduration = 0):
735 print self.sim.now(),'>> %s starts' % (self.name)
736 tripleft = 1000
737 while tripleft > 0:
738 yield hold, self, tripleft
739 if self.interrupted():
740 print 'interrupted by %s' %self.interruptCause.name
741 print '%s: %s breaks down ' %(now(),self.name)
742 tripleft = self.interruptLeft
743 self.interruptReset()
744 print 'tripleft ', tripleft
745 s.reactivate(br, delay = repairduration)
746 yield hold, self, repairduration
747 print self.sim.now(),' repaired'
748 else:
749 break
750 print self.sim.now(),'<< %s done' % (self.name)
751
752 class Breakdown(Process):
753 def __init__(self, myBus,sim=None):
754 Process.__init__(self, name = 'Breakdown ' + myBus.name,sim=sim)
755 self.bus = myBus
756
757 def breakBus(self, interval):
758
759 while True:
760 yield hold, self, interval
761 if self.bus.terminated(): break
762 self.interrupt(self.bus)
763
764 print'\n\n+++test_interrupt'
765 s=SimulationTrace()
766 s.initialize()
767 b = Bus(name='Bus 1',sim=s)
768 s.activate(b, b.operate(repairduration = 20))
769 br = Breakdown(b,sim=s)
770 s.activate(br, br.breakBus(200))
771 print s.simulate(until = 4000)
772
774 class Waiter(Process):
775 def __init__(self,**vars):
776 Process.__init__(self,**vars)
777 def waiting(self, theSignal):
778 while True:
779 yield waitevent, self, theSignal
780 print '%s: process \'%s\' continued after waiting for %s' %\
781 (self.sim.now(),self.name, theSignal.name)
782 yield queueevent, self, theSignal
783 print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name)
784
785 class ORWaiter(Process):
786 def __init__(self,**vars):
787 Process.__init__(self,**vars)
788 def waiting(self, signals):
789 while True:
790 yield waitevent, self, signals
791 print self.sim.now(),'one of %s signals occurred' %\
792 [x.name for x in signals]
793 print '\t%s (fired / param)'%\
794 [(x.name, x.signalparam) for x in self.eventsFired]
795 yield hold, self, 1
796
797 class Caller(Process):
798 def __init__(self,**vars):
799 Process.__init__(self,**vars)
800 def calling(self):
801 while True:
802 signal1.signal('wake up!')
803 print '%s: signal 1 has occurred'%now()
804 yield hold, self, 10
805 signal2.signal('and again')
806 signal2.signal('sig 2 again')
807 print '%s: signal1, signal2 have occurred'%now()
808 yield hold, self, 10
809 print'\n+++testSimEvents output'
810 s=SimulationTrace()
811 s.initialize()
812 signal1 = SimEvent('signal 1',sim=s)
813 signal2 = SimEvent('signal 2',sim=s)
814 signal1.signal('startup1')
815 signal2.signal('startup2')
816 w1 = Waiter(name='waiting for signal 1',sim=s)
817 s.activate(w1, w1.waiting(signal1))
818 w2 = Waiter(name='waiting for signal 2',sim=s)
819 s.activate(w2, w2.waiting(signal2))
820 w3 = Waiter(name='also waiting for signal 2',sim=s)
821 s.activate(w3, w3.waiting(signal2))
822 w4 = ORWaiter(name='waiting for either signal 1 or signal 2',sim=s)
823 s.activate(w4, w4.waiting([signal1, signal2]),prior = True)
824 c = Caller(name='Caller',sim=s)
825 s.activate(c, c.calling())
826 print s.simulate(until = 100)
827
829 """
830 Demo of waitUntil capability.
831
832 Scenario:
833 Three workers require sets of tools to do their jobs. Tools are shared,
834 scarce resources for which they compete.
835 """
836 class Worker(Process):
837 def __init__(self, name, heNeeds = [],sim=None):
838 Process.__init__(self, name,sim=sim)
839 self.heNeeds = heNeeds
840 def work(self):
841 def workerNeeds():
842 for item in self.heNeeds:
843 if item.n == 0:
844 return False
845 return True
846
847 while self.sim.now() < 8 * 60:
848 yield waituntil, self, workerNeeds
849 for item in self.heNeeds:
850 yield request, self, item
851 print '%s %s has %s and starts job' % (self.sim.now(),self.name,
852 [x.name for x in self.heNeeds])
853 yield hold, self, random.uniform(10, 30)
854 for item in self.heNeeds:
855 yield release, self, item
856 yield hold, self, 2
857
858 print '\n+++ nwaituntil demo output'
859 random.seed(12345)
860 s=SimulationTrace()
861 s.initialize()
862 brush = Resource(capacity = 1, name = 'brush',sim=s)
863 ladder = Resource(capacity = 2, name = 'ladder',sim=s)
864 hammer = Resource(capacity = 1, name = 'hammer',sim=s)
865 saw = Resource(capacity = 1, name = 'saw',sim=s)
866 painter = Worker('painter',[brush, ladder],sim=s)
867 s.activate(painter, painter.work())
868 roofer = Worker('roofer',[hammer, ladder, ladder],sim=s)
869 s.activate(roofer, roofer.work())
870 treeguy = Worker('treeguy',[saw, ladder],sim=s)
871 s.activate(treeguy, treeguy.work())
872 for who in (painter, roofer, treeguy):
873 print '%s needs %s for his job' %\
874 (who.name,[x.name for x in who.heNeeds])
875 print
876 print s.simulate(until = 9 * 60)
877
878
879
880
881
882
883
884
885
886
888 """ Job class for testing timeout reneging
889 """
890 - def __init__(self, server = None, name = '',sim=None):
894
895 - def execute(self, timeout, usetime):
896 yield (request, self, self.res),(hold, self, timeout)
897 if self.acquired(self.res):
898 self.gotResource = True
899 yield hold, self, usetime
900 yield release, self, self.res
901 else:
902 self.gotResource = False
903
904
906 """Test that resource gets acquired without timeout
907 """
908 s=SimulationTrace()
909 s.initialize()
910 res = Resource(name = 'Server', capacity = 1,sim=s)
911 usetime = 5
912 timeout = 1000000
913 j1 = JobTO(server = res, name = 'Job_1',sim=s)
914 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime))
915 j2 = JobTO(server = res, name = 'Job_2',sim=s)
916 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime))
917 s.simulate(until = 2 * usetime)
918 assert s.now() == 2 * usetime, 'time not == 2 * usetime'
919 assert j1.gotResource and j2.gotResource,\
920 'at least one job failed to get resource'
921 assert not (res.waitQ or res.activeQ),\
922 'job waiting or using resource'
923
925 """Test that timeout occurs when resource busy
926 """
927 s=SimulationTrace()
928 s.initialize()
929 res = Resource(name = 'Server', capacity = 1, monitored = True,sim=s)
930 usetime = 5
931 timeout = 3
932 j1 = JobTO(server = res, name = 'Job_1',sim=s)
933 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime))
934 j2 = JobTO(server = res, name = 'Job_2',sim=s)
935 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime))
936 s.simulate(until = 2 * usetime)
937 assert(s.now() == usetime),'time not == usetime'
938 assert(j1.gotResource),'Job_1 did not get resource'
939 assert(not j2.gotResource),'Job_2 did not renege'
940 assert not (res.waitQ or res.activeQ),\
941 'job waiting or using resource'
942
944 """Test that timeout occurs when resource has no capacity free
945 """
946 s=SimulationTrace()
947 s.initialize()
948 res = Resource(name = 'Server', capacity = 0,sim=s)
949 usetime = 5
950 timeout = 3
951 j1 = JobTO(server = res, name = 'Job_1',sim=s)
952 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime))
953 j2 = JobTO(server = res, name = 'Job_2',sim=s)
954 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime))
955 s.simulate(until = 2 * usetime)
956 assert s.now() == timeout, 'time %s not == timeout'%now()
957 assert not j1.gotResource, 'Job_1 got resource'
958 assert not j2.gotResource, 'Job_2 got resource'
959 assert not (res.waitQ or res.activeQ),\
960 'job waiting or using resource'
961
962
963
964
965
967 """ Job class for testing event reneging
968 """
969 - def __init__(self, server = None, name = '',sim=None):
973
974 - def execute(self, event, usetime):
975 yield (request, self, self.res),(waitevent, self, event)
976 if self.acquired(self.res):
977 self.gotResource = True
978 yield hold, self, usetime
979 yield release, self, self.res
980 else:
981 self.gotResource = False
982
984 """ Job class for testing event reneging with multi - event lists
985 """
986 - def __init__(self, server = None, name = '',sim=None):
990
991 - def execute(self, eventlist, usetime):
992 yield (request, self, self.res),(waitevent, self, eventlist)
993 if self.acquired(self.res):
994 self.gotResource = True
995 yield hold, self, usetime
996 yield release, self, self.res
997 else:
998 self.gotResource = False
999
1001 """Fires reneging event
1002 """
1005 - def fire(self, fireDelay, event):
1006 yield hold, self, fireDelay
1007 event.signal()
1008
1010 """Test that processes acquire resource normally if no event fires
1011 """
1012 s=SimulationTrace()
1013 s.initialize()
1014 res = Resource(name = 'Server', capacity = 1,sim=s)
1015 event = SimEvent(name='Renege_trigger',sim=s)
1016 usetime = 5
1017 j1 = JobEvt(server = res, name = 'Job_1',sim=s)
1018 s.activate(j1, j1.execute(event = event, usetime = usetime))
1019 j2 = JobEvt(server = res, name = 'Job_2',sim=s)
1020 s.activate(j2, j2.execute(event = event, usetime = usetime))
1021 s.simulate(until = 2 * usetime)
1022
1023 assert s.now() == 2 * usetime, 'time not == 2 * usetime'
1024 assert j1.gotResource and j2.gotResource,\
1025 'at least one job failed to get resource'
1026 assert not (res.waitQ or res.activeQ),\
1027 'job waiting or using resource'
1028
1030 """Test that signalled event leads to renege when resource busy
1031 """
1032 s=SimulationTrace()
1033 s.initialize()
1034 res = Resource(name = 'Server', capacity = 1,sim=s)
1035 event = SimEvent('Renege_trigger',sim=s)
1036 usetime = 5
1037 eventtime = 1
1038 j1 = JobEvt(server = res, name = 'Job_1',sim=s)
1039 s.activate(j1, j1.execute(event = event, usetime = usetime))
1040 j2 = JobEvt(server = res, name = 'Job_2',sim=s)
1041 s.activate(j2, j2.execute(event = event, usetime = usetime))
1042 f = FireEvent(name = 'FireEvent',sim=s)
1043 s.activate(f, f.fire(fireDelay = eventtime, event = event))
1044 s.simulate(until = 2 * usetime)
1045
1046 assert(s.now() == usetime),'time not == usetime'
1047 assert(j1.gotResource),'Job_1 did not get resource'
1048 assert(not j2.gotResource),'Job_2 did not renege'
1049 assert not (res.waitQ or res.activeQ),\
1050 'job waiting or using resource'
1051
1053 """Test that renege - triggering event can be one of an event list
1054 """
1055 s=SimulationTrace()
1056 s.initialize()
1057 res = Resource(name = 'Server', capacity = 1,sim=s)
1058 event1 = SimEvent('Renege_trigger_1',sim=s)
1059 event2 = SimEvent('Renege_trigger_2',sim=s)
1060 usetime = 5
1061 eventtime = 1
1062 j1 = JobEvtMulti(server = res, name = 'Job_1',sim=s)
1063 s.activate(j1, j1.execute(eventlist = [event1, event2],usetime = usetime))
1064 j2 = JobEvtMulti(server = res, name = 'Job_2',sim=s)
1065 s.activate(j2, j2.execute(eventlist = [event1, event2],usetime = usetime))
1066 f1 = FireEvent(name = 'FireEvent_1',sim=s)
1067 s.activate(f1, f1.fire(fireDelay = eventtime, event = event1))
1068 f2 = FireEvent(name = 'FireEvent_2',sim=s)
1069 s.activate(f2, f2.fire(fireDelay = eventtime, event = event2))
1070 s.simulate(until = 2 * usetime)
1071
1072 assert(s.now() == usetime),'time not == usetime'
1073 assert(j1.gotResource),'Job_1 did not get resource'
1074 assert(not j2.gotResource),'Job_2 did not renege'
1075 assert not (res.waitQ or res.activeQ),\
1076 'job waiting or using resource'
1077
1078 testNoTimeout()
1079 testTimeout1()
1080 testTimeout2()
1081 testNoEvent()
1082 testWaitEvent1()
1083 testWaitEvent2()
1084 test_demo()
1085 test_interrupt()
1086 testSimEvents()
1087 testwaituntil()
1088