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