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