1
2 from SimPy.Lister import *
3 import heapq as hq
4 import types
5 import sys
6 import new
7 import random
8 import inspect
9
10
11 """SimulationTrace 1.9 Traces execution of SimPy models.
12 Implements SimPy Processes, Resources, Buffers, and the backbone simulation
13 scheduling by coroutine calls. Provides data collection through classes
14 Monitor and Tally.
15 Based on generators (Python 2.3 and later)
16
17 LICENSE:
18 Copyright (C) 2002,2005,2006,2007 Klaus G. Muller, Tony Vignaux
19 mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz
20
21 This library is free software; you can redistribute it and/or
22 modify it under the terms of the GNU Lesser General Public
23 License as published by the Free Software Foundation; either
24 version 2.1 of the License, or (at your option) any later version.
25
26 This library is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 Lesser General Public License for more details.
30
31 You should have received a copy of the GNU Lesser General Public
32 License along with this library; if not, write to the Free Software
33 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 END OF LICENSE
35
36
37 **Change history:**
38 9 May 03: SimulationTrace module based on SimPy 1.3
39
40 12/5/2003: Changed eventlist handling from dictionary to bisect
41
42 9/6/2003: - Changed eventlist handling from pure dictionary to bisect-
43 sorted "timestamps" list of keys, resulting in greatly
44 improved performance for models with large
45 numbers of event notices with differing event times.
46 =========================================================
47 This great change was suggested by Prof. Simon Frost.
48 Thank you, Simon! This version 1.3 is dedicated to you!
49 =========================================================
50 - Added import of Lister which supports well-structured
51 printing of all attributes of Process and Resource instances.
52
53 November 03: Brought up to Simulation 1.4alpha
54
55 13 Dec 2003: Merged in Monitor and Histogram
56
57 27 Feb 2004: Repaired bug in activeQ monitor of class Resource. Now actMon
58 correctly records departures from activeQ.
59
60 19 May 2004: Added erroneously omitted Histogram class.
61
62 5 Sep 2004: Added SimEvents synchronization constructs
63
64 17 Sep 2004: Added waituntil synchronization construct
65
66 01 Dec 2004: SimPy version 1.5
67 Changes in this module: Repaired SimEvents bug re proc.eventsFired
68
69 12 Jan 2005: SimPy version 1.5.1
70 Changes in this module: Monitor objects now have a default name
71 'a_Monitor'
72
73 29 Mar 2005: Start SimPy 1.6: compound "yield request" statements
74
75 05 Jun 2005: Fixed bug in _request method -- waitMon did not work properly in
76 preemption case
77
78 09 Jun 2005: Added test in 'activate' to see whether 'initialize()' was called first.
79
80 23 Aug 2005: - Added Tally data collection class
81 - Adjusted Resource to work with Tally
82 - Redid function allEventNotices() (returns prettyprinted string with event
83 times and names of process instances
84 - Added function allEventTimes (returns event times of all scheduled events)
85
86 16 Mar 2006: - Added Store and Level classes
87 - Added 'yield get' and 'yield put'
88
89 10 May 2006: - Repaired bug in Store._get method
90 - Repaired Level to allow initialBuffered have float value
91 - Added type test for Level get parameter 'nrToGet'
92
93 06 Jun 2006: - To improve pretty-printed output of 'Level' objects, changed attribute
94 _nrBuffered to nrBuffered (synonym for amount property)
95 - To improve pretty-printed output of 'Store' objects, added attribute
96 buffered (which refers to _theBuffer)
97
98 25 Aug 2006: - Start of version 1.8
99 - made 'version' public
100 - corrected condQ initialization bug
101
102 30 Sep 2006: - Introduced checks to ensure capacity of a Buffer > 0
103 - Removed from __future__ import (so Python 2.3 or later needed)
104
105 15 Oct 2006: - Added code to register all Monitors and all Tallies in variables
106 'allMonitors' and 'allTallies'
107 - Added function 'startCollection' to activate Monitors and Tallies at a
108 specified time (e.g. after warmup period)
109 - Moved all test/demo programs to after 'if __name__=="__main__":'.
110
111 17 Oct 2006: - Added compound 'put' and 'get' statements for Level and Store.
112
113 18 Oct 2006: - Repaired bug: self.eventsFired now gets set after an event fires
114 in a compound yield get/put with a waitevent clause (reneging case).
115
116 21 Oct 2006: - Introduced Store 'yield get' with a filter function.
117
118 22 Oct 2006: - Repaired bug in prettyprinting of Store objects (the buffer
119 content==._theBuffer was not shown) by changing ._theBuffer
120 to .theBuffer.
121
122 04 Dec 2006: - Added printHistogram method to Tally and Monitor (generates
123 table-form histogram)
124
125 07 Dec 2006: - Changed the __str__ method of Histogram to print a table
126 (like printHistogram).
127
128 18 Dec 2006: - Added trace printing of Buffers' "unitName" for yield get and put.
129
130 09 Jun 2007: - Enabled tracing of "activate" and "passivate".
131 - Cleaned out all uses of "object" to prevent name clash.
132 18 Nov 2007: - Start of 1.9 development
133 - Added 'start' method (alternative to activate) to Process
134
135 22 Nov 2007: - Major change to event list handling to speed up larger models:
136 * Drop dictionary
137 * Replace bisect by heapq
138 * Mark cancelled event notices in unpost and skip them in
139 nextev (great idea of Tony Vignaux))
140 4 Dec 2007: - Added twVariance calculation for both Monitor and Tally (gav)
141 5 Dec 2007: - Changed name back to timeVariance (gav)
142 """
143 __TESTING=False
144 version=__version__="1.9 $Revision: 1.1.1.31 $ $Date: 2007/12/13 05:02:22 $"
145 if __TESTING:
146 print "SimPy.SimulationTrace %s" %__version__,
147 if __debug__:
148 print "__debug__ on"
149 else:
150 print
151
152
153 hold=1
154 passivate=2
155 request=3
156 release=4
157 waitevent=5
158 queueevent=6
159 waituntil=7
160 get=8
161 put=9
162
163 _endtime=0
164 _t=0
165 _e=None
166 _stop=True
167 _wustep=False
168 try:
169 True, False
170 except NameError:
171 True, False = (1 == 1), (0 == 1)
172 condQ=[]
173 allMonitors=[]
174 allTallies=[]
175
184
187
189 """Application function to stop simulation run"""
190 global _stop
191 _stop=True
192
194 """Application function to start stepping through simulation for waituntil construct."""
195 global _wustep
196 _wustep=True
197
199 """Application function to stop stepping through simulation."""
200 global _wustep
201 _wustep=False
202
205 self.value=value
206
208 return `self.value`
209
214
216 """Superclass of classes which may use generator functions"""
218
219 self._nextpoint=None
220 self.name=name
221 self._nextTime=None
222 self._remainService=0
223 self._preempted=0
224 self._priority={}
225 self._getpriority={}
226 self._putpriority={}
227 self._terminated= False
228 self._inInterrupt= False
229 self.eventsFired=[]
230
232 return self._nextTime <> None and not self._inInterrupt
233
235 return self._nextTime is None and not self._terminated
236
238 return self._terminated
239
241 return self._inInterrupt and not self._terminated
242
244 return self in resource.waitQ
245
247 """Application function to cancel all event notices for this Process
248 instance;(should be all event notices for the _generator_)."""
249 _e._unpost(whom=victim)
250
251 - def start(self,pem=None,at="undefined",delay="undefined",prior=False):
252 """Activates PEM of this Process.
253 p.start(p.pemname([args])[,{at= t |delay=period}][,prior=False]) or
254 p.start([p.ACTIONS()][,{at= t |delay=period}][,prior=False]) (ACTIONS
255 parameter optional)
256 """
257 if pem is None:
258 try:
259 pem=self.ACTIONS()
260 except AttributeError:
261 raise FatalSimerror\
262 ("Fatal SimPy error: no generator function to activate")
263 else:
264 pass
265 if _e is None:
266 raise FatalSimerror\
267 ("Fatal SimPy error: simulation is not initialized"\
268 "(call initialize() first)")
269 if not (type(pem) == types.GeneratorType):
270 raise FatalSimerror("Fatal SimPy error: activating function which"+
271 " is not a generator (contains no 'yield')")
272 if not self._terminated and not self._nextTime:
273
274 self._nextpoint=pem
275 if at=="undefined":
276 at=_t
277 if delay=="undefined":
278 zeit=max(_t,at)
279 else:
280 zeit=max(_t,_t+delay)
281 trace.recordActivate(who=self,when=zeit,prior=prior)
282 _e._post(what=self,at=zeit,prior=prior)
283
285 if len(a[0]) == 3:
286 delay=abs(a[0][2])
287 else:
288 delay=0
289 who=a[1]
290 self.interruptLeft=delay
291 self._inInterrupt=False
292 self.interruptCause=None
293 _e._post(what=who,at=_t+delay)
294
296 a[0][1]._nextTime=None
297
299 """Application function to interrupt active processes"""
300
301 if victim.active():
302 save=trace._comment
303 trace._comment=None
304 victim.interruptCause=self
305 left=victim._nextTime-_t
306 victim.interruptLeft=left
307 victim._inInterrupt=True
308 reactivate(victim)
309 trace._comment=save
310 trace.recordInterrupt(self,victim)
311 return left
312 else:
313 return None
314
316 """
317 Application function for an interrupt victim to get out of
318 'interrupted' state.
319 """
320 self._inInterrupt= False
321
323 """Multi-functional test for reneging for 'request' and 'get':
324 (1)If res of type Resource:
325 Tests whether resource res was acquired when proces reactivated.
326 If yes, the parallel wakeup process is killed.
327 If not, process is removed from res.waitQ (reneging).
328 (2)If res of type Store:
329 Tests whether item(s) gotten from Store res.
330 If yes, the parallel wakeup process is killed.
331 If no, process is removed from res.getQ
332 (3)If res of type Level:
333 Tests whether units gotten from Level res.
334 If yes, the parallel wakeup process is killed.
335 If no, process is removed from res.getQ.
336 """
337 if isinstance(res,Resource):
338 test=self in res.activeQ
339 if test:
340 self.cancel(self._holder)
341 else:
342 res.waitQ.remove(self)
343 if res.monitored:
344 res.waitMon.observe(len(res.waitQ),t=now())
345 return test
346 elif isinstance(res,Store):
347 test=len(self.got)
348 if test:
349 self.cancel(self._holder)
350 else:
351 res.getQ.remove(self)
352 if res.monitored:
353 res.getQMon.observe(len(res.getQ),t=now())
354 return test
355 elif isinstance(res,Level):
356 test=not (self.got is None)
357 if test:
358 self.cancel(self._holder)
359 else:
360 res.getQ.remove(self)
361 if res.monitored:
362 res.getQMon.observe(len(res.getQ),t=now())
363 return test
364
366 """Test for reneging for 'yield put . . .' compound statement (Level and
367 Store. Returns True if not reneged.
368 If self not in buffer.putQ, kill wakeup process, else take self out of
369 buffer.putQ (reneged)"""
370 test=self in buffer.putQ
371 if test:
372 buffer.putQ.remove(self)
373 if buffer.monitored:
374 buffer.putQMon.observe(len(buffer.putQ),t=now())
375 else:
376 self.cancel(self._holder)
377 return not test
378
380 """Returns string with eventlist as;
381 t1: processname,processname2
382 t2: processname4,processname5, . . .
383 . . . .
384 """
385 ret=""
386 tempList=[]
387 tempList[:]=_e.timestamps
388 tempList.sort()
389
390 tempList=[[x[0],x[2].name] for x in tempList if not x[3]]
391 tprev=-1
392 for t in tempList:
393
394 if t[0]==tprev:
395
396 ret+=",%s"%t[1]
397 else:
398
399 if tprev==-1:
400 ret="%s: %s"%(t[0],t[1])
401 else:
402 ret+="\n%s: %s"%(t[0],t[1])
403 tprev=t[0]
404 return ret+"\n"
405
407 """Returns list of all times for which events are scheduled.
408 """
409 r=[]
410 r[:]=_e.timestamps
411 r.sort()
412
413 r1=[x[0] for x in r if not r[3]]
414 tprev=-1
415 ret=[]
416 for t in r1:
417 if t==tprev:
418
419 pass
420 else:
421 ret.append(t)
422 tprev=t
423 return ret
424
426 """Defines event list and operations on it"""
428
429
430 self.timestamps = []
431 self.sortpr=0
432
433 - def _post(self, what, at, prior=False):
434 """Post an event notice for process what for time at"""
435
436 if at < _t:
437 raise Simerror("Attempt to schedule event in the past")
438 what._nextTime = at
439 self.sortpr-=1
440 if prior:
441
442
443
444
445 what._rec=[at,self.sortpr,what,False]
446
447 hq.heappush(self.timestamps,what._rec)
448 else:
449
450
451 what._rec=[at,-self.sortpr,what,False]
452
453 hq.heappush(self.timestamps,what._rec)
454
455 - def _unpost(self, whom):
456 """
457 Mark event notice for whom as cancelled if whom is a suspended process
458 """
459 if whom._nextTime is not None:
460 whom._rec[3]=True
461 whom._nextTime=None
462
464 """Retrieve next event from event list"""
465 global _t, _stop
466 noActiveNotice=True
467
468 while noActiveNotice:
469 if self.timestamps:
470
471 (_tnotice, p,nextEvent,cancelled) = hq.heappop(self.timestamps)
472 noActiveNotice=cancelled
473 else:
474 raise Simerror("No more events at time %s" % _t)
475 _t=_tnotice
476 if _t > _endtime:
477 _t = _endtime
478 _stop = True
479 return (None,)
480 try:
481 resultTuple = nextEvent._nextpoint.next()
482 except StopIteration:
483 nextEvent._nextpoint = None
484 nextEvent._terminated = True
485 nextEvent._nextTime = None
486 resultTuple = None
487 return (resultTuple, nextEvent)
488
490 return not self.timestamps
491
493 """Returns string with eventlist as
494 t1: [procname,procname2]
495 t2: [procname4,procname5, . . . ]
496 . . . .
497 """
498 ret=""
499 for t in self.timestamps:
500 ret+="%s:%s\n"%(t[1]._nextTime, t[1].name)
501 return ret[:-1]
502
504 """Returns list of all times for which events are scheduled.
505 """
506 return self.timestamps
507
508 -def activate(obj,process,at="undefined",delay="undefined",prior=False):
509 """Application function to activate passive process."""
510 if _e is None:
511 raise FatalSimerror\
512 ("Fatal error: simulation is not initialized (call initialize() first)")
513 if not (type(process) == types.GeneratorType):
514 raise FatalSimerror("Activating function which"+
515 " is not a generator (contains no 'yield')")
516 if not obj._terminated and not obj._nextTime:
517
518 obj._nextpoint=process
519 if at=="undefined":
520 at=_t
521 if delay=="undefined":
522 zeit=max(_t,at)
523 else:
524 zeit=max(_t,_t+delay)
525 trace.recordActivate(who=obj,when=zeit,prior=prior)
526 _e._post(obj,at=zeit,prior=prior)
527
528 -def reactivate(obj,at="undefined",delay="undefined",prior=False):
529 """Application function to reactivate a process which is active,
530 suspended or passive."""
531
532 if not obj._terminated:
533 a=Process("SimPysystem")
534 a.cancel(obj)
535
536 if at=="undefined":
537 at=_t
538 if delay=="undefined":
539 zeit=max(_t,at)
540 else:
541 zeit=max(_t,_t+delay)
542 trace.recordReactivate(who=obj,when=zeit,prior=prior)
543 _e._post(obj,at=zeit,prior=prior)
544
546 """ A histogram gathering and sampling class"""
547
548 - def __init__(self,name = '',low=0.0,high=100.0,nbins=10):
549 list.__init__(self)
550 self.name = name
551 self.low = float(low)
552 self.high = float(high)
553 self.nbins = nbins
554 self.binsize=(self.high-self.low)/nbins
555 self._nrObs=0
556 self._sum=0
557 self[:] =[[low+(i-1)*self.binsize,0] for i in range(self.nbins+2)]
558
560 """ add a value into the correct bin"""
561 self._nrObs+=1
562 self._sum+=y
563 b = int((y-self.low+self.binsize)/self.binsize)
564 if b < 0: b = 0
565 if b > self.nbins+1: b = self.nbins+1
566 assert 0 <= b <=self.nbins+1,'Histogram.addIn: b out of range: %s'%b
567 self[b][1]+=1
568
570 histo=self
571 ylab="value"
572 nrObs=self._nrObs
573 width=len(str(nrObs))
574 res=[]
575 res.append("<Histogram %s:"%self.name)
576 res.append("\nNumber of observations: %s"%nrObs)
577 if nrObs:
578 su=self._sum
579 cum=histo[0][1]
580 fmt="%s"
581 line="\n%s <= %s < %s: %s (cum: %s/%s%s)"\
582 %(fmt,"%s",fmt,"%s","%s","%5.1f","%s")
583 line1="\n%s%s < %s: %s (cum: %s/%s%s)"\
584 %("%s","%s",fmt,"%s","%s","%5.1f","%s")
585 l1width=len(("%s <= "%fmt)%histo[1][0])
586 res.append(line1\
587 %(" "*l1width,ylab,histo[1][0],str(histo[0][1]).rjust(width),\
588 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
589 )
590 for i in range(1,len(histo)-1):
591 cum+=histo[i][1]
592 res.append(line\
593 %(histo[i][0],ylab,histo[i+1][0],str(histo[i][1]).rjust(width),\
594 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
595 )
596 cum+=histo[-1][1]
597 linen="\n%s <= %s %s : %s (cum: %s/%s%s)"\
598 %(fmt,"%s","%s","%s","%s","%5.1f","%s")
599 lnwidth=len(("<%s"%fmt)%histo[1][0])
600 res.append(linen\
601 %(histo[-1][0],ylab," "*lnwidth,str(histo[-1][1]).rjust(width),\
602 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
603 )
604 res.append("\n>")
605 return " ".join(res)
606
608 """Starts data collection of all designated Monitor and Tally objects
609 (default=all) at time 'when'.
610 """
611 class Starter(Process):
612 def collect(self,monitors,tallies):
613 for m in monitors:
614 print m.name
615 m.reset()
616 for t in tallies:
617 t.reset()
618 yield hold,self
619 if monitors is None:
620 monitors=allMonitors
621 if tallies is None:
622 tallies=allTallies
623 s=Starter()
624 activate(s,s.collect(monitors=monitors,tallies=tallies),at=when)
625
627 """ Monitored variables
628
629 A Class for monitored variables, that is, variables that allow one
630 to gather simple statistics. A Monitor is a subclass of list and
631 list operations can be performed on it. An object is established
632 using m= Monitor(name = '..'). It can be given a
633 unique name for use in debugging and in tracing and ylab and tlab
634 strings for labelling graphs.
635 """
636 - def __init__(self,name='a_Monitor',ylab='y',tlab='t'):
637 list.__init__(self)
638 self.startTime = 0.0
639 self.name = name
640 self.ylab = ylab
641 self.tlab = tlab
642 allMonitors.append(self)
643
644 - def setHistogram(self,name = '',low=0.0,high=100.0,nbins=10):
645 """Sets histogram parameters.
646 Must be called before call to getHistogram"""
647 if name=='':
648 histname=self.name
649 else:
650 histname=name
651 self.histo=Histogram(name=histname,low=low,high=high,nbins=nbins)
652
654 """record y and t"""
655 if t is None: t = now()
656 self.append([t,y])
657
659 """ deprecated: tally for backward compatibility"""
660 self.observe(y,0)
661
662 - def accum(self,y,t=None):
663 """ deprecated: accum for backward compatibility"""
664 self.observe(y,t)
665
667 """reset the sums and counts for the monitored variable """
668 self[:]=[]
669 if t is None: t = now()
670 self.startTime = t
671
673 """ the series of measured times"""
674 return list(zip(*self)[0])
675
677 """ the series of measured values"""
678 return list(zip(*self)[1])
679
681 """ deprecated: the number of observations made """
682 return self.__len__()
683
685 """ the sum of the y"""
686 if self.__len__()==0: return 0
687 else:
688 sum = 0.0
689 for i in range(self.__len__()):
690 sum += self[i][1]
691 return sum
692
694 """ the simple average of the monitored variable"""
695 try: return 1.0*self.total()/self.__len__()
696 except: print 'SimPy: No observations for mean'
697
699 """ the sample variance of the monitored variable """
700 n = len(self)
701 tot = self.total()
702 ssq=0.0
703
704 for i in range(self.__len__()):
705 ssq += self[i][1]**2
706 try: return (ssq - float(tot*tot)/n)/n
707 except: print 'SimPy: No observations for sample variance'
708
710 """ the time-weighted average of the monitored variable.
711
712 If t is used it is assumed to be the current time,
713 otherwise t = now()
714 """
715 N = self.__len__()
716 if N == 0:
717 print 'SimPy: No observations for timeAverage'
718 return None
719
720 if t is None: t = now()
721 sum = 0.0
722 tlast = self.startTime
723
724 ylast = 0.0
725 for i in range(N):
726 ti,yi = self[i]
727 sum += ylast*(ti-tlast)
728 tlast = ti
729 ylast = yi
730 sum += ylast*(t-tlast)
731 T = t - self.startTime
732 if T == 0:
733 print 'SimPy: No elapsed time for timeAverage'
734 return None
735
736 return sum/float(T)
737
739 """ the time-weighted Variance of the monitored variable.
740
741 If t is used it is assumed to be the current time,
742 otherwise t = now()
743 """
744 N = self.__len__()
745 if N == 0:
746 print 'SimPy: No observations for timeVariance'
747 return None
748 if t is None: t = now()
749 sm = 0.0
750 ssq = 0.0
751 tlast = self.startTime
752
753 ylast = 0.0
754 for i in range(N):
755 ti,yi = self[i]
756 sm += ylast*(ti-tlast)
757 ssq += ylast*ylast*(ti-tlast)
758 tlast = ti
759 ylast = yi
760 sm += ylast*(t-tlast)
761 ssq += ylast*ylast*(t-tlast)
762 T = t - self.startTime
763 if T == 0:
764 print 'SimPy: No elapsed time for timeVariance'
765 return None
766 mn = sm/float(T)
767
768 return ssq/float(T) - mn*mn
769
770
771 - def histogram(self,low=0.0,high=100.0,nbins=10):
772 """ A histogram of the monitored y data values.
773 """
774 h = Histogram(name=self.name,low=low,high=high,nbins=nbins)
775 ys = self.yseries()
776 for y in ys: h.addIn(y)
777 return h
778
780 """Returns a histogram based on the parameters provided in
781 preceding call to setHistogram.
782 """
783 ys = self.yseries()
784 h=self.histo
785 for y in ys: h.addIn(y)
786 return h
787
789 """Returns formatted frequency distribution table string from Monitor.
790 Precondition: setHistogram must have been called.
791 fmt==format of bin range values
792 """
793 try:
794 histo=self.getHistogram()
795 except:
796 raise FatalSimerror("histogramTable: call setHistogram first"\
797 " for Monitor %s"%self.name)
798 ylab=self.ylab
799 nrObs=self.count()
800 width=len(str(nrObs))
801 res=[]
802 res.append("\nHistogram for %s:"%histo.name)
803 res.append("\nNumber of observations: %s"%nrObs)
804 su=sum(self.yseries())
805 cum=histo[0][1]
806 line="\n%s <= %s < %s: %s (cum: %s/%s%s)"\
807 %(fmt,"%s",fmt,"%s","%s","%5.1f","%s")
808 line1="\n%s%s < %s: %s (cum: %s/%s%s)"\
809 %("%s","%s",fmt,"%s","%s","%5.1f","%s")
810 l1width=len(("%s <= "%fmt)%histo[1][0])
811 res.append(line1\
812 %(" "*l1width,ylab,histo[1][0],str(histo[0][1]).rjust(width),\
813 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
814 )
815 for i in range(1,len(histo)-1):
816 cum+=histo[i][1]
817 res.append(line\
818 %(histo[i][0],ylab,histo[i+1][0],str(histo[i][1]).rjust(width),\
819 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
820 )
821 cum+=histo[-1][1]
822 linen="\n%s <= %s %s : %s (cum: %s/%s%s)"\
823 %(fmt,"%s","%s","%s","%s","%5.1f","%s")
824 lnwidth=len(("<%s"%fmt)%histo[1][0])
825 res.append(linen\
826 %(histo[-1][0],ylab," "*lnwidth,str(histo[-1][1]).rjust(width),\
827 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
828 )
829 return " ".join(res)
830
832 - def __init__(self, name="a_Tally", ylab="y",tlab="t"):
833 self.name = name
834 self.ylab = ylab
835 self.tlab = tlab
836 self.reset()
837 self.startTime = 0.0
838 self.histo = None
839 self.sum = 0.0
840 self._sum_of_squares = 0
841 self._integral = 0.0
842 self._integral2 = 0.0
843 allTallies.append(self)
844
845 - def setHistogram(self,name = '',low=0.0,high=100.0,nbins=10):
846 """Sets histogram parameters.
847 Must be called to prior to observations initiate data collection
848 for histogram.
849 """
850 if name=='':
851 hname=self.name
852 else:
853 hname=name
854 self.histo=Histogram(name=hname,low=low,high=high,nbins=nbins)
855
857 if t is None:
858 t = now()
859 self._integral += (t - self._last_timestamp) * self._last_observation
860 yy = self._last_observation* self._last_observation
861 self._integral2 += (t - self._last_timestamp) * yy
862 self._last_timestamp = t
863 self._last_observation = y
864 self._total += y
865 self._count += 1
866 self._sum += y
867 self._sum_of_squares += y * y
868 if self.histo:
869 self.histo.addIn(y)
870
871 - def reset(self, t=None):
872 if t is None:
873 t = now()
874 self.startTime = t
875 self._last_timestamp = t
876 self._last_observation = 0.0
877 self._count = 0
878 self._total = 0.0
879 self._integral = 0.0
880 self._integral2 = 0.0
881 self._sum = 0.0
882 self._sum_of_squares = 0.0
883
885 return self._count
886
888 return self._total
889
891 return 1.0 * self._total / self._count
892
894 if t is None:
895 t=now()
896 integ=self._integral+(t - self._last_timestamp) * self._last_observation
897 if (t > self.startTime):
898 return 1.0 * integ/(t - self.startTime)
899 else:
900 print 'SimPy: No elapsed time for timeAverage'
901 return None
902
904 return 1.0 * (self._sum_of_squares - (1.0 * (self._sum * self._sum)\
905 / self._count)) / (self._count)
906
908 """ the time-weighted Variance of the Tallied variable.
909
910 If t is used it is assumed to be the current time,
911 otherwise t = now()
912 """
913 if t is None:
914 t=now()
915 twAve = self.timeAverage(t)
916
917 last = self._last_observation
918 twinteg2=self._integral2+(t - self._last_timestamp) * last * last
919
920 if (t > self.startTime):
921 return 1.0 * twinteg2/(t - self.startTime) - twAve*twAve
922 else:
923 print 'SimPy: No elapsed time for timeVariance'
924 return None
925
926
927
929 return self._count
930
932 return len(l) == self._count
933
935 return self.histo
936
938 """Returns formatted frequency distribution table string from Tally.
939 Precondition: setHistogram must have been called.
940 fmt==format of bin range values
941 """
942 try:
943 histo=self.getHistogram()
944 except:
945 raise FatalSimerror("histogramTable: call setHistogram first"\
946 " for Tally %s"%self.name)
947 ylab=self.ylab
948 nrObs=self.count()
949 width=len(str(nrObs))
950 res=[]
951 res.append("\nHistogram for %s:"%histo.name)
952 res.append("\nNumber of observations: %s"%nrObs)
953 su=self.total()
954 cum=histo[0][1]
955 line="\n%s <= %s < %s: %s (cum: %s/%s%s)"\
956 %(fmt,"%s",fmt,"%s","%s","%5.1f","%s")
957 line1="\n%s%s < %s: %s (cum: %s/%s%s)"\
958 %("%s","%s",fmt,"%s","%s","%5.1f","%s")
959 l1width=len(("%s <= "%fmt)%histo[1][0])
960 res.append(line1\
961 %(" "*l1width,ylab,histo[1][0],str(histo[0][1]).rjust(width),\
962 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
963 )
964 for i in range(1,len(histo)-1):
965 cum+=histo[i][1]
966 res.append(line\
967 %(histo[i][0],ylab,histo[i+1][0],str(histo[i][1]).rjust(width),\
968 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
969 )
970 cum+=histo[-1][1]
971 linen="\n%s <= %s %s : %s (cum: %s/%s%s)"\
972 %(fmt,"%s","%s","%s","%s","%5.1f","%s")
973 lnwidth=len(("<%s"%fmt)%histo[1][0])
974 res.append(linen\
975 %(histo[-1][0],ylab," "*lnwidth,str(histo[-1][1]).rjust(width),\
976 str(cum).rjust(width),(float(cum)/nrObs)*100,"%")
977 )
978 return " ".join(res)
979
982 if not moni is None:
983 self.monit=True
984 else:
985 self.monit=False
986 self.moni=moni
987 self.resource=res
988
990 pass
991
993 pass
994
996 self.remove(obj)
997 if self.monit:
998 self.moni.observe(len(self),t=now())
999
1003
1005 self.append(obj)
1006 if self.monit:
1007 self.moni.observe(len(self),t=now())
1008
1011
1014
1016 a= self.pop(0)
1017 if self.monit:
1018 self.moni.observe(len(self),t=now())
1019 return a
1020
1022 """Queue is always ordered according to priority.
1023 Higher value of priority attribute == higher priority.
1024 """
1027
1029 """Handles request queue for Resource"""
1030 if len(self):
1031 ix=self.resource
1032 if self[-1]._priority[ix] >= obj._priority[ix]:
1033 self.append(obj)
1034 else:
1035 z=0
1036 while self[z]._priority[ix] >= obj._priority[ix]:
1037 z += 1
1038 self.insert(z,obj)
1039 else:
1040 self.append(obj)
1041 if self.monit:
1042 self.moni.observe(len(self),t=now())
1043
1045 """Handles getQ in Buffer"""
1046 if len(self):
1047 ix=self.resource
1048
1049 if self[-1]._getpriority[ix] >= obj._getpriority[ix]:
1050 self.append(obj)
1051 else:
1052 z=0
1053 while self[z]._getpriority[ix] >= obj._getpriority[ix]:
1054 z += 1
1055 self.insert(z,obj)
1056 else:
1057 self.append(obj)
1058 if self.monit:
1059 self.moni.observe(len(self),t=now())
1060
1062 """Handles putQ in Buffer"""
1063 if len(self):
1064 ix=self.resource
1065
1066 if self[-1]._putpriority[ix] >= obj._putpriority[ix]:
1067 self.append(obj)
1068 else:
1069 z=0
1070 while self[z]._putpriority[ix] >= obj._putpriority[ix]:
1071 z += 1
1072 self.insert(z,obj)
1073 else:
1074 self.append(obj)
1075 if self.monit:
1076 self.moni.observe(len(self),t=now())
1077
1079 """Models shared, limited capacity resources with queuing;
1080 FIFO is default queuing discipline.
1081 """
1082
1083 - def __init__(self,capacity=1,name="a_resource",unitName="units",
1084 qType=FIFO,preemptable=0,monitored=False,monitorType=Monitor):
1085 """
1086 monitorType={Monitor(default)|Tally}
1087 """
1088 self.name=name
1089 self.capacity=capacity
1090 self.unitName=unitName
1091 self.n=capacity
1092 self.monitored=monitored
1093
1094 if self.monitored:
1095 self.actMon=monitorType(name="Active Queue Monitor %s"%self.name,
1096 ylab="nr in queue",tlab="time")
1097 monact=self.actMon
1098 self.waitMon=monitorType(name="Wait Queue Monitor %s"%self.name,
1099 ylab="nr in queue",tlab="time")
1100 monwait=self.waitMon
1101 else:
1102 monwait=None
1103 monact=None
1104 self.waitQ=qType(self,monwait)
1105 self.preemptable=preemptable
1106 self.activeQ=qType(self,monact)
1107 self.priority_default=0
1108
1110 """Process request event for this resource"""
1111 obj=arg[1]
1112 if len(arg[0]) == 4:
1113 obj._priority[self]=arg[0][3]
1114 else:
1115 obj._priority[self]=self.priority_default
1116 if self.preemptable and self.n == 0:
1117
1118 preempt=obj._priority[self] > self.activeQ[-1]._priority[self]
1119
1120 if preempt:
1121 z=self.activeQ[-1]
1122
1123
1124
1125 z._remainService = z._nextTime - _t
1126 Process().cancel(z)
1127
1128 self.activeQ.remove(z)
1129
1130 self.waitQ.insert(0,z)
1131
1132 if self.monitored:
1133 self.waitMon.observe(len(self.waitQ),now())
1134
1135 z._preempted = 1
1136
1137 z._nextTime=None
1138
1139 self.activeQ.enter(obj)
1140
1141 _e._post(obj,at=_t,prior=1)
1142 else:
1143 self.waitQ.enter(obj)
1144
1145 obj._nextTime=None
1146 else:
1147 if self.n == 0:
1148 self.waitQ.enter(obj)
1149
1150 obj._nextTime=None
1151 else:
1152 self.n -= 1
1153 self.activeQ.enter(obj)
1154 _e._post(obj,at=_t,prior=1)
1155
1157 """Process release request for this resource"""
1158 self.n += 1
1159 self.activeQ.remove(arg[1])
1160 if self.monitored:
1161 self.actMon.observe(len(self.activeQ),t=now())
1162
1163 if self.waitQ:
1164 obj=self.waitQ.leave()
1165 self.n -= 1
1166 self.activeQ.enter(obj)
1167
1168 if self.preemptable:
1169
1170 if obj._preempted:
1171 obj._preempted = 0
1172
1173 reactivate(obj,delay=obj._remainService)
1174
1175 else:
1176 reactivate(obj,delay=0,prior=1)
1177
1178 else:
1179 reactivate(obj,delay=0,prior=1)
1180 _e._post(arg[1],at=_t,prior=1)
1181
1183 """Abstract class for buffers
1184 Blocks a process when a put would cause buffer overflow or a get would cause
1185 buffer underflow.
1186 Default queuing discipline for blocked processes is FIFO."""
1187
1188 priorityDefault=0
1189 - def __init__(self,name=None,capacity="unbounded",unitName="units",
1190 putQType=FIFO,getQType=FIFO,
1191 monitored=False,monitorType=Monitor,initialBuffered=None):
1192 if capacity=="unbounded": capacity=sys.maxint
1193 self.capacity=capacity
1194 self.name=name
1195 self.putQType=putQType
1196 self.getQType=getQType
1197 self.monitored=monitored
1198 self.initialBuffered=initialBuffered
1199 self.unitName=unitName
1200 if self.monitored:
1201
1202 self.putQMon=monitorType(name="Producer Queue Monitor %s"%self.name,
1203 ylab="nr in queue",tlab="time")
1204
1205 self.getQMon=monitorType(name="Consumer Queue Monitor %s"%self.name,
1206 ylab="nr in queue",tlab="time")
1207
1208 self.bufferMon=monitorType(name="Buffer Monitor %s"%self.name,
1209 ylab="nr in buffer",tlab="time")
1210 else:
1211 self.putQMon=None
1212 self.getQMon=None
1213 self.bufferMon=None
1214 self.putQ=self.putQType(res=self,moni=self.putQMon)
1215 self.getQ=self.getQType(res=self,moni=self.getQMon)
1216 if self.monitored:
1217 self.putQMon.observe(y=len(self.putQ),t=now())
1218 self.getQMon.observe(y=len(self.getQ),t=now())
1219 self._putpriority={}
1220 self._getpriority={}
1221
1222 def _put(self):
1223 pass
1224 def _get(self):
1225 pass
1226
1228 """Models buffers for processes putting/getting un-distinguishable items.
1229 """
1232
1235
1236 theBuffer=property(gettheBuffer)
1237
1239 Buffer.__init__(self,**pars)
1240 if self.name is None:
1241 self.name="a_level"
1242
1243 if (type(self.capacity)!=type(1.0) and\
1244 type(self.capacity)!=type(1)) or\
1245 self.capacity<0:
1246 raise FatalSimerror\
1247 ("Level: capacity parameter not a positive number: %s"\
1248 %self.initialBuffered)
1249
1250 if type(self.initialBuffered)==type(1.0) or\
1251 type(self.initialBuffered)==type(1):
1252 if self.initialBuffered>self.capacity:
1253 raise FatalSimerror("initialBuffered exceeds capacity")
1254 if self.initialBuffered>=0:
1255 self.nrBuffered=self.initialBuffered
1256
1257 else:
1258 raise FatalSimerror\
1259 ("initialBuffered param of Level negative: %s"\
1260 %self.initialBuffered)
1261 elif self.initialBuffered is None:
1262 self.initialBuffered=0
1263 self.nrBuffered=0
1264 else:
1265 raise FatalSimerror\
1266 ("Level: wrong type of initialBuffered (parameter=%s)"\
1267 %self.initialBuffered)
1268 if self.monitored:
1269 self.bufferMon.observe(y=self.amount,t=now())
1270 amount=property(getamount)
1271
1272 - def _put(self,arg):
1273 """Handles put requests for Level instances"""
1274 obj=arg[1]
1275 if len(arg[0]) == 5:
1276 obj._putpriority[self]=arg[0][4]
1277 whatToPut=arg[0][3]
1278 elif len(arg[0]) == 4:
1279 obj._putpriority[self]=Buffer.priorityDefault
1280 whatToPut=arg[0][3]
1281 else:
1282 obj._putpriority[self]=Buffer.priorityDefault
1283 whatToPut=1
1284 if type(whatToPut)!=type(1) and type(whatToPut)!=type(1.0):
1285 raise FatalSimerror("Level: put parameter not a number")
1286 if not whatToPut>=0.0:
1287 raise FatalSimerror("Level: put parameter not positive number")
1288 whatToPutNr=whatToPut
1289 if whatToPutNr+self.amount>self.capacity:
1290 obj._nextTime=None
1291 obj._whatToPut=whatToPutNr
1292 self.putQ.enterPut(obj)
1293 else:
1294 self.nrBuffered+=whatToPutNr
1295 if self.monitored:
1296 self.bufferMon.observe(y=self.amount,t=now())
1297
1298
1299
1300 while len(self.getQ) and self.amount>0:
1301 proc=self.getQ[0]
1302 if proc._nrToGet<=self.amount:
1303 proc.got=proc._nrToGet
1304 self.nrBuffered-=proc.got
1305 if self.monitored:
1306 self.bufferMon.observe(y=self.amount,t=now())
1307 self.getQ.takeout(proc)
1308 _e._post(proc,at=_t)
1309 else:
1310 break
1311 _e._post(obj,at=_t,prior=1)
1312
1313 - def _get(self,arg):
1314 """Handles get requests for Level instances"""
1315 obj=arg[1]
1316 obj.got=None
1317 if len(arg[0]) == 5:
1318 obj._getpriority[self]=arg[0][4]
1319 nrToGet=arg[0][3]
1320 elif len(arg[0]) == 4:
1321 obj._getpriority[self]=Buffer.priorityDefault
1322 nrToGet=arg[0][3]
1323 else:
1324 obj._getpriority[self]=Buffer.priorityDefault
1325 nrToGet=1
1326 if type(nrToGet)!=type(1.0) and type(nrToGet)!=type(1):
1327 raise FatalSimerror\
1328 ("Level: get parameter not a number: %s"%nrToGet)
1329 if nrToGet<0:
1330 raise FatalSimerror\
1331 ("Level: get parameter not positive number: %s"%nrToGet)
1332 if self.amount < nrToGet:
1333 obj._nrToGet=nrToGet
1334 self.getQ.enterGet(obj)
1335
1336 obj._nextTime=None
1337 else:
1338 obj.got=nrToGet
1339 self.nrBuffered-=nrToGet
1340 if self.monitored:
1341 self.bufferMon.observe(y=self.amount,t=now())
1342 _e._post(obj,at=_t,prior=1)
1343
1344
1345
1346 while len(self.putQ):
1347 proc=self.putQ[0]
1348 if proc._whatToPut+self.amount<=self.capacity:
1349 self.nrBuffered+=proc._whatToPut
1350 if self.monitored:
1351 self.bufferMon.observe(y=self.amount,t=now())
1352 self.putQ.takeout(proc)
1353 _e._post(proc,at=_t)
1354 else:
1355 break
1356
1358 """Models buffers for processes coupled by putting/getting distinguishable
1359 items.
1360 Blocks a process when a put would cause buffer overflow or a get would cause
1361 buffer underflow.
1362 Default queuing discipline for blocked processes is priority FIFO.
1363 """
1366 nrBuffered=property(getnrBuffered)
1367
1370 buffered=property(getbuffered)
1371
1373 Buffer.__init__(self,**pars)
1374 self.theBuffer=[]
1375 if self.name is None:
1376 self.name="a_store"
1377 if type(self.capacity)!=type(1) or self.capacity<=0:
1378 raise FatalSimerror\
1379 ("Store: capacity parameter not a positive integer > 0: %s"\
1380 %self.initialBuffered)
1381 if type(self.initialBuffered)==type([]):
1382 if len(self.initialBuffered)>self.capacity:
1383 raise FatalSimerror("initialBuffered exceeds capacity")
1384 else:
1385 self.theBuffer[:]=self.initialBuffered
1386 elif self.initialBuffered is None:
1387 self.theBuffer=[]
1388 else:
1389 raise FatalSimerror\
1390 ("Store: initialBuffered not a list")
1391 if self.monitored:
1392 self.bufferMon.observe(y=self.nrBuffered,t=now())
1393 self._sort=None
1394
1395
1396
1398 """Adds buffer sorting to this instance of Store. It maintains
1399 theBuffer sorted by the sortAttr attribute of the objects in the
1400 buffer.
1401 The user-provided 'sortFunc' must look like this:
1402
1403 def mySort(self,par):
1404 tmplist=[(x.sortAttr,x) for x in par]
1405 tmplist.sort()
1406 return [x for (key,x) in tmplist]
1407
1408 """
1409
1410 self._sort=new.instancemethod(sortFunc,self,self.__class__)
1411 self.theBuffer=self._sort(self.theBuffer)
1412
1413 - def _put(self,arg):
1414 """Handles put requests for Store instances"""
1415 obj=arg[1]
1416 if len(arg[0]) == 5:
1417 obj._putpriority[self]=arg[0][4]
1418 whatToPut=arg[0][3]
1419 elif len(arg[0]) == 4:
1420 obj._putpriority[self]=Buffer.priorityDefault
1421 whatToPut=arg[0][3]
1422 else:
1423 raise FatalSimerror("Item to put missing in yield put stmt")
1424 if type(whatToPut)!=type([]):
1425 raise FatalSimerror("put parameter is not a list")
1426 whatToPutNr=len(whatToPut)
1427 if whatToPutNr+self.nrBuffered>self.capacity:
1428 obj._nextTime=None
1429 obj._whatToPut=whatToPut
1430 self.putQ.enterPut(obj)
1431 else:
1432 self.theBuffer.extend(whatToPut)
1433 if not(self._sort is None):
1434 self.theBuffer=self._sort(self.theBuffer)
1435 if self.monitored:
1436 self.bufferMon.observe(y=self.nrBuffered,t=now())
1437
1438
1439
1440
1441 while self.nrBuffered>0 and len(self.getQ):
1442 proc=self.getQ[0]
1443 if inspect.isfunction(proc._nrToGet):
1444 movCand=proc._nrToGet(self.theBuffer)
1445 if movCand:
1446 proc.got=movCand[:]
1447 for i in movCand:
1448 self.theBuffer.remove(i)
1449 self.getQ.takeout(proc)
1450 if self.monitored:
1451 self.bufferMon.observe(y=self.nrBuffered,t=now())
1452 _e._post(what=proc,at=_t)
1453 else:
1454 break
1455 else:
1456 if proc._nrToGet<=self.nrBuffered:
1457 nrToGet=proc._nrToGet
1458 proc.got=[]
1459 proc.got[:]=self.theBuffer[0:nrToGet]
1460 self.theBuffer[:]=self.theBuffer[nrToGet:]
1461 if self.monitored:
1462 self.bufferMon.observe(y=self.nrBuffered,t=now())
1463
1464 self.getQ.takeout(proc)
1465 _e._post(what=proc,at=_t)
1466 else:
1467 break
1468
1469 _e._post(what=obj,at=_t,prior=1)
1470
1471 - def _get(self,arg):
1472 """Handles get requests"""
1473 filtfunc=None
1474 obj=arg[1]
1475 obj.got=[]
1476 if len(arg[0]) == 5:
1477 obj._getpriority[self]=arg[0][4]
1478 if inspect.isfunction(arg[0][3]):
1479 filtfunc=arg[0][3]
1480 else:
1481 nrToGet=arg[0][3]
1482 elif len(arg[0]) == 4:
1483 obj._getpriority[self]=Buffer.priorityDefault
1484 if inspect.isfunction(arg[0][3]):
1485 filtfunc=arg[0][3]
1486 else:
1487 nrToGet=arg[0][3]
1488 else:
1489 obj._getpriority[self]=Buffer.priorityDefault
1490 nrToGet=1
1491 if not filtfunc:
1492 if nrToGet<0:
1493 raise FatalSimerror\
1494 ("Store: get parameter not positive number: %s"%nrToGet)
1495 if self.nrBuffered < nrToGet:
1496 obj._nrToGet=nrToGet
1497 self.getQ.enterGet(obj)
1498
1499 obj._nextTime=None
1500 else:
1501 for i in range(nrToGet):
1502 obj.got.append(self.theBuffer.pop(0))
1503
1504 if self.monitored:
1505 self.bufferMon.observe(y=self.nrBuffered,t=now())
1506 _e._post(obj,at=_t,prior=1)
1507
1508
1509
1510 while len(self.putQ):
1511 proc=self.putQ[0]
1512 if len(proc._whatToPut)+self.nrBuffered<=self.capacity:
1513 for i in proc._whatToPut:
1514 self.theBuffer.append(i)
1515 if not(self._sort is None):
1516 self.theBuffer=self._sort(self.theBuffer)
1517 if self.monitored:
1518 self.bufferMon.observe(y=self.nrBuffered,t=now())
1519 self.putQ.takeout(proc)
1520 _e._post(proc,at=_t)
1521 else:
1522 break
1523 else:
1524 movCand=filtfunc(self.theBuffer)
1525 if movCand:
1526 _e._post(obj,at=_t,prior=1)
1527 obj.got=movCand[:]
1528 for item in movCand:
1529 self.theBuffer.remove(item)
1530 if self.monitored:
1531 self.bufferMon.observe(y=self.nrBuffered,t=now())
1532
1533
1534
1535 while len(self.putQ):
1536 proc=self.putQ[0]
1537 if len(proc._whatToPut)+self.nrBuffered<=self.capacity:
1538 for i in proc._whatToPut:
1539 self.theBuffer.append(i)
1540 if not(self._sort is None):
1541 self.theBuffer=self._sort(self.theBuffer)
1542 if self.monitored:
1543 self.bufferMon.observe(y=self.nrBuffered,t=now())
1544 self.putQ.takeout(proc)
1545 _e._post(proc,at=_t)
1546 else:
1547 break
1548 else:
1549 obj._nrToGet=filtfunc
1550 self.getQ.enterGet(obj)
1551
1552 obj._nextTime=None
1553
1555 """Supports one-shot signalling between processes. All processes waiting for an event to occur
1556 get activated when its occurrence is signalled. From the processes queuing for an event, only
1557 the first gets activated.
1558 """
1560 self.name=name
1561 self.waits=[]
1562 self.queues=[]
1563 self.occurred=False
1564 self.signalparam=None
1565
1566 - def signal(self,param=None):
1567 """Produces a signal to self;
1568 Fires this event (makes it occur).
1569 Reactivates ALL processes waiting for this event. (Cleanup waits lists
1570 of other events if wait was for an event-group (OR).)
1571 Reactivates the first process for which event(s) it is queuing for
1572 have fired. (Cleanup queues of other events if wait was for an event-group (OR).)
1573 """
1574 self.signalparam=param
1575
1576 trace.recordSignal(self)
1577 if not self.waits and not self.queues:
1578 self.occurred=True
1579 else:
1580
1581 for p in self.waits:
1582 p[0].eventsFired.append(self)
1583 reactivate(p[0],prior=True)
1584
1585 for ev in p[1]:
1586 if ev!=self:
1587 if ev.occurred:
1588 p[0].eventsFired.append(ev)
1589 for iev in ev.waits:
1590 if iev[0]==p[0]:
1591 ev.waits.remove(iev)
1592 break
1593 self.waits=[]
1594 if self.queues:
1595 proc=self.queues.pop(0)[0]
1596 proc.eventsFired.append(self)
1597 reactivate(proc)
1598
1600 """Consumes a signal if it has occurred, otherwise process 'proc'
1601 waits for this event.
1602 """
1603 proc=par[0][1]
1604 proc.eventsFired=[]
1605 if not self.occurred:
1606 self.waits.append([proc,[self]])
1607 proc._nextTime=None
1608 else:
1609 proc.eventsFired.append(self)
1610 self.occurred=False
1611 _e._post(proc,at=_t,prior=1)
1612
1614 """Handles waiting for an OR of events in a tuple/list.
1615 """
1616 proc=par[0][1]
1617 evlist=par[0][2]
1618 proc.eventsFired=[]
1619 anyoccur=False
1620 for ev in evlist:
1621 if ev.occurred:
1622 anyoccur=True
1623 proc.eventsFired.append(ev)
1624 ev.occurred=False
1625 if anyoccur:
1626 _e._post(proc,at=_t,prior=1)
1627
1628 else:
1629 proc.eventsFired=[]
1630 proc._nextTime=None
1631 for ev in evlist:
1632 ev.waits.append([proc,evlist])
1633
1635 """Consumes a signal if it has occurred, otherwise process 'proc'
1636 queues for this event.
1637 """
1638 proc=par[0][1]
1639 proc.eventsFired=[]
1640 if not self.occurred:
1641 self.queues.append([proc,[self]])
1642 proc._nextTime=None
1643 else:
1644 proc.eventsFired.append(self)
1645 self.occurred=False
1646 _e._post(proc,at=_t,prior=1)
1647
1649 """Handles queueing for an OR of events in a tuple/list.
1650 """
1651 proc=par[0][1]
1652 evlist=par[0][2]
1653 proc.eventsFired=[]
1654 anyoccur=False
1655 for ev in evlist:
1656 if ev.occurred:
1657 anyoccur=True
1658 proc.eventsFired.append(ev)
1659 ev.occurred=False
1660 if anyoccur:
1661 _e._post(proc,at=_t,prior=1)
1662
1663 else:
1664 proc.eventsFired=[]
1665 proc._nextTime=None
1666 for ev in evlist:
1667 ev.queues.append([proc,evlist])
1668
1669
1671 """
1672 Gets called by simulate after every event, as long as there are processes
1673 waiting in condQ for a condition to be satisfied.
1674 Tests the conditions for all waiting processes. Where condition satisfied,
1675 reactivates that process immediately and removes it from queue.
1676 """
1677 global condQ
1678 rList=[]
1679 for el in condQ:
1680 if el.cond():
1681 rList.append(el)
1682 reactivate(el)
1683 for i in rList:
1684 condQ.remove(i)
1685
1686 if not condQ:
1687 _stopWUStepping()
1688
1690 global condQ
1691 """
1692 Puts a process 'proc' waiting for a condition into a waiting queue.
1693 'cond' is a predicate function which returns True if the condition is
1694 satisfied.
1695 """
1696 if not cond():
1697 condQ.append(proc)
1698 proc.cond=cond
1699 _startWUStepping()
1700
1701 proc._nextTime=None
1702 else:
1703
1704 _e._post(proc,at=_t,prior=1)
1705
1706
1707
1708
1710 """Schedules Processes/semi-coroutines until time 'till'.
1711 Deprecated since version 0.5.
1712 """
1713 simulate(until=till)
1714
1717
1719 """Handles 'yield request,self,res' and 'yield (request,self,res),(<code>,self,par)'.
1720 <code> can be 'hold' or 'waitevent'.
1721 """
1722 if type(a[0][0])==tuple:
1723
1724
1725 b=a[0][0]
1726
1727
1728
1729 b[2]._request(arg=(b,a[1]))
1730
1731
1732 class _Holder(Process):
1733 """Provides timeout process"""
1734 def trigger(self,delay):
1735 yield hold,self,delay
1736 if not proc in b[2].activeQ:
1737 reactivate(proc)
1738
1739 class _EventWait(Process):
1740 """Provides event waiting process"""
1741 def trigger(self,event):
1742 yield waitevent,self,event
1743 if not proc in b[2].activeQ:
1744 a[1].eventsFired=self.eventsFired
1745 reactivate(proc)
1746
1747
1748 proc=a[0][0][1]
1749 actCode=a[0][1][0]
1750 trace.tstop()
1751 if actCode==hold:
1752 proc._holder=_Holder(name="RENEGE-hold for %s"%proc.name)
1753
1754 activate(proc._holder,proc._holder.trigger(a[0][1][2]))
1755 elif actCode==waituntil:
1756 raise FatalSimerror("Illegal code for reneging: waituntil")
1757 elif actCode==waitevent:
1758 proc._holder=_EventWait(name="RENEGE-waitevent for %s"%proc.name)
1759
1760 activate(proc._holder,proc._holder.trigger(a[0][1][2]))
1761 elif actCode==queueevent:
1762 raise FatalSimerror("Illegal code for reneging: queueevent")
1763 else:
1764 raise FatalSimerror("Illegal code for reneging %s"%actCode)
1765 trace.tstart()
1766 else:
1767
1768 a[0][2]._request(a)
1769
1772
1775
1777
1778 evtpar=a[0][2]
1779 if isinstance(evtpar,SimEvent):
1780 a[0][2]._wait(a)
1781
1782 else:
1783
1784 evtpar[0]._waitOR(a)
1785
1787
1788 evtpar=a[0][2]
1789 if isinstance(evtpar,SimEvent):
1790 a[0][2]._queue(a)
1791
1792 else:
1793
1794 evtpar[0]._queueOR(a)
1795
1798
1800 """Handles 'yield get,self,buffer,what,priority' and
1801 'yield (get,self,buffer,what,priority),(<code>,self,par)'.
1802 <code> can be 'hold' or 'waitevent'.
1803 """
1804 if type(a[0][0])==tuple:
1805
1806
1807 b=a[0][0]
1808
1809
1810
1811 b[2]._get(arg=(b,a[1]))
1812
1813
1814 class _Holder(Process):
1815 """Provides timeout process"""
1816 def trigger(self,delay):
1817 yield hold,self,delay
1818
1819 if proc in b[2].getQ:
1820 reactivate(proc)
1821
1822 class _EventWait(Process):
1823 """Provides event waiting process"""
1824 def trigger(self,event):
1825 yield waitevent,self,event
1826 if proc in b[2].getQ:
1827 a[1].eventsFired=self.eventsFired
1828 reactivate(proc)
1829
1830
1831 proc=a[0][0][1]
1832 actCode=a[0][1][0]
1833 if actCode==hold:
1834 proc._holder=_Holder("RENEGE-hold for %s"%proc.name)
1835
1836 activate(proc._holder,proc._holder.trigger(a[0][1][2]))
1837 elif actCode==waituntil:
1838 raise FatalSimerror("Illegal code for reneging: waituntil")
1839 elif actCode==waitevent:
1840 proc._holder=_EventWait(proc.name)
1841
1842 activate(proc._holder,proc._holder.trigger(a[0][1][2]))
1843 elif actCode==queueevent:
1844 raise FatalSimerror("Illegal code for reneging: queueevent")
1845 else:
1846 raise FatalSimerror("Illegal code for reneging %s"%actCode)
1847 else:
1848
1849 a[0][2]._get(a)
1850
1851
1853 """Handles 'yield put' (simple and compound hold/waitevent)
1854 """
1855 if type(a[0][0])==tuple:
1856
1857
1858 b=a[0][0]
1859
1860
1861
1862 b[2]._put(arg=(b,a[1]))
1863
1864
1865 class _Holder(Process):
1866 """Provides timeout process"""
1867 def trigger(self,delay):
1868 yield hold,self,delay
1869
1870 if proc in b[2].putQ:
1871 reactivate(proc)
1872
1873 class _EventWait(Process):
1874 """Provides event waiting process"""
1875 def trigger(self,event):
1876 yield waitevent,self,event
1877 if proc in b[2].putQ:
1878 a[1].eventsFired=self.eventsFired
1879 reactivate(proc)
1880
1881
1882 proc=a[0][0][1]
1883 actCode=a[0][1][0]
1884 if actCode==hold:
1885 proc._holder=_Holder("RENEGE-hold for %s"%proc.name)
1886
1887 activate(proc._holder,proc._holder.trigger(a[0][1][2]))
1888 elif actCode==waituntil:
1889 raise FatalSimerror("Illegal code for reneging: waituntil")
1890 elif actCode==waitevent:
1891 proc._holder=_EventWait("RENEGE-waitevent for %s"%proc.name)
1892
1893 activate(proc._holder,proc._holder.trigger(a[0][1][2]))
1894 elif actCode==queueevent:
1895 raise FatalSimerror("Illegal code for reneging: queueevent")
1896 else:
1897 raise FatalSimerror("Illegal code for reneging %s"%actCode)
1898 else:
1899
1900 a[0][2]._put(a)
1901
1903 """Schedules Processes/semi-coroutines until time 'until'"""
1904
1905 """Gets called once. Afterwards, co-routines (generators) return by
1906 'yield' with a cargo:
1907 yield hold, self, <delay>: schedules the "self" process for activation
1908 after <delay> time units.If <,delay> missing,
1909 same as "yield hold,self,0"
1910
1911 yield passivate,self : makes the "self" process wait to be re-activated
1912
1913 yield request,self,<Resource>[,<priority>]: request 1 unit from <Resource>
1914 with <priority> pos integer (default=0)
1915
1916 yield release,self,<Resource> : release 1 unit to <Resource>
1917
1918 yield waitevent,self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]:
1919 wait for one or more of several events
1920
1921
1922 yield queueevent,self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]:
1923 queue for one or more of several events
1924
1925 yield waituntil,self,cond : wait for arbitrary condition
1926
1927 yield get,self,<buffer>[,<WhatToGet>[,<priority>]]
1928 get <WhatToGet> items from buffer (default=1);
1929 <WhatToGet> can be a pos integer or a filter function
1930 (Store only)
1931
1932 yield put,self,<buffer>[,<WhatToPut>[,priority]]
1933 put <WhatToPut> items into buffer (default=1);
1934 <WhatToPut> can be a pos integer (Level) or a list of objects
1935 (Store)
1936
1937 EXTENSIONS:
1938 Request with timeout reneging:
1939 yield (request,self,<Resource>),(hold,self,<patience>) :
1940 requests 1 unit from <Resource>. If unit not acquired in time period
1941 <patience>, self leaves waitQ (reneges).
1942
1943 Request with event-based reneging:
1944 yield (request,self,<Resource>),(waitevent,self,<eventlist>):
1945 requests 1 unit from <Resource>. If one of the events in <eventlist> occurs before unit
1946 acquired, self leaves waitQ (reneges).
1947
1948 Get with timeout reneging (for Store and Level):
1949 yield (get,self,<buffer>,nrToGet etc.),(hold,self,<patience>)
1950 requests <nrToGet> items/units from <buffer>. If not acquired <nrToGet> in time period
1951 <patience>, self leaves <buffer>.getQ (reneges).
1952
1953 Get with event-based reneging (for Store and Level):
1954 yield (get,self,<buffer>,nrToGet etc.),(waitevent,self,<eventlist>)
1955 requests <nrToGet> items/units from <buffer>. If not acquired <nrToGet> before one of
1956 the events in <eventlist> occurs, self leaves <buffer>.getQ (reneges).
1957
1958
1959
1960 Event notices get posted in event-list by scheduler after "yield" or by
1961 "activate"/"reactivate" functions.
1962
1963 """
1964 global _endtime,_e,_stop,_t,_wustep
1965 _stop=False
1966
1967 if _e is None:
1968 raise FatalSimerror("Simulation not initialized")
1969 if _e._isEmpty():
1970 message="SimPy: No activities scheduled"
1971 return message
1972
1973 _endtime=until
1974 message="SimPy: Normal exit"
1975 dispatch={hold:holdfunc,request:requestfunc,release:releasefunc,
1976 passivate:passivatefunc,waitevent:waitevfunc,queueevent:queueevfunc,
1977 waituntil:waituntilfunc,get:getfunc,put:putfunc}
1978 commandcodes=dispatch.keys()
1979 commandwords={hold:"hold",request:"request",release:"release",passivate:"passivate",
1980 waitevent:"waitevent",queueevent:"queueevent",waituntil:"waituntil",
1981 get:"get",put:"put"}
1982 nextev=_e._nextev
1983 while not _stop and _t<=_endtime:
1984 try:
1985 a=nextev()
1986 if not a[0] is None:
1987
1988 if type(a[0][0])==tuple:
1989
1990 command=a[0][0][0]
1991 else:
1992 command = a[0][0]
1993 if __debug__:
1994 if not command in commandcodes:
1995 raise FatalSimerror("Illegal command: yield %s"%command)
1996 dispatch[command](a)
1997 trace.recordEvent(command,a)
1998 else:
1999 if not a==(None,):
2000 trace.tterminated(a[1])
2001 except FatalSimerror,error:
2002 print "SimPy: "+error.value
2003 sys.exit(1)
2004 except Simerror,error:
2005 message="SimPy: "+error.value
2006 _stop = True
2007 if _wustep:
2008 _test()
2009 _stopWUStepping()
2010 _e=None
2011 if not(trace.outfile is sys.stdout):
2012 trace.outfile.close()
2013 return message
2014
2016 commands={hold:"hold",passivate:"passivate",request:"request",release:"release",
2017 waitevent:"waitevent",queueevent:"queueevent",waituntil:"waituntil",
2018 get:"get",put:"put"}
2019
2020 - def __init__(self,start=0,end=10000000000L,toTrace=\
2021 ["hold","activate","cancel","reactivate","passivate","request",
2022 "release","interrupt","terminated","waitevent","queueevent",
2023 "signal","waituntil","put","get"
2024 ],outfile=sys.stdout):
2038
2040 Trace.commandsproc={hold:Trace.thold,passivatre:Trace.tpassivate,
2041 request:Trace.trequest,release:Trace.trelease,
2042 waitevent:Trace.twaitevent,
2043 queueevent:Trace.tqueueevent,
2044 waituntil:Trace.twaituntil,
2045 get:Trace.tget,put:Trace.tput}
2046 self.start=0
2047 self.end=10000000000L
2048 self.toTrace=["hold","activate","cancel","reactivate","passivate","request",
2049 "release","interrupt","terminated","waitevent","queueevent",
2050 "signal","waituntil","put","get"]
2051 self.tracego=True
2052 self.outfile=sys.stdout
2053 self._comment=None
2054
2056 for v in kmvar.keys():
2057 if v=="start":
2058 self.start=kmvar[v]
2059 elif v=="end":
2060 self.end=kmvar[v]
2061 elif v=="toTrace":
2062 self.toTrace=kmvar[v]
2063 elif v=="outfile":
2064 self.outfile=kmvar[v]
2065
2067 self.tracego=True
2068
2070 self.tracego=False
2071
2073 if self.tracego and (self.start <= now() <= self.end) and cond:
2074 return True
2075
2077 try:
2078 return "delay: %s"%par[0][2]
2079 except:
2080 return 0
2081 thold=classmethod(thold)
2082
2084 res=par[0][2]
2085 if len(par[0])==4:
2086 priority=" priority: "+str(par[0][3])
2087 else:
2088 priority=" priority: default"
2089 wQ=[x.name for x in res.waitQ]
2090 aQ=[x.name for x in res.activeQ]
2091 return "<%s> %s \n. . .waitQ: %s \n. . .activeQ: %s"%(res.name,priority,wQ,aQ)
2092 trequest=classmethod(trequest)
2093
2095 res=par[0][2]
2096 wQ=[x.name for x in res.waitQ]
2097 aQ=[x.name for x in res.activeQ]
2098 return "<%s> \n. . .waitQ: %s \n. . .activeQ: %s"%(res.name,wQ,aQ)
2099 trelease=classmethod(trelease)
2100
2102 return ""
2103 tpassivate=classmethod(tpassivate)
2104
2106 pass
2107 tactivate=classmethod(tactivate)
2108
2110 evt=par[0][2]
2111 if type(evt)==list or type(evt)==tuple:
2112 enames=[x.name for x in evt]
2113 return "waits for events <%s>"%enames
2114 else:
2115 return "waits for event <%s>"%evt.name
2116 twaitevent=classmethod(twaitevent)
2117
2119 evt=par[0][2]
2120 if type(evt)==list or type(evt)==tuple:
2121 enames=[x.name for x in evt]
2122 return "queues for events <%s>"%enames
2123 else:
2124 return "queues for event <%s>"%evt.name
2125 tqueueevent=classmethod(tqueueevent)
2126
2128 wQ=[x.name for x in evt.waits]
2129 qQ=[x.name for x in evt.queues]
2130 return "<%s> \n. . . occurred: %s\n. . . waiting: %s\n. . . queueing: %s"\
2131 %(evt.name,evt.occurred,wQ,qQ)
2132 pass
2133 tsignal=classmethod(tsignal)
2134
2136 condition=par[0][2]
2137 return "for condition <%s>"%condition.func_name
2138 twaituntil=classmethod(twaituntil)
2139
2140 - def tget(self,par):
2141 buff=par[0][2]
2142 if len(par[0])==5:
2143 priority=" priority: "+str(par[0][4])
2144 else:
2145 priority=" priority: default"
2146 if len(par[0])==3:
2147 nrToGet=1
2148 else:
2149 nrToGet=par[0][3]
2150 toGet="to get: %s %s from"%(nrToGet,buff.unitName)
2151 getQ=[x.name for x in buff.getQ]
2152 putQ=[x.name for x in buff.putQ]
2153 try:
2154 inBuffer=buff.amount
2155 except:
2156 inBuffer=buff.nrBuffered
2157 return "%s <%s> %s \n. . .getQ: %s \n. . .putQ: %s \n. . .in buffer: %s"\
2158 %(toGet,buff.name,priority,getQ,putQ,inBuffer)
2159 tget=classmethod(tget)
2160
2161 - def tput(self,par):
2162 buff=par[0][2]
2163 if len(par[0])==5:
2164 priority=" priority: "+str(par[0][4])
2165 else:
2166 priority=" priority: default"
2167 if len(par[0])==3:
2168 nrToPut=1
2169 else:
2170 if type(par[0][3])==type([]):
2171 nrToPut=len(par[0][3])
2172 else:
2173 nrToPut=par[0][3]
2174 getQ=[x.name for x in buff.getQ]
2175 putQ=[x.name for x in buff.putQ]
2176 toPut="to put: %s %s into"%(nrToPut,buff.unitName)
2177 try:
2178 inBuffer=buff.amount
2179 except:
2180 inBuffer=buff.nrBuffered
2181 return "%s <%s> %s \n. . .getQ: %s \n. . .putQ: %s \n. . .in buffer: %s"\
2182 %(toPut,buff.name,priority,getQ,putQ,inBuffer)
2183 tput=classmethod(tput)
2184
2186 if self.ifTrace(Trace.commands[command] in self.toTrace):
2187 if not type(whole[0][0])==tuple:
2188 try:
2189 print >>self.outfile, now(),Trace.commands[command],\
2190 "<"+whole[0][1].name+">",\
2191 Trace.commandsproc[command](whole)
2192 except TypeError:
2193 print "l.1649: whole[0][1].name",whole[0][1].name,\
2194 Trace.commands[command],Trace.commandsproc[command]
2195 Trace.commands[command],Trace.commandsproc[command]
2196 if self._comment:
2197 print >>self.outfile,"----",self._comment
2198 else:
2199
2200 print >>self.outfile, now(),Trace.commands[command],\
2201 "<"+whole[0][0][1].name+">"+\
2202 Trace.commandsproc[command](whole[0])
2203 print >>self.outfile,"|| RENEGE COMMAND:"
2204 command1=whole[0][1][0]
2205 print >>self.outfile,"||\t",Trace.commands[command1],\
2206 "<"+whole[0][1][1].name+">",\
2207 Trace.commandsproc[command1]((whole[0][1],))
2208 if self._comment:
2209 print >>self.outfile,"----",self._comment
2210
2211 self._comment=None
2212
2214 if self.ifTrace("interrupt" in self.toTrace):
2215 print >>self.outfile,"%s interrupt by: <%s> of: <%s>"%(now(),who.name,victim.name)
2216 if self._comment:
2217 print >>self.outfile,"----",self._comment
2218 self._comment=None
2219
2221 if self.ifTrace("cancel" in self.toTrace):
2222 print >>self.outfile,"%s cancel by: <%s> of: <%s>"%(now(),who.name,victim.name)
2223 if self._comment:
2224 print >>self.outfile,"----",self._comment
2225 self._comment=None
2226
2228 if self.ifTrace("activate" in self.toTrace):
2229 print >>self.outfile,"%s activate <%s> at time: %s prior: %s"%(now(),who.name,\
2230 when, prior)
2231 if self._comment:
2232 print >>self.outfile,"----",self._comment
2233 self._comment=None
2234
2236 if self.ifTrace("reactivate" in self.toTrace):
2237 print >>self.outfile,"%s reactivate <%s> time: %s prior: %s"%(now(),who.name,\
2238 when, prior)
2239 if self._comment:
2240 print >>self.outfile,"----",self._comment
2241 self._comment=None
2242
2244 if self.ifTrace("signal" in self.toTrace):
2245 print >>self.outfile,"%s event <%s> is signalled"%(now(),evt.name)
2246 if self._comment:
2247 print >>self.outfile,"----",self._comment
2248 self._comment=None
2249
2251 if self.ifTrace("terminated" in self.toTrace):
2252 print >>self.outfile,"%s <%s> terminated"%(now(),who.name)
2253 if self._comment:
2254 print >>self.outfile,"----",self._comment
2255 self._comment=None
2256
2257 - def ttext(self,par):
2258 self._comment=par
2259
2261 """ A histogram gathering and sampling class"""
2262
2263 - def __init__(self,name = '',low=0.0,high=100.0,nbins=10):
2264 list.__init__(self)
2265 self.name = name
2266 self.low = low
2267 self.high = high
2268 self.nbins = nbins
2269 self.binsize=(self.high-self.low)/nbins
2270
2271 self[:] =[[low+(i-1)*self.binsize,0] for i in range(self.nbins+2)]
2272
2273
2275 """ add a value into the correct bin"""
2276 b = int((y-self.low+self.binsize)/self.binsize)
2277 if b < 0: b = 0
2278 if b > self.nbins+1: b = self.nbins+1
2279 assert 0 <= b <=self.nbins+1,'Histogram.addIn: b out of range: %s'%b
2280 self[b][1]+=1
2281
2282
2283
2284 if __name__ == "__main__":
2285 print "SimPy.SimulationTrace %s" %__version__
2286
2288 class Aa(Process):
2289 sequIn=[]
2290 sequOut=[]
2291 def __init__(self,holdtime,name):
2292 Process.__init__(self,name)
2293 self.holdtime=holdtime
2294
2295 def life(self,priority):
2296 for i in range(1):
2297 Aa.sequIn.append(self.name)
2298 print now(),rrr.name,"waitQ:",len(rrr.waitQ),"activeQ:",\
2299 len(rrr.activeQ)
2300 print "waitQ: ",[(k.name,k._priority[rrr]) for k in rrr.waitQ]
2301 print "activeQ: ",[(k.name,k._priority[rrr]) \
2302 for k in rrr.activeQ]
2303 assert rrr.n+len(rrr.activeQ)==rrr.capacity, \
2304 "Inconsistent resource unit numbers"
2305 print now(),self.name,"requests 1 ", rrr.unitName
2306 yield request,self,rrr,priority
2307 print now(),self.name,"has 1 ",rrr.unitName
2308 print now(),rrr.name,"waitQ:",len(rrr.waitQ),"activeQ:",\
2309 len(rrr.activeQ)
2310 print now(),rrr.name,"waitQ:",len(rrr.waitQ),"activeQ:",\
2311 len(rrr.activeQ)
2312 assert rrr.n+len(rrr.activeQ)==rrr.capacity, \
2313 "Inconsistent resource unit numbers"
2314 yield hold,self,self.holdtime
2315 print now(),self.name,"gives up 1",rrr.unitName
2316 yield release,self,rrr
2317 Aa.sequOut.append(self.name)
2318 print now(),self.name,"has released 1 ",rrr.unitName
2319 print "waitQ: ",[(k.name,k._priority[rrr]) for k in rrr.waitQ]
2320 print now(),rrr.name,"waitQ:",len(rrr.waitQ),"activeQ:",\
2321 len(rrr.activeQ)
2322 assert rrr.n+len(rrr.activeQ)==rrr.capacity, \
2323 "Inconsistent resource unit numbers"
2324
2325 class Observer(Process):
2326 def __init__(self):
2327 Process.__init__(self)
2328
2329 def observe(self,step,processes,res):
2330 while now()<11:
2331 for i in processes:
2332 print "++ %s process: %s: active:%s, passive:%s, terminated: %s,interrupted:%s, queuing:%s"\
2333 %(now(),i.name,i.active(),i.passive(),i.terminated(),i.interrupted(),i.queuing(res))
2334 print
2335 yield hold,self,step
2336
2337 print"\n+++test_demo output"
2338 print "****First case == priority queue, resource service not preemptable"
2339 initialize()
2340 rrr=Resource(5,name="Parking",unitName="space(s)", qType=PriorityQ,
2341 preemptable=0)
2342 procs=[]
2343 for i in range(10):
2344 z=Aa(holdtime=i,name="Car "+str(i))
2345 procs.append(z)
2346 activate(z,z.life(priority=i))
2347 o=Observer()
2348 activate(o,o.observe(1,procs,rrr))
2349 a=simulate(until=10000)
2350 print a
2351 print "Input sequence: ",Aa.sequIn
2352 print "Output sequence: ",Aa.sequOut
2353
2354 print "\n****Second case == priority queue, resource service preemptable"
2355 initialize()
2356 rrr=Resource(5,name="Parking",unitName="space(s)", qType=PriorityQ,
2357 preemptable=1)
2358 procs=[]
2359 for i in range(10):
2360 z=Aa(holdtime=i,name="Car "+str(i))
2361 procs.append(z)
2362 activate(z,z.life(priority=i))
2363 o=Observer()
2364 activate(o,o.observe(1,procs,rrr))
2365 Aa.sequIn=[]
2366 Aa.sequOut=[]
2367 a=simulate(until=10000)
2368 print a
2369 print "Input sequence: ",Aa.sequIn
2370 print "Output sequence: ",Aa.sequOut
2371
2373 class Bus(Process):
2374 def __init__(self,name):
2375 Process.__init__(self,name)
2376
2377 def operate(self,repairduration=0):
2378 print now(),">> %s starts" %(self.name)
2379 tripleft = 1000
2380 while tripleft > 0:
2381 yield hold,self,tripleft
2382 if self.interrupted():
2383 print "interrupted by %s" %self.interruptCause.name
2384 print "%s: %s breaks down " %(now(),self.name)
2385 tripleft=self.interruptLeft
2386 self.interruptReset()
2387 print "tripleft ",tripleft
2388 reactivate(br,delay=repairduration)
2389 yield hold,self,repairduration
2390 print now()," repaired"
2391 else:
2392 break
2393 print now(),"<< %s done" %(self.name)
2394
2395 class Breakdown(Process):
2396 def __init__(self,myBus):
2397 Process.__init__(self,name="Breakdown "+myBus.name)
2398 self.bus=myBus
2399
2400 def breakBus(self,interval):
2401
2402 while True:
2403 yield hold,self,interval
2404 if self.bus.terminated(): break
2405 self.interrupt(self.bus)
2406
2407 print"\n\n+++test_interrupt"
2408 initialize()
2409 b=Bus("Bus 1")
2410 activate(b,b.operate(repairduration=20))
2411 br=Breakdown(b)
2412 activate(br,br.breakBus(200))
2413 print simulate(until=4000)
2414
2416 class Waiter(Process):
2417 def waiting(self,theSignal):
2418 while True:
2419 yield waitevent,self,theSignal
2420 print "%s: process '%s' continued after waiting for %s"%(now(),self.name,theSignal.name)
2421 yield queueevent,self,theSignal
2422 print "%s: process '%s' continued after queueing for %s"%(now(),self.name,theSignal.name)
2423
2424 class ORWaiter(Process):
2425 def waiting(self,signals):
2426 while True:
2427 yield waitevent,self,signals
2428 print now(),"one of %s signals occurred"%[x.name for x in signals]
2429 print "\t%s (fired/param)"%[(x.name,x.signalparam) for x in self.eventsFired]
2430 yield hold,self,1
2431
2432 class Caller(Process):
2433 def calling(self):
2434 while True:
2435 signal1.signal("wake up!")
2436 print "%s: signal 1 has occurred"%now()
2437 yield hold,self,10
2438 signal2.signal("and again")
2439 signal2.signal("sig 2 again")
2440 print "%s: signal1, signal2 have occurred"%now()
2441 yield hold,self,10
2442 print"\n+++testSimEvents output"
2443 initialize()
2444 signal1=SimEvent("signal 1")
2445 signal2=SimEvent("signal 2")
2446 signal1.signal("startup1")
2447 signal2.signal("startup2")
2448 w1=Waiter("waiting for signal 1")
2449 activate(w1,w1.waiting(signal1))
2450 w2=Waiter("waiting for signal 2")
2451 activate(w2,w2.waiting(signal2))
2452 w3=Waiter("also waiting for signal 2")
2453 activate(w3,w3.waiting(signal2))
2454 w4=ORWaiter("waiting for either signal 1 or signal 2")
2455 activate(w4,w4.waiting([signal1,signal2]),prior=True)
2456 c=Caller("Caller")
2457 activate(c,c.calling())
2458 print simulate(until=100)
2459
2461 """
2462 Demo of waitUntil capability.
2463
2464 Scenario:
2465 Three workers require sets of tools to do their jobs. Tools are shared, scarce
2466 resources for which they compete.
2467 """
2468
2469
2470 class Worker(Process):
2471 def __init__(self,name,heNeeds=[]):
2472 Process.__init__(self,name)
2473 self.heNeeds=heNeeds
2474 def work(self):
2475
2476 def workerNeeds():
2477 for item in self.heNeeds:
2478 if item.n==0:
2479 return False
2480 return True
2481
2482 while now()<8*60:
2483 yield waituntil,self,workerNeeds
2484 for item in self.heNeeds:
2485 yield request,self,item
2486 print "%s %s has %s and starts job" %(now(),self.name,
2487 [x.name for x in self.heNeeds])
2488 yield hold,self,random.uniform(10,30)
2489 for item in self.heNeeds:
2490 yield release,self,item
2491 yield hold,self,2
2492
2493 print "\n+++ nwaituntil demo output"
2494 initialize()
2495 brush=Resource(capacity=1,name="brush")
2496 ladder=Resource(capacity=2,name="ladder")
2497 hammer=Resource(capacity=1,name="hammer")
2498 saw=Resource(capacity=1,name="saw")
2499 painter=Worker("painter",[brush,ladder])
2500 activate(painter,painter.work())
2501 roofer=Worker("roofer",[hammer,ladder,ladder])
2502 activate(roofer,roofer.work())
2503 treeguy=Worker("treeguy",[saw,ladder])
2504 activate(treeguy,treeguy.work())
2505 for who in (painter,roofer,treeguy):
2506 print "%s needs %s for his job" %(who.name,[x.name for x in who.heNeeds])
2507 print
2508 print simulate(until=9*60)
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2520 """ Job class for testing timeout reneging
2521 """
2522 - def __init__(self,server=None,name=""):
2523 Process.__init__(self,name)
2524 self.res=server
2525 self.gotResource=None
2526
2527 - def execute(self,timeout,usetime):
2528 yield (request,self,self.res),(hold,self,timeout)
2529 if self.acquired(self.res):
2530 self.gotResource=True
2531 yield hold,self,usetime
2532 yield release,self,self.res
2533 else:
2534 self.gotResource=False
2535
2536
2538 """Test that resource gets acquired without timeout
2539 """
2540 res=Resource(name="Server",capacity=1)
2541 initialize()
2542 usetime=5
2543 timeout=1000000
2544 j1=JobTO(server=res,name="Job_1")
2545 activate(j1,j1.execute(timeout=timeout,usetime=usetime))
2546 j2=JobTO(server=res,name="Job_2")
2547 activate(j2,j2.execute(timeout=timeout,usetime=usetime))
2548 simulate(until=2*usetime)
2549 assert now()==2*usetime,"time not ==2*usetime"
2550 assert j1.gotResource and j2.gotResource,\
2551 "at least one job failed to get resource"
2552 assert not (res.waitQ or res.activeQ),\
2553 "job waiting or using resource"
2554
2556 """Test that timeout occurs when resource busy
2557 """
2558 res=Resource(name="Server",capacity=1,monitored=True)
2559 initialize()
2560 usetime=5
2561 timeout=3
2562 j1=JobTO(server=res,name="Job_1")
2563 activate(j1,j1.execute(timeout=timeout,usetime=usetime))
2564 j2=JobTO(server=res,name="Job_2")
2565 activate(j2,j2.execute(timeout=timeout,usetime=usetime))
2566 simulate(until=2*usetime)
2567 assert(now()==usetime),"time not ==usetime"
2568 assert(j1.gotResource),"Job_1 did not get resource"
2569 assert(not j2.gotResource),"Job_2 did not renege"
2570 assert not (res.waitQ or res.activeQ),\
2571 "job waiting or using resource"
2572
2574 """Test that timeout occurs when resource has no capacity free
2575 """
2576 res=Resource(name="Server",capacity=0)
2577 initialize()
2578 usetime=5
2579 timeout=3
2580 j1=JobTO(server=res,name="Job_1")
2581 activate(j1,j1.execute(timeout=timeout,usetime=usetime))
2582 j2=JobTO(server=res,name="Job_2")
2583 activate(j2,j2.execute(timeout=timeout,usetime=usetime))
2584 simulate(until=2*usetime)
2585 assert now()==timeout,"time %s not == timeout"%now()
2586 assert not j1.gotResource,"Job_1 got resource"
2587 assert not j2.gotResource,"Job_2 got resource"
2588 assert not (res.waitQ or res.activeQ),\
2589 "job waiting or using resource"
2590
2591
2592
2593
2594
2596 """ Job class for testing event reneging
2597 """
2598 - def __init__(self,server=None,name=""):
2599 Process.__init__(self,name)
2600 self.res=server
2601 self.gotResource=None
2602
2604 yield (request,self,self.res),(waitevent,self,event)
2605 if self.acquired(self.res):
2606 self.gotResource=True
2607 yield hold,self,usetime
2608 yield release,self,self.res
2609 else:
2610 self.gotResource=False
2611
2613 """ Job class for testing event reneging with multi-event lists
2614 """
2615 - def __init__(self,server=None,name=""):
2616 Process.__init__(self,name)
2617 self.res=server
2618 self.gotResource=None
2619
2620 - def execute(self,eventlist,usetime):
2621 yield (request,self,self.res),(waitevent,self,eventlist)
2622 if self.acquired(self.res):
2623 self.gotResource=True
2624 yield hold,self,usetime
2625 yield release,self,self.res
2626 else:
2627 self.gotResource=False
2628
2630 """Fires reneging event
2631 """
2632 - def fire(self,fireDelay,event):
2633 yield hold,self,fireDelay
2634 event.signal()
2635
2637 """Test that processes acquire resource normally if no event fires
2638 """
2639 res=Resource(name="Server",capacity=1)
2640 event=SimEvent("Renege_trigger")
2641 initialize()
2642 usetime=5
2643 j1=JobEvt(server=res,name="Job_1")
2644 activate(j1,j1.execute(event=event,usetime=usetime))
2645 j2=JobEvt(server=res,name="Job_2")
2646 activate(j2,j2.execute(event=event,usetime=usetime))
2647 simulate(until=2*usetime)
2648
2649 assert now()==2*usetime,"time not ==2*usetime"
2650 assert j1.gotResource and j2.gotResource,\
2651 "at least one job failed to get resource"
2652 assert not (res.waitQ or res.activeQ),\
2653 "job waiting or using resource"
2654
2656 """Test that signalled event leads to renege when resource busy
2657 """
2658 res=Resource(name="Server",capacity=1)
2659 initialize()
2660 event=SimEvent("Renege_trigger")
2661 usetime=5
2662 eventtime=1
2663 j1=JobEvt(server=res,name="Job_1")
2664 activate(j1,j1.execute(event=event,usetime=usetime))
2665 j2=JobEvt(server=res,name="Job_2")
2666 activate(j2,j2.execute(event=event,usetime=usetime))
2667 f=FireEvent(name="FireEvent")
2668 activate(f,f.fire(fireDelay=eventtime,event=event))
2669 simulate(until=2*usetime)
2670
2671 assert(now()==usetime),"time not ==usetime"
2672 assert(j1.gotResource),"Job_1 did not get resource"
2673 assert(not j2.gotResource),"Job_2 did not renege"
2674 assert not (res.waitQ or res.activeQ),\
2675 "job waiting or using resource"
2676
2678 """Test that renege-triggering event can be one of an event list
2679 """
2680 res=Resource(name="Server",capacity=1)
2681 initialize()
2682 event1=SimEvent("Renege_trigger_1")
2683 event2=SimEvent("Renege_trigger_2")
2684 usetime=5
2685 eventtime=1
2686 j1=JobEvtMulti(server=res,name="Job_1")
2687 activate(j1,j1.execute(eventlist=[event1,event2],usetime=usetime))
2688 j2=JobEvtMulti(server=res,name="Job_2")
2689 activate(j2,j2.execute(eventlist=[event1,event2],usetime=usetime))
2690 f1=FireEvent(name="FireEvent_1")
2691 activate(f1,f1.fire(fireDelay=eventtime,event=event1))
2692 f2=FireEvent(name="FireEvent_2")
2693 activate(f2,f2.fire(fireDelay=eventtime,event=event2))
2694 simulate(until=2*usetime)
2695
2696 assert(now()==usetime),"time not ==usetime"
2697 assert(j1.gotResource),"Job_1 did not get resource"
2698 assert(not j2.gotResource),"Job_2 did not renege"
2699 assert not (res.waitQ or res.activeQ),\
2700 "job waiting or using resource"
2701 trace=Trace()
2702 testNoTimeout()
2703 testTimeout1()
2704 testTimeout2()
2705 testNoEvent()
2706 testWaitEvent1()
2707 testWaitEvent2()
2708 trace=Trace(end=4000)
2709 test_demo()
2710 trace=Trace(end=2000)
2711 test_interrupt()
2712 testSimEvents()
2713 testwaituntil()
2714
2715
2716 else:
2717 trace=Trace()
2718