Package SimPy :: Module SimulationTrace
[hide private]
[frames] | no frames]

Source Code for Module SimPy.SimulationTrace

   1  #!/usr/bin/env python 
   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  # $Revision: 1.1.1.27 $ $Date: 2007/01/18 14:43:59 $ kgm 
  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  # yield keywords 
 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 #controls per event stepping for waituntil construct; not for user API 
 155  True=1 
 156  False=0 
 157  condQ=[] 
 158  allMonitors=[] 
 159  allTallies=[] 
 160   
161 -def initialize():
162 global _e,_t,_stop,condQ,allMonitors,allTallies 163 _e=__Evlist() 164 _t=0 165 _stop=False 166 condQ=[] 167 allMonitors=[] 168 allTallies=[]
169
170 -def now():
171 return _t
172
173 -def stopSimulation():
174 """Application function to stop simulation run""" 175 global _stop 176 _stop=True
177
178 -def _startWUStepping():
179 """Application function to start stepping through simulation for waituntil construct.""" 180 global _wustep 181 _wustep=True
182
183 -def _stopWUStepping():
184 """Application function to stop stepping through simulation.""" 185 global _wustep 186 _wustep=False
187
188 -class Simerror(Exception):
189 - def __init__(self,value):
190 self.value=value
191
192 - def __str__(self):
193 return `self.value`
194
195 -class FatalSimerror(Simerror):
196 - def __init__(self,value):
197 Simerror.__init__(self,value) 198 self.value=value
199
200 -class Process(Lister):
201 """Superclass of classes which may use generator functions"""
202 - def __init__(self,name="a_process"):
203 #the reference to this Process instances single process (==generator) 204 self._nextpoint=None 205 self.name=name 206 self._nextTime=None #next activation time 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=[] #events waited/queued for occurred
215
216 - def active(self):
217 return self._nextTime <> None and not self._inInterrupt
218
219 - def passive(self):
220 return self._nextTime is None and not self._terminated
221
222 - def terminated(self):
223 return self._terminated
224
225 - def interrupted(self):
226 return self._inInterrupt and not self._terminated
227
228 - def queuing(self,resource):
229 return self in resource.waitQ
230 # TODO: Review whether this should include waiting for a buffer; or throw away?
231 - def cancel(self,victim):
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
236 - def _hold(self,a):
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
247 - def _passivate(self,a):
248 a[0][1]._nextTime=None
249
250 - def interrupt(self,victim):
251 """Application function to interrupt active processes""" 252 # can't interrupt terminated/passive/interrupted process 253 if victim.active(): 254 save=trace._comment 255 trace._comment=None 256 victim.interruptCause=self # self causes interrupt 257 left=victim._nextTime-_t 258 victim.interruptLeft=left # time left in current 'hold' 259 victim._inInterrupt=True 260 reactivate(victim) 261 trace._comment=save 262 trace.recordInterrupt(self,victim) 263 return left 264 else: #victim not active -- can't interrupt 265 return None
266
267 - def interruptReset(self):
268 """ 269 Application function for an interrupt victim to get out of 270 'interrupted' state. 271 """ 272 self._inInterrupt= False
273
274 - def acquired(self,res):
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
318 - def stored(self,buffer):
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: #reneged 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
332 -def allEventNotices():
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
343 -def allEventTimes():
344 """Returns list of all times for which events are scheduled. 345 """ 346 return _e.timestamps
347 348
349 -class __Evlist:
350 """Defines event list and operations on it"""
351 - def __init__(self):
352 #has the structure {<time1>:(ev notice1, ev notice2, . . ), 353 # <time2>:(ev notice 3,...),..} 354 self.events={} 355 #always sorted list of event times (keys for self.events) 356 self.timestamps=[]
357
358 - def _post(self,what,at,prior=False):
359 """Post an event notice for process what for time at""" 360 # event notices are _Action instances 361 if at < _t: 362 raise Simerror("Attempt to schedule event in the past") 363 if at in self.events: 364 if prior: 365 #before all other event notices at this time 366 self.events[at][0:0]= [what] 367 else: 368 self.events[at].append(what) 369 else: # first event notice at this time 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: #check if whom was actually active 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 # no other ev notices for thistime 390 del(self.events[thistime]) 391 item_delete_point = bisect.bisect(self.timestamps, 392 thistime) 393 del self.timestamps[item_delete_point-1]
394
395 - def _nextev(self):
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] #list of actions for this time _t 402 tempev=temp[0] #first action 403 del(self.events[earliest][0]) 404 if self.events[earliest]==[]: 405 del(self.events[earliest]) #delete empty list of actions 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
422 -class _Action:
423 """Structure (who=process owner, generator=process)"""
424 - def __init__(self,who,generator):
425 self.who=who 426 self.process=generator
427
428 - def __str__(self):
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 #store generator reference in object; needed for reactivation 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
451 -def reactivate(obj,at="undefined",delay="undefined",prior=False):
452 """Application function to reactivate a process which is active, 453 suspended or passive.""" 454 # Object may be active, suspended or passive 455 if not obj._terminated: 456 a=Process("SimPysystem") 457 a.cancel(obj) 458 # object now passive 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
468 -class Histogram(list):
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
482 - def addIn(self,y):
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
492 - def __str__(self):
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
530 -def startCollection(when=0.0,monitors=None,tallies=None):
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
549 -class Monitor(list):
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
576 - def observe(self,y,t=None):
577 """record y and t""" 578 if t is None: t = now() 579 self.append([t,y])
580
581 - def tally(self,y):
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
589 - def reset(self,t=None):
590 """reset the sums and counts for the monitored variable """ 591 self[:]=[] 592 if t is None: t = now() 593 self.startTime = t
594
595 - def tseries(self):
596 """ the series of measured times""" 597 return list(zip(*self)[0])
598
599 - def yseries(self):
600 """ the series of measured values""" 601 return list(zip(*self)[1])
602
603 - def count(self):
604 """ deprecated: the number of observations made """ 605 return self.__len__()
606
607 - def total(self):
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 # replace by sum() later
615
616 - def mean(self):
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
621 - def var(self):
622 """ the sample variance of the monitored variable """ 623 n = len(self) 624 tot = self.total() 625 ssq=0.0 626 ##yy = self.yseries() 627 for i in range(self.__len__()): 628 ssq += self[i][1]**2 # replace by sum() eventually 629 try: return (ssq - float(tot*tot)/n)/n 630 except: print 'SimPy: No observations for sample variance'
631
632 - def timeAverage(self,t=None):
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 #print 'DEBUG: timave ',t,tlast 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 #print 'DEBUG: timave ',sum,t,T 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
669 - def getHistogram(self):
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
678 - def printHistogram(self,fmt="%s"):
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
721 -class Tally:
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
743 - def observe(self, y, t=None):
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
768 - def count(self):
769 return self._count
770
771 - def total(self):
772 return self._total
773
774 - def mean(self):
775 return 1.0 * self._total / self._count
776
777 - def timeAverage(self,t=None):
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
787 - def var(self):
788 return 1.0 * (self._sum_of_squares - (1.0 * (self._sum * self._sum)\ 789 / self._count)) / (self._count)
790
791 - def __len__(self):
792 return self._count
793
794 - def __eq__(self, l):
795 return len(l) == self._count
796
797 - def getHistogram(self):
798 return self.histo
799
800 - def printHistogram(self,fmt="%s"):
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
843 -class Queue(list):
844 - def __init__(self,res,moni):
845 if not moni is None: #moni==[]: 846 self.monit=True # True if a type of Monitor/Tally attached 847 else: 848 self.monit=False 849 self.moni=moni # The Monitor/Tally 850 self.resource=res # the resource/buffer this queue belongs to
851
852 - def enter(self,obj):
853 pass
854
855 - def leave(self):
856 pass
857
858 - def takeout(self,obj):
859 self.remove(obj) 860 if self.monit: 861 self.moni.observe(len(self),t=now())
862
863 -class FIFO(Queue):
864 - def __init__(self,res,moni):
865 Queue.__init__(self,res,moni)
866
867 - def enter(self,obj):
868 self.append(obj) 869 if self.monit: 870 self.moni.observe(len(self),t=now())
871
872 - def enterGet(self,obj):
873 self.enter(obj)
874
875 - def enterPut(self,obj):
876 self.enter(obj)
877
878 - def leave(self):
879 a= self.pop(0) 880 if self.monit: 881 self.moni.observe(len(self),t=now()) 882 return a
883
884 -class PriorityQ(FIFO):
885 """Queue is always ordered according to priority. 886 Higher value of priority attribute == higher priority. 887 """
888 - def __init__(self,res,moni):
889 FIFO.__init__(self,res,moni)
890
891 - def enter(self,obj):
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
907 - def enterGet(self,obj):
908 """Handles getQ in Buffer""" 909 if len(self): 910 ix=self.resource 911 #print "priority:",[x._priority[ix] for x in self] 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
924 - def enterPut(self,obj):
925 """Handles putQ in Buffer""" 926 if len(self): 927 ix=self.resource 928 #print "priority:",[x._priority[ix] for x in self] 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
941 -class Resource(Lister):
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 # resource name 952 self.capacity=capacity # resource units in this resource 953 self.unitName=unitName # type name of resource units 954 self.n=capacity # uncommitted resource units 955 self.monitored=monitored 956 957 if self.monitored: # Monitor waitQ, activeQ 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
972 - def _request(self,arg):
973 """Process request event for this resource""" 974 object=arg[1].who 975 if len(arg[0]) == 4: # yield request,self,resource,priority 976 object._priority[self]=arg[0][3] 977 else: # yield request,self,resource 978 object._priority[self]=self.priority_default 979 if self.preemptable and self.n == 0: # No free resource 980 # test for preemption condition 981 preempt=object._priority[self] > self.activeQ[-1]._priority[self] 982 # If yes: 983 if preempt: 984 z=self.activeQ[-1] 985 # suspend lowest priority process being served 986 ##suspended = z 987 # record remaining service time 988 z._remainService = z._nextTime - _t 989 Process().cancel(z) 990 # remove from activeQ 991 self.activeQ.remove(z) 992 # put into front of waitQ 993 self.waitQ.insert(0,z) 994 # if self is monitored, update waitQ monitor 995 if self.monitored: 996 self.waitMon.observe(len(self.waitQ),now()) 997 # record that it has been preempted 998 z._preempted = 1 999 # passivate re-queued process 1000 z._nextTime=None 1001 # assign resource unit to preemptor 1002 self.activeQ.enter(object) 1003 # post event notice for preempting process 1004 _e._post(_Action(who=object,generator=object._nextpoint), 1005 at=_t,prior=1) 1006 else: 1007 self.waitQ.enter(object) 1008 # passivate queuing process 1009 object._nextTime=None 1010 else: # treat non-preemption case 1011 if self.n == 0: 1012 self.waitQ.enter(object) 1013 # passivate queuing process 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
1021 - def _release(self,arg):
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 #reactivate first waiting requestor if any; assign Resource to it 1028 if self.waitQ: 1029 object=self.waitQ.leave() 1030 self.n -= 1 #assign 1 resource unit to object 1031 self.activeQ.enter(object) 1032 # if resource preemptable: 1033 if self.preemptable: 1034 # if object had been preempted: 1035 if object._preempted: 1036 object._preempted = 0 1037 # reactivate object delay= remaining service time 1038 reactivate(object,delay=object._remainService) 1039 # else reactivate right away 1040 else: 1041 reactivate(object,delay=0,prior=1) 1042 # else: 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
1048 -class Buffer(Lister):
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 ## monitor for Producer processes' queue 1068 self.putQMon=monitorType(name="Producer Queue Monitor %s"%self.name, 1069 ylab="nr in queue",tlab="time") 1070 ## monitor for Consumer processes' queue 1071 self.getQMon=monitorType(name="Consumer Queue Monitor %s"%self.name, 1072 ylab="nr in queue",tlab="time") 1073 ## monitor for nr items in buffer 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
1093 -class Level(Buffer):
1094 """Models buffers for processes putting/getting un-distinguishable items. 1095 """
1096 - def getamount(self):
1097 return self.nrBuffered
1098
1099 - def gettheBuffer(self):
1100 return self.nrBuffered
1101 1102 theBuffer=property(gettheBuffer) 1103
1104 - def __init__(self,**pars):
1105 Buffer.__init__(self,**pars) 1106 if self.name is None: 1107 self.name="a_level" ## default name 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 ## nr items initially in buffer 1122 ## buffer is just a counter (int type) 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: # yield put,self,buff,whattoput,priority 1142 obj._putpriority[self]=arg[0][4] 1143 whatToPut=arg[0][3] 1144 elif len(arg[0]) == 4: # yield get,self,buff,whattoput 1145 obj._putpriority[self]=Buffer.priorityDefault #default 1146 whatToPut=arg[0][3] 1147 else: # yield get,self,buff 1148 obj._putpriority[self]=Buffer.priorityDefault #default 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 #passivate put requestor 1157 obj._whatToPut=whatToPutNr 1158 self.putQ.enterPut(obj) #and queue, with size of put 1159 else: 1160 self.nrBuffered+=whatToPutNr 1161 if self.monitored: 1162 self.bufferMon.observe(y=self.amount,t=now()) 1163 # service any getters waiting 1164 # service in queue-order; do not serve second in queue before first 1165 # has been served 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) # get requestor's record out of queue 1174 _e._post(_Action(who=proc,generator=proc._nextpoint), 1175 at=_t) # continue a blocked get requestor 1176 else: 1177 break 1178 _e._post(_Action(who=obj,generator=obj._nextpoint), 1179 at=_t,prior=1) # continue the put requestor
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: # yield get,self,buff,whattoget,priority 1186 obj._getpriority[self]=arg[0][4] 1187 nrToGet=arg[0][3] 1188 elif len(arg[0]) == 4: # yield get,self,buff,whattoget 1189 obj._getpriority[self]=Buffer.priorityDefault #default 1190 nrToGet=arg[0][3] 1191 else: # yield get,self,buff 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 # passivate queuing process 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 # reactivate any put requestors for which space is now available 1213 # service in queue-order; do not serve second in queue before first 1214 # has been served 1215 while len(self.putQ): #test for queued producers 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)#requestor's record out of queue 1222 _e._post(_Action(who=proc,generator=proc._nextpoint), 1223 at=_t) # continue a blocked put requestor 1224 else: 1225 break
1226
1227 -class Store(Buffer):
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 """
1234 - def getnrBuffered(self):
1235 return len(self.theBuffer)
1236 nrBuffered=property(getnrBuffered) 1237
1238 - def getbuffered(self):
1239 return self.theBuffer
1240 buffered=property(getbuffered) 1241
1242 - def __init__(self,**pars):
1243 Buffer.__init__(self,**pars) 1244 self.theBuffer=[] 1245 if self.name is None: 1246 self.name="a_store" ## default name 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##buffer==list of objects 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
1267 - def addSort(self,sortFunc):
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: # yield put,self,buff,whattoput,priority 1287 obj._putpriority[self]=arg[0][4] 1288 whatToPut=arg[0][3] 1289 elif len(arg[0]) == 4: # yield put,self,buff,whattoput 1290 obj._putpriority[self]=Buffer.priorityDefault #default 1291 whatToPut=arg[0][3] 1292 else: # error, whattoput missing 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 #passivate put requestor 1299 obj._whatToPut=whatToPut 1300 self.putQ.enterPut(obj) #and queue, with items to put 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) # continue the put requestor 1309 1310 # service any waiting getters 1311 # service in queue order: do not serve second in queue before first 1312 # has been served 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) #predicate parameter 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) # continue a blocked get requestor 1329 else: #numerical parameter 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 # take this get requestor's record out of queue: 1338 self.getQ.takeout(proc) 1339 _e._post(_Action(who=proc,generator=proc._nextpoint), 1340 at=_t) # continue a blocked get requestor 1341 else: 1342 # block subsequent numerically specified get's in getQ 1343 # to prevent starvation of larger gets by smaller ones 1344 block=True 1345 else: 1346 break # either out of items in buffer or out of getters in getQ
1347
1348 - def _get(self,arg):
1349 """Handles get requests""" 1350 filtfunc=None 1351 obj=arg[1].who 1352 obj.got=[] # the list of items retrieved by 'get' 1353 if len(arg[0]) == 5: # yield get,self,buff,whattoget,priority 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: # yield get,self,buff,whattoget 1360 obj._getpriority[self]=Buffer.priorityDefault #default 1361 if inspect.isfunction(arg[0][3]): 1362 filtfunc=arg[0][3] 1363 else: 1364 nrToGet=arg[0][3] 1365 else: # yield get,self,buff 1366 obj._getpriority[self]=Buffer.priorityDefault 1367 nrToGet=1 1368 if not filtfunc: #number specifies nr items to get 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 # passivate/block queuing 'get' process 1376 obj._nextTime=None 1377 else: 1378 for i in range(nrToGet): 1379 obj.got.append(self.theBuffer.pop(0)) # move items from 1380 # buffer to requesting process 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 # reactivate any put requestors for which space is now available 1386 # serve in queue order: do not serve second in queue before first 1387 # has been served 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) #move items to buffer 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) # dequeue requestor's record 1398 _e._post(_Action(who=proc,generator=proc._nextpoint), 1399 at=_t) # continue a blocked put requestor 1400 else: 1401 break 1402 else: # items to get determined by filtfunc 1403 movCand=filtfunc(self.theBuffer) 1404 if movCand: # get succeded 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 # reactivate any put requestors for which space is now available 1413 # serve in queue order: do not serve second in queue before first 1414 # has been served 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) #move items to buffer 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) # dequeue requestor's record 1425 _e._post(_Action(who=proc,generator=proc._nextpoint), 1426 at=_t) # continue a blocked put requestor 1427 else: 1428 break 1429 else: # get did not succeed, block 1430 obj._nrToGet=filtfunc 1431 self.getQ.enterGet(obj) 1432 # passivate/block queuing 'get' process 1433 obj._nextTime=None
1434 1435 1436 1437
1438 -class SimEvent(Lister):
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 """
1443 - def __init__(self,name="a_SimEvent"):
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 #reactivate all waiting processes 1465 for p in self.waits: 1466 p[0].eventsFired.append(self) 1467 reactivate(p[0],prior=True) 1468 #delete waits entries for this process in other events 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
1483 - def _wait(self,par):
1484 """Consumes a signal if it has occurred, otherwise process 'proc' 1485 waits for this event. 1486 """ 1487 proc=par[0][1] #the process issuing the yield waitevent command 1488 proc.eventsFired=[] 1489 if not self.occurred: 1490 self.waits.append([proc,[self]]) 1491 proc._nextTime=None #passivate calling process 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
1498 - def _waitOR(self,par):
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: #at least one event has fired; continue process 1511 _e._post(_Action(who=proc,generator=proc._nextpoint), 1512 at=_t,prior=1) 1513 1514 else: #no event in list has fired, enter process in all 'waits' lists 1515 proc.eventsFired=[] 1516 proc._nextTime=None #passivate calling process 1517 for ev in evlist: 1518 ev.waits.append([proc,evlist])
1519
1520 - def _queue(self,par):
1521 """Consumes a signal if it has occurred, otherwise process 'proc' 1522 queues for this event. 1523 """ 1524 proc=par[0][1] #the process issuing the yield queueevent command 1525 proc.eventsFired=[] 1526 if not self.occurred: 1527 self.queues.append([proc,[self]]) 1528 proc._nextTime=None #passivate calling process 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
1535 - def _queueOR(self,par):
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: #at least one event has fired; continue process 1548 _e._post(_Action(who=proc,generator=proc._nextpoint), 1549 at=_t,prior=1) 1550 1551 else: #no event in list has fired, enter process in all 'waits' lists 1552 proc.eventsFired=[] 1553 proc._nextTime=None #passivate calling process 1554 for ev in evlist: 1555 ev.queues.append([proc,evlist])
1556 1557 ## begin waituntil functionality
1558 -def _test():
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
1577 -def _waitUntilFunc(proc,cond):
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() #signal 'simulate' that a process is waiting 1588 # passivate calling process 1589 proc._nextTime=None 1590 else: 1591 #schedule continuation of calling process 1592 _e._post(_Action(who=proc,generator=proc._nextpoint), 1593 at=_t,prior=1)
1594 1595 1596 ##end waituntil functionality 1597
1598 -def scheduler(till=0):
1599 """Schedules Processes/semi-coroutines until time 'till'. 1600 Deprecated since version 0.5. 1601 """ 1602 simulate(until=till) 1603
1604 -def holdfunc(a):
1605 a[0][1]._hold(a)
1606
1607 -def requestfunc(a):
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 ## Compound yield request statement 1613 ## first tuple in ((request,self,res),(xx,self,yy)) 1614 b=a[0][0] 1615 ## b[2]==res (the resource requested) 1616 ##process the first part of the compound yield statement 1617 ##a[1] is the _Action instance (a[1].who==process owner, a[1].process==generator) 1618 b[2]._request(arg=(b,a[1])) 1619 ##deal with add-on condition to command 1620 ##Trigger processes for reneging 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 #activate it 1637 proc=a[0][0][1] # the process to be woken up 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 ## the timeout delay 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 ## the event 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 ## Simple yield request command 1657 a[0][2]._request(a) 1658
1659 -def releasefunc(a):
1660 a[0][2]._release(a)
1661
1662 -def passivatefunc(a):
1663 a[0][1]._passivate(a)
1664
1665 -def waitevfunc(a):
1666 #if waiting for one event only (not a tuple or list) 1667 evtpar=a[0][2] 1668 if isinstance(evtpar,SimEvent): 1669 a[0][2]._wait(a) 1670 # else, if waiting for an OR of events (list/tuple): 1671 else: #it should be a list/tuple of events 1672 # call _waitOR for first event 1673 evtpar[0]._waitOR(a)
1674
1675 -def queueevfunc(a):
1676 #if queueing for one event only (not a tuple or list) 1677 evtpar=a[0][2] 1678 if isinstance(evtpar,SimEvent): 1679 a[0][2]._queue(a) 1680 #else, if queueing for an OR of events (list/tuple): 1681 else: #it should be a list/tuple of events 1682 # call _queueOR for first event 1683 evtpar[0]._queueOR(a)
1684
1685 -def waituntilfunc(par):
1686 _waitUntilFunc(par[0][1],par[0][2])
1687
1688 -def getfunc(a):
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 ## Compound yield request statement 1695 ## first tuple in ((request,self,res),(xx,self,yy)) 1696 b=a[0][0] 1697 ## b[2]==res (the resource requested) 1698 ##process the first part of the compound yield statement 1699 ##a[1] is the _Action instance (a[1].who==process owner, a[1].process==generator) 1700 b[2]._get(arg=(b,a[1])) 1701 ##deal with add-on condition to command 1702 ##Trigger processes for reneging 1703 class _Holder(Process): 1704 """Provides timeout process""" 1705 def trigger(self,delay): 1706 yield hold,self,delay 1707 #if not proc in b[2].activeQ: 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 #activate it 1720 proc=a[0][0][1] # the process to be woken up 1721 actCode=a[0][1][0] 1722 if actCode==hold: 1723 proc._holder=_Holder("RENEGE-hold for %s"%proc.name) 1724 ## the timeout delay 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 ## the event 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 ## Simple yield request command 1738 a[0][2]._get(a) 1739 1740
1741 -def putfunc(a):
1742 """Handles 'yield put' (simple and compound hold/waitevent) 1743 """ 1744 if type(a[0][0])==tuple: 1745 ## Compound yield request statement 1746 ## first tuple in ((request,self,res),(xx,self,yy)) 1747 b=a[0][0] 1748 ## b[2]==res (the resource requested) 1749 ##process the first part of the compound yield statement 1750 ##a[1] is the _Action instance (a[1].who==process owner, a[1].process==generator) 1751 b[2]._put(arg=(b,a[1])) 1752 ##deal with add-on condition to command 1753 ##Trigger processes for reneging 1754 class _Holder(Process): 1755 """Provides timeout process""" 1756 def trigger(self,delay): 1757 yield hold,self,delay 1758 #if not proc in b[2].activeQ: 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 #activate it 1771 proc=a[0][0][1] # the process to be woken up 1772 actCode=a[0][1][0] 1773 if actCode==hold: 1774 proc._holder=_Holder("RENEGE-hold for %s"%proc.name) 1775 ## the timeout delay 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 ## the event 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 ## Simple yield request command 1789 a[0][2]._put(a) 1790
1791 -def simulate(until=0):
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 ## 'a' is tuple "(<yield command>, <action>)" 1876 if type(a[0][0])==tuple: 1877 ##allowing for yield (request,self,res),(waituntil,self,cond) 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,): #not at endtime! 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
1903 -class Trace(Lister):
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):
1913 1914 Trace.commandsproc={hold:Trace.thold,passivate:Trace.tpassivate, 1915 request:Trace.trequest,release:Trace.trelease, 1916 waitevent:Trace.twaitevent, 1917 queueevent:Trace.tqueueevent, 1918 waituntil:Trace.twaituntil, 1919 get:Trace.tget,put:Trace.tput} 1920 self.start=start 1921 self.end=end 1922 self.toTrace=toTrace 1923 self.tracego=True 1924 self.outfile=outfile 1925 self._comment=None
1926
1927 - def treset(self):
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
1943 - def tchange(self,**kmvar):
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
1954 - def tstart(self):
1955 self.tracego=True
1956
1957 - def tstop(self):
1958 self.tracego=False
1959
1960 - def ifTrace(self,cond):
1961 if self.tracego and (self.start <= now() <= self.end) and cond: 1962 return True
1963
1964 - def thold(self,par):
1965 try: 1966 return "delay: %s"%par[0][2] 1967 except: 1968 return 0
1969 thold=classmethod(thold) 1970
1971 - def trequest(self,par):
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
1982 - def trelease(self,par):
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
1989 - def tpassivate(self,par):
1990 return ""
1991 tpassivate=classmethod(tpassivate) 1992
1993 - def tactivate(self,par):
1994 pass
1995 tactivate=classmethod(tactivate) 1996
1997 - def twaitevent(self,par):
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
2006 - def tqueueevent(self,par):
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
2015 - def tsignal(self,evt):
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
2023 - def twaituntil(self,par):
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
2073 - def recordEvent(self,command,whole):
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 ##print >>self.outfile, "[WHOLE]",whole,"\n[END WHOLE]" 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
2101 - def recordInterrupt(self,who,victim):
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
2108 - def recordCancel(self,who,victim):
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
2115 - def recordActivate(self,who,when,prior):
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
2123 - def recordReactivate(self,who,when,prior):
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
2131 - def recordSignal(self,evt):
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
2138 - def tterminated(self,who):
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
2148 -class Histogram(list):
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 #self[:] = [[1,2],[3,4]] 2159 self[:] =[[low+(i-1)*self.binsize,0] for i in range(self.nbins+2)]
2160 #print '__init__ :', self[0],self 2161
2162 - def addIn(self,y):
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 ############# Test/demo functions #############
2175 - def test_demo():
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
2260 - def test_interrupt():
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) # breakdowns only during operation 2277 yield hold,self,repairduration 2278 print now()," repaired" 2279 else: 2280 break # no breakdown, ergo bus arrived 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
2303 - def testSimEvents():
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
2348 - def testwaituntil():
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 #rest 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 ## TEST COMPOUND "YIELD REQUEST" COMMANDS 2400 ## ------------------------------------------------------------- 2401 2402 ## ------------------------------------------------------------- 2403 ## TEST "yield (request,self,res),(hold,self,delay)" 2404 ## == timeout renege 2405 ## ------------------------------------------------------------- 2406
2407 - class JobTO(Process):
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
2425 - def testNoTimeout():
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
2443 - def testTimeout1():
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
2461 - def testTimeout2():
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 ## TEST "yield (request,self,res),(waitevent,self,event)" 2481 ## == event renege 2482 ## ------------------------------------------------------------------
2483 - class JobEvt(Process):
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
2491 - def execute(self,event,usetime):
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
2500 - class JobEvtMulti(Process):
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
2517 - class FireEvent(Process):
2518 """Fires reneging event 2519 """
2520 - def fire(self,fireDelay,event):
2521 yield hold,self,fireDelay 2522 event.signal()
2523
2524 - def testNoEvent():
2525 """Test that processes acquire resource normally if no event fires 2526 """ 2527 res=Resource(name="Server",capacity=1) 2528 event=SimEvent("Renege_trigger") #never gets fired 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 # Both jobs should get server (in sequence) 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
2543 - def testWaitEvent1():
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 # Job_1 should get server, Job_2 renege 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 - def testWaitEvent2():
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 #for both events 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 # Job_1 should get server, Job_2 should renege 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