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