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

Source Code for Module SimPy.Simulation

   1  #!/usr / bin / env python 
   2  # coding=utf-8 
   3  # $Revision: 484 $ $Date: 2008-09-10 17:25:13 +0200 (Mi, 10 Sep 2008)  
   4  """Simulation 2.1 Implements SimPy Processes, Resources, Buffers, and the backbone simulation 
   5  scheduling by coroutine calls. Provides data collection through classes  
   6  Monitor and Tally. 
   7  Based on generators (Python 2.3 and later; not 3.0) 
   8   
   9  LICENSE: 
  10  Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010  Klaus G. Muller, Tony Vignaux 
  11  mailto: kgmuller at xs4all.nl and Tony.Vignaux at vuw.ac.nz 
  12   
  13      This library is free software; you can redistribute it and / or 
  14      modify it under the terms of the GNU Lesser General Public 
  15      License as published by the Free Software Foundation; either 
  16      version 2.1 of the License, or (at your option) any later version. 
  17   
  18      This library is distributed in the hope that it will be useful, 
  19      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  20      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  21      Lesser General Public License for more details. 
  22   
  23      You should have received a copy of the GNU Lesser General Public 
  24      License along with this library; if not, write to the Free Software 
  25      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111 - 1307  USA 
  26  END OF LICENSE 
  27      
  28  """ 
  29   
  30  import random 
  31  import sys 
  32  import types 
  33  from heapq import heappush, heappop 
  34   
  35  from SimPy.Lister import Lister 
  36  from SimPy.Recording import Monitor, Tally 
  37  from SimPy.Lib import Process, SimEvent, PriorityQ, Resource, Level, \ 
  38                        Store, Simerror, FatalSimerror 
  39   
  40  # Required for backward compatibility 
  41  import SimPy.Globals as Globals 
  42  from SimPy.Globals import initialize, simulate, now, stopSimulation, \ 
  43          allEventNotices, allEventTimes, startCollection,\ 
  44          _startWUStepping, _stopWUStepping, activate, reactivate 
  45   
  46   
  47  __TESTING = False 
  48  version = __version__ = '2.1 $Revision: 484 $ $Date: 2010-05-10 09:18:06 +0200 (Mon, 10 May 2010) $' 
  49  if __TESTING:  
  50      print 'SimPy.Simulation %s' %__version__, 
  51      if __debug__: 
  52          print '__debug__ on' 
  53      else: 
  54          print 
  55   
  56  # yield keywords 
  57  hold = 1 
  58  passivate = 2 
  59  request = 3 
  60  release = 4 
  61  waitevent = 5 
  62  queueevent = 6 
  63  waituntil = 7 
  64  get = 8 
  65  put = 9 
  66   
  67   
68 -class Infinity(object):
69 - def __cmp__(self, other):
70 return 1
71 72 infinity = Infinity() 73
74 -def holdfunc(a):
75 a[0][1]._hold(a)
76
77 -def requestfunc(a):
78 """Handles 'yield request, self, res' and 79 'yield (request, self, res),(<code>,self, par)'. 80 <code > can be 'hold' or 'waitevent'. 81 """ 82 if type(a[0][0]) == tuple: 83 ## Compound yield request statement 84 ## first tuple in ((request, self, res),(xx, self, yy)) 85 b = a[0][0] 86 ## b[2] == res (the resource requested) 87 ##process the first part of the compound yield statement 88 ##a[1] is the Process instance 89 b[2]._request(arg = (b, a[1])) 90 ##deal with add - on condition to command 91 ##Trigger processes for reneging 92 class _Holder(Process): 93 """Provides timeout process""" 94 def __init__(self,name,sim=None): 95 Process.__init__(self,name=name,sim=sim)
96 def trigger(self, delay): 97 yield hold, self, delay 98 if not proc in b[2].activeQ: 99 proc.sim.reactivate(proc) 100 101 class _EventWait(Process): 102 """Provides event waiting process""" 103 def __init__(self,name,sim=None): 104 Process.__init__(self,name=name,sim=sim) 105 def trigger(self, event): 106 yield waitevent, self, event 107 if not proc in b[2].activeQ: 108 proc.eventsFired = self.eventsFired 109 proc.sim.reactivate(proc) 110 111 #activate it 112 proc = a[0][0][1] # the process to be woken up 113 actCode = a[0][1][0] 114 if actCode == hold: 115 proc._holder = _Holder(name = 'RENEGE - hold for %s'%proc.name, 116 sim=proc.sim) 117 ## the timeout delay 118 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 119 elif actCode == waituntil: 120 raise FatalSimerror('Illegal code for reneging: waituntil') 121 elif actCode == waitevent: 122 proc._holder = _EventWait(name = 'RENEGE - waitevent for %s'\ 123 %proc.name,sim=proc.sim) 124 ## the event 125 proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 126 elif actCode == queueevent: 127 raise FatalSimerror('Illegal code for reneging: queueevent') 128 else: 129 raise FatalSimerror('Illegal code for reneging %s'%actCode) 130 else: 131 ## Simple yield request command 132 a[0][2]._request(a) 133
134 -def releasefunc(a):
135 a[0][2]._release(a)
136
137 -def passivatefunc(a):
138 a[0][1]._passivate(a)
139
140 -def waitevfunc(a):
141 #if waiting for one event only (not a tuple or list) 142 evtpar = a[0][2] 143 if isinstance(evtpar, SimEvent): 144 a[0][2]._wait(a) 145 # else, if waiting for an OR of events (list / tuple): 146 else: #it should be a list / tuple of events 147 # call _waitOR for first event 148 evtpar[0]._waitOR(a)
149
150 -def queueevfunc(a):
151 #if queueing for one event only (not a tuple or list) 152 evtpar = a[0][2] 153 if isinstance(evtpar, SimEvent): 154 a[0][2]._queue(a) 155 #else, if queueing for an OR of events (list / tuple): 156 else: #it should be a list / tuple of events 157 # call _queueOR for first event 158 evtpar[0]._queueOR(a)
159
160 -def waituntilfunc(par):
161 par[0][1].sim._waitUntilFunc(par[0][1], par[0][2])
162
163 -def getfunc(a):
164 """Handles 'yield get, self, buffer, what, priority' and 165 'yield (get, self, buffer, what, priority),(<code>,self, par)'. 166 <code > can be 'hold' or 'waitevent'. 167 """ 168 if type(a[0][0]) == tuple: 169 ## Compound yield request statement 170 ## first tuple in ((request, self, res),(xx, self, yy)) 171 b = a[0][0] 172 ## b[2] == res (the resource requested) 173 ##process the first part of the compound yield statement 174 ##a[1] is the Process instance 175 b[2]._get(arg = (b, a[1])) 176 ##deal with add - on condition to command 177 ##Trigger processes for reneging 178 class _Holder(Process): 179 """Provides timeout process""" 180 def __init__(self,**par): 181 Process.__init__(self,**par)
182 def trigger(self, delay): 183 yield hold, self, delay 184 #if not proc in b[2].activeQ: 185 if proc in b[2].getQ: 186 a[1].sim.reactivate(proc) 187 188 class _EventWait(Process): 189 """Provides event waiting process""" 190 def __init__(self,**par): 191 Process.__init__(self,**par) 192 def trigger(self, event): 193 yield waitevent, self, event 194 if proc in b[2].getQ: 195 a[1].eventsFired = self.eventsFired 196 a[1].sim.reactivate(proc) 197 198 #activate it 199 proc = a[0][0][1] # the process to be woken up 200 actCode = a[0][1][0] 201 if actCode == hold: 202 proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name, 203 sim=proc.sim) 204 ## the timeout delay 205 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 206 elif actCode == waituntil: 207 raise FatalSimerror('waituntil: Illegal code for reneging: waituntil') 208 elif actCode == waitevent: 209 proc._holder = _EventWait(name="RENEGE - waitevent for%s"\ 210 %proc.name,sim=proc.sim) 211 ## the event 212 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 213 elif actCode == queueevent: 214 raise FatalSimerror('Illegal code for reneging: queueevent') 215 else: 216 raise FatalSimerror('Illegal code for reneging %s'%actCode) 217 else: 218 ## Simple yield request command 219 a[0][2]._get(a) 220 221
222 -def putfunc(a):
223 """Handles 'yield put' (simple and compound hold / waitevent) 224 """ 225 if type(a[0][0]) == tuple: 226 ## Compound yield request statement 227 ## first tuple in ((request, self, res),(xx, self, yy)) 228 b = a[0][0] 229 ## b[2] == res (the resource requested) 230 ##process the first part of the compound yield statement 231 ##a[1] is the Process instance 232 b[2]._put(arg = (b, a[1])) 233 ##deal with add - on condition to command 234 ##Trigger processes for reneging 235 class _Holder(Process): 236 """Provides timeout process""" 237 def __init__(self,**par): 238 Process.__init__(self,**par)
239 def trigger(self, delay): 240 yield hold, self, delay 241 #if not proc in b[2].activeQ: 242 if proc in b[2].putQ: 243 a[1].sim.reactivate(proc) 244 245 class _EventWait(Process): 246 """Provides event waiting process""" 247 def __init__(self,**par): 248 Process.__init__(self,**par) 249 def trigger(self, event): 250 yield waitevent, self, event 251 if proc in b[2].putQ: 252 a[1].eventsFired = self.eventsFired 253 a[1].sim.reactivate(proc) 254 255 #activate it 256 proc = a[0][0][1] # the process to be woken up 257 actCode = a[0][1][0] 258 if actCode == hold: 259 proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name, 260 sim=proc.sim) 261 ## the timeout delay 262 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 263 elif actCode == waituntil: 264 raise FatalSimerror('Illegal code for reneging: waituntil') 265 elif actCode == waitevent: 266 proc._holder = _EventWait(name='RENEGE - waitevent for %s'\ 267 %proc.name,sim=proc.sim) 268 ## the event 269 a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2])) 270 elif actCode == queueevent: 271 raise FatalSimerror('Illegal code for reneging: queueevent') 272 else: 273 raise FatalSimerror('Illegal code for reneging %s'%actCode) 274 else: 275 ## Simple yield request command 276 a[0][2]._put(a) 277 278
279 -class Simulation(object):
280 _dispatch = { 281 hold: holdfunc, request: requestfunc, release: releasefunc, 282 passivate: passivatefunc, waitevent: waitevfunc, 283 queueevent: queueevfunc, waituntil: waituntilfunc, get: getfunc, 284 put: putfunc, 285 } 286 _commandcodes = _dispatch.keys() 287 _commandwords = { 288 hold: 'hold', request: 'request', release: 'release', 289 passivate: 'passivate', waitevent: 'waitevent', 290 queueevent: 'queueevent', waituntil: 'waituntil', get: 'get', 291 put: 'put' 292 } 293
294 - def __init__(self):
295 self.initialize()
296
297 - def initialize(self):
298 self._t = 0 299 self.next_time = 0 300 301 # Eventqueue stuff. 302 self._timestamps = [] 303 self._sortpr = 0 304 305 self._start = False 306 self._stop = False 307 self.condQ = [] 308 self.allMonitors = [] 309 self.allTallies = []
310
311 - def now(self):
312 return self._t
313
314 - def stopSimulation(self):
315 """Application function to stop simulation run""" 316 self._stop = True
317
318 - def _post(self, what, at, prior = False):
319 """Post an event notice for process what for time at""" 320 # event notices are Process instances 321 if at < self._t: 322 raise FatalSimerror('Attempt to schedule event in the past') 323 what._nextTime = at 324 self._sortpr -= 1 325 if prior: 326 # before all other event notices at this time 327 # heappush with highest priority value so far (negative of 328 # monotonely decreasing number) 329 # store event notice in process instance 330 what._rec = [at, self._sortpr, what, False] 331 # make event list refer to it 332 heappush(self._timestamps, what._rec) 333 else: 334 # heappush with lowest priority 335 # store event notice in process instance 336 what._rec = [at,-self._sortpr, what, False] 337 # make event list refer to it 338 heappush(self._timestamps, what._rec)
339
340 - def _unpost(self, whom):
341 """ 342 Mark event notice for whom as cancelled if whom is a suspended process 343 """ 344 if whom._nextTime is not None: # check if whom was actually active 345 whom._rec[3] = True ## Mark as cancelled 346 whom._nextTime = None
347
348 - def allEventNotices(self):
349 """Returns string with eventlist as; 350 t1: processname, processname2 351 t2: processname4, processname5, . . . 352 . . . . 353 """ 354 ret = '' 355 tempList = [] 356 tempList[:] = self._timestamps 357 tempList.sort() 358 # return only event notices which are not cancelled 359 tempList = [[x[0],x[2].name] for x in tempList if not x[3]] 360 tprev = -1 361 for t in tempList: 362 # if new time, new line 363 if t[0] == tprev: 364 # continue line 365 ret += ', %s'%t[1] 366 else: 367 # new time 368 if tprev == -1: 369 ret = '%s: %s' % (t[0],t[1]) 370 else: 371 ret += '\n%s: %s' % (t[0],t[1]) 372 tprev = t[0] 373 return ret + '\n'
374
375 - def allEventTimes(self):
376 """Returns list of all times for which events are scheduled. 377 """ 378 r = [] 379 r[:] = self._timestamps 380 r.sort() 381 # return only event times of not cancelled event notices 382 r1 = [x[0] for x in r if not x[3]] 383 tprev = -1 384 ret = [] 385 for t in r1: 386 if t == tprev: 387 #skip time, already in list 388 pass 389 else: 390 ret.append(t) 391 tprev = t 392 return ret
393
394 - def activate(self, obj, process, at = 'undefined', delay = 'undefined', 395 prior = False):
396 """Application function to activate passive process.""" 397 if __debug__: 398 if not (obj.sim == self): 399 txt="activate: Process %s not in activating Simulation instance"\ 400 %obj.name 401 raise FatalSimerror(txt) 402 if not (type(process) == types.GeneratorType): 403 raise FatalSimerror('Activating function which'+ 404 ' is not a generator (contains no \'yield\')') 405 if not obj._terminated and not obj._nextTime: 406 #store generator reference in object; needed for reactivation 407 obj._nextpoint = process 408 if at == 'undefined': 409 at = self._t 410 if delay == 'undefined': 411 zeit = max(self._t, at) 412 else: 413 zeit = max(self._t, self._t + delay) 414 self._post(obj, at = zeit, prior = prior)
415
416 - def reactivate(self, obj, at = 'undefined', delay = 'undefined', 417 prior = False):
418 """Application function to reactivate a process which is active, 419 suspended or passive.""" 420 # Object may be active, suspended or passive 421 if not obj._terminated: 422 a = Process('SimPysystem',sim=self) 423 a.cancel(obj) 424 # object now passive 425 if at == 'undefined': 426 at = self._t 427 if delay == 'undefined': 428 zeit = max(self._t, at) 429 else: 430 zeit = max(self._t, self._t + delay) 431 self._post(obj, at = zeit, prior = prior)
432
433 - def startCollection(self, when = 0.0, monitors = None, tallies = None):
434 """Starts data collection of all designated Monitor and Tally objects 435 (default = all) at time 'when'. 436 """ 437 class Starter(Process): 438 def collect(self, monitors, tallies): 439 for m in monitors: 440 m.reset() 441 for t in tallies: 442 t.reset() 443 yield hold, self
444 if monitors is None: 445 monitors = self.allMonitors 446 if tallies is None: 447 tallies = self.allTallies 448 if when == 0.0: 449 for m in monitors: 450 try: 451 ylast = m[-1][1] 452 empty = False 453 except IndexError: 454 empty = True 455 m.reset() 456 if not empty: 457 m.observe(t = now(), y = ylast) 458 for t in tallies: 459 t.reset() 460 else: 461 s = Starter(sim = self) 462 self.activate(s, s.collect(monitors = monitors, tallies = tallies),\ 463 at = when, prior = True) 464 465
466 - def _waitUntilFunc(self, proc, cond):
467 """ 468 Puts a process 'proc' waiting for a condition into a waiting queue. 469 'cond' is a predicate function which returns True if the condition is 470 satisfied. 471 """ 472 if not cond(): 473 self.condQ.append(proc) 474 proc.cond = cond 475 # passivate calling process 476 proc._nextTime = None 477 else: 478 #schedule continuation of calling process 479 self._post(proc, at = self._t, prior = 1)
480
481 - def _terminate(self, process):
482 """Marks a process as terminated.""" 483 process._nextpoint = None 484 process._terminated = True 485 process._nextTime = None
486
487 - def has_events(self):
488 """ 489 Checks if there are events which can be processed. Returns ``True`` if 490 there are events and the simulation has not been stopped. 491 """ 492 return not self._stop and self._timestamps
493
494 - def peek(self):
495 """ 496 Returns the time of the next event or infinity, if no 497 more events are scheduled. 498 """ 499 if not self._timestamps: 500 return infinity 501 else: 502 return self._timestamps[0][0]
503
504 - def step(self):
505 """ 506 Executes the next uncancelled event in the eventqueue. 507 """ 508 509 # Fetch next process and advance its process execution method. 510 noActiveNotice = True 511 # Get an uncancelled event 512 while noActiveNotice: 513 if self._timestamps: 514 _tnotice, p, proc, cancelled = heappop(self._timestamps) 515 noActiveNotice = cancelled 516 else: 517 return None 518 519 # Advance simulation time. 520 proc._rec = None 521 self._t = _tnotice 522 523 # Execute the event. This will advance the process execution method. 524 try: 525 resultTuple = proc._nextpoint.next() 526 527 # Process the command function which has been yielded by the 528 # process. 529 if type(resultTuple[0]) == tuple: 530 # allowing for reneges, e.g.: 531 # >>> yield (request, self, res),(waituntil, self, cond) 532 command = resultTuple[0][0] 533 else: 534 command = resultTuple[0] 535 if __debug__: 536 if not command in self._commandcodes: 537 raise FatalSimerror('Illegal command: yield %s'%command) 538 self._dispatch[command]((resultTuple, proc)) 539 except StopIteration: 540 # Process execution method has terminated. 541 self._terminate(proc) 542 543 # Test the conditions for all waiting processes if there are any at 544 # all. Where condition are satisfied, reactivate that process 545 # immediately and remove it from queue. 546 # Always test the wait conditions. They might be triggered by on a 547 # terminating process execution method (e.g. the above next() call 548 # raises the StopIteration exception) 549 if self.condQ: 550 i = 0 551 while i < len(self.condQ): 552 proc = self.condQ[i] 553 if proc.cond(): 554 self.condQ.pop(i) 555 self.reactivate(proc) 556 else: 557 i += 1 558 559 # Return time of the next scheduled event. 560 #return self._timestamps[0][0] if self._timestamps else None 561 if self._timestamps: 562 return self._timestamps[0][0] 563 else: 564 return None
565
566 - def simulate(self, until=0):
567 """ 568 Start the simulation and run its loop until the timeout ``until`` is 569 reached, stopSimulation is called, or no more events are scheduled. 570 """ 571 try: 572 if not self._timestamps: 573 return 'SimPy: No activities scheduled' 574 575 # Some speedups. Storing these values in local variables prevents 576 # the self-lookup. Note that this can't be done for _stop because 577 # this variable will get overwritten, bools are immutable. 578 step = self.step 579 timestamps = self._timestamps 580 while not self._stop and timestamps and timestamps[0][0] <= until: 581 step() 582 583 if not self._stop and timestamps: 584 # Timestamps left, simulation not stopped 585 self._t = until 586 return 'SimPy: Normal exit at time %s' % self._t 587 elif not timestamps: 588 # No more timestamps 589 return 'SimPy: No more events at time %s' % self._t 590 else: 591 # Stopped by call of stopSimulation 592 return 'SimPy: Run stopped at time %s' % self._t 593 except FatalSimerror, error: 594 print 'SimPy: ' + error.value 595 raise FatalSimerror, 'SimPy: ' + error.value 596 except Simerror, error: 597 return 'SimPy: ' + error.value 598 finally: 599 self._stop = True
600 601 # For backward compatibility 602 Globals.sim = Simulation() 603 604 peek = Globals.sim.peek 605 606 step = Globals.sim.step 607 # End backward compatibility 608 609 if __name__ == '__main__': 610 print 'SimPy.Simulation %s' %__version__ 611 ############# Test / demo functions #############
612 - def test_demo():
613 class Aa(Process): 614 sequIn = [] 615 sequOut = [] 616 def __init__(self, holdtime, name,sim=None): 617 Process.__init__(self, name,sim=sim) 618 self.holdtime = holdtime
619 620 def life(self, priority): 621 for i in range(1): 622 Aa.sequIn.append(self.name) 623 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 624 len(rrr.activeQ) 625 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ] 626 print 'activeQ: ',[(k.name, k._priority[rrr]) \ 627 for k in rrr.activeQ] 628 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 629 'Inconsistent resource unit numbers' 630 print self.sim.now(),self.name, 'requests 1 ', rrr.unitName 631 yield request, self, rrr, priority 632 print self.sim.now(),self.name, 'has 1 ', rrr.unitName 633 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 634 len(rrr.activeQ) 635 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 636 len(rrr.activeQ) 637 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 638 'Inconsistent resource unit numbers' 639 yield hold, self, self.holdtime 640 print self.sim.now(),self.name, 'gives up 1', rrr.unitName 641 yield release, self, rrr 642 Aa.sequOut.append(self.name) 643 print self.sim.now(),self.name, 'has released 1 ', rrr.unitName 644 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ] 645 print self.sim.now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 646 len(rrr.activeQ) 647 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 648 'Inconsistent resource unit numbers' 649 650 class Observer(Process): 651 def __init__(self,**vars): 652 Process.__init__(self,**vars) 653 654 def observe(self, step, processes, res): 655 while self.sim.now() < 11: 656 for i in processes: 657 print '++ %s process: %s: active:%s, passive:%s, terminated: %s, interrupted:%s, queuing:%s'\ 658 %(self.sim.now(),i.name, i.active(),i.passive(),\ 659 i.terminated(),i.interrupted(),i.queuing(res)) 660 print 661 yield hold, self, step 662 663 print'\n+++test_demo output' 664 print '****First case == priority queue, resource service not preemptable' 665 s=Simulation() 666 s.initialize() 667 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ, 668 preemptable = 0,sim=s) 669 procs = [] 670 for i in range(10): 671 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s) 672 procs.append(z) 673 s.activate(z, z.life(priority = i)) 674 o = Observer(sim=s) 675 s.activate(o, o.observe(1, procs, rrr)) 676 a = s.simulate(until = 10000) 677 print a 678 print 'Input sequence: ', Aa.sequIn 679 print 'Output sequence: ', Aa.sequOut 680 681 print '\n****Second case == priority queue, resource service preemptable' 682 s=Simulation() 683 s.initialize() 684 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ, 685 preemptable = 1,sim=s) 686 procs = [] 687 for i in range(10): 688 z = Aa(holdtime = i, name = 'Car ' + str(i),sim=s) 689 procs.append(z) 690 s.activate(z, z.life(priority = i)) 691 o = Observer(sim=s) 692 s.activate(o, o.observe(1, procs, rrr)) 693 Aa.sequIn = [] 694 Aa.sequOut = [] 695 a = s.simulate(until = 10000) 696 print a 697 print 'Input sequence: ', Aa.sequIn 698 print 'Output sequence: ', Aa.sequOut 699
700 - def test_interrupt():
701 class Bus(Process): 702 def __init__(self, **vars): 703 Process.__init__(self, **vars)
704 705 def operate(self, repairduration = 0): 706 print self.sim.now(),'>> %s starts' % (self.name) 707 tripleft = 1000 708 while tripleft > 0: 709 yield hold, self, tripleft 710 if self.interrupted(): 711 print 'interrupted by %s' %self.interruptCause.name 712 print '%s: %s breaks down ' %(now(),self.name) 713 tripleft = self.interruptLeft 714 self.interruptReset() 715 print 'tripleft ', tripleft 716 s.reactivate(br, delay = repairduration) # breakdowns only during operation 717 yield hold, self, repairduration 718 print self.sim.now(),' repaired' 719 else: 720 break # no breakdown, ergo bus arrived 721 print self.sim.now(),'<< %s done' % (self.name) 722 723 class Breakdown(Process): 724 def __init__(self, myBus,sim=None): 725 Process.__init__(self, name = 'Breakdown ' + myBus.name,sim=sim) 726 self.bus = myBus 727 728 def breakBus(self, interval): 729 730 while True: 731 yield hold, self, interval 732 if self.bus.terminated(): break 733 self.interrupt(self.bus) 734 735 print'\n\n+++test_interrupt' 736 s=Simulation() 737 s.initialize() 738 b = Bus(name='Bus 1',sim=s) 739 s.activate(b, b.operate(repairduration = 20)) 740 br = Breakdown(b,sim=s) 741 s.activate(br, br.breakBus(200)) 742 print s.simulate(until = 4000) 743
744 - def testSimEvents():
745 class Waiter(Process): 746 def __init__(self,**vars): 747 Process.__init__(self,**vars)
748 def waiting(self, theSignal): 749 while True: 750 yield waitevent, self, theSignal 751 print '%s: process \'%s\' continued after waiting for %s' %\ 752 (self.sim.now(),self.name, theSignal.name) 753 yield queueevent, self, theSignal 754 print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name) 755 756 class ORWaiter(Process): 757 def __init__(self,**vars): 758 Process.__init__(self,**vars) 759 def waiting(self, signals): 760 while True: 761 yield waitevent, self, signals 762 print self.sim.now(),'one of %s signals occurred' %\ 763 [x.name for x in signals] 764 print '\t%s (fired / param)'%\ 765 [(x.name, x.signalparam) for x in self.eventsFired] 766 yield hold, self, 1 767 768 class Caller(Process): 769 def __init__(self,**vars): 770 Process.__init__(self,**vars) 771 def calling(self): 772 while True: 773 signal1.signal('wake up!') 774 print '%s: signal 1 has occurred'%now() 775 yield hold, self, 10 776 signal2.signal('and again') 777 signal2.signal('sig 2 again') 778 print '%s: signal1, signal2 have occurred'%now() 779 yield hold, self, 10 780 print'\n+++testSimEvents output' 781 s=Simulation() 782 s.initialize() 783 signal1 = SimEvent('signal 1',sim=s) 784 signal2 = SimEvent('signal 2',sim=s) 785 signal1.signal('startup1') 786 signal2.signal('startup2') 787 w1 = Waiter(name='waiting for signal 1',sim=s) 788 s.activate(w1, w1.waiting(signal1)) 789 w2 = Waiter(name='waiting for signal 2',sim=s) 790 s.activate(w2, w2.waiting(signal2)) 791 w3 = Waiter(name='also waiting for signal 2',sim=s) 792 s.activate(w3, w3.waiting(signal2)) 793 w4 = ORWaiter(name='waiting for either signal 1 or signal 2',sim=s) 794 s.activate(w4, w4.waiting([signal1, signal2]),prior = True) 795 c = Caller(name='Caller',sim=s) 796 s.activate(c, c.calling()) 797 print s.simulate(until = 100) 798
799 - def testwaituntil():
800 """ 801 Demo of waitUntil capability. 802 803 Scenario: 804 Three workers require sets of tools to do their jobs. Tools are shared, 805 scarce resources for which they compete. 806 """ 807 class Worker(Process): 808 def __init__(self, name, heNeeds = [],sim=None): 809 Process.__init__(self, name,sim=sim) 810 self.heNeeds = heNeeds
811 def work(self): 812 def workerNeeds(): 813 for item in self.heNeeds: 814 if item.n == 0: 815 return False 816 return True 817 818 while self.sim.now() < 8 * 60: 819 yield waituntil, self, workerNeeds 820 for item in self.heNeeds: 821 yield request, self, item 822 print '%s %s has %s and starts job' % (self.sim.now(),self.name, 823 [x.name for x in self.heNeeds]) 824 yield hold, self, random.uniform(10, 30) 825 for item in self.heNeeds: 826 yield release, self, item 827 yield hold, self, 2 #rest 828 829 print '\n+++ nwaituntil demo output' 830 random.seed(12345) 831 s=Simulation() 832 s.initialize() 833 brush = Resource(capacity = 1, name = 'brush',sim=s) 834 ladder = Resource(capacity = 2, name = 'ladder',sim=s) 835 hammer = Resource(capacity = 1, name = 'hammer',sim=s) 836 saw = Resource(capacity = 1, name = 'saw',sim=s) 837 painter = Worker('painter',[brush, ladder],sim=s) 838 s.activate(painter, painter.work()) 839 roofer = Worker('roofer',[hammer, ladder, ladder],sim=s) 840 s.activate(roofer, roofer.work()) 841 treeguy = Worker('treeguy',[saw, ladder],sim=s) 842 s.activate(treeguy, treeguy.work()) 843 for who in (painter, roofer, treeguy): 844 print '%s needs %s for his job' %\ 845 (who.name,[x.name for x in who.heNeeds]) 846 print 847 print s.simulate(until = 9 * 60) 848 849 ## ------------------------------------------------------------- 850 ## TEST COMPOUND 'YIELD REQUEST' COMMANDS 851 ## ------------------------------------------------------------- 852 853 ## ------------------------------------------------------------- 854 ## TEST 'yield (request, self, res),(hold, self, delay)' 855 ## == timeout renege 856 ## ------------------------------------------------------------- 857
858 - class JobTO(Process):
859 """ Job class for testing timeout reneging 860 """
861 - def __init__(self, server = None, name = '',sim=None):
862 Process.__init__(self, name,sim=sim) 863 self.res = server 864 self.gotResource = None
865
866 - def execute(self, timeout, usetime):
867 yield (request, self, self.res),(hold, self, timeout) 868 if self.acquired(self.res): 869 self.gotResource = True 870 yield hold, self, usetime 871 yield release, self, self.res 872 else: 873 self.gotResource = False
874 875
876 - def testNoTimeout():
877 """Test that resource gets acquired without timeout 878 """ 879 s=Simulation() 880 s.initialize() 881 res = Resource(name = 'Server', capacity = 1,sim=s) 882 usetime = 5 883 timeout = 1000000 884 j1 = JobTO(server = res, name = 'Job_1',sim=s) 885 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime)) 886 j2 = JobTO(server = res, name = 'Job_2',sim=s) 887 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime)) 888 s.simulate(until = 2 * usetime) 889 assert s.now() == 2 * usetime, 'time not == 2 * usetime' 890 assert j1.gotResource and j2.gotResource,\ 891 'at least one job failed to get resource' 892 assert not (res.waitQ or res.activeQ),\ 893 'job waiting or using resource'
894
895 - def testTimeout1():
896 """Test that timeout occurs when resource busy 897 """ 898 s=Simulation() 899 s.initialize() 900 res = Resource(name = 'Server', capacity = 1, monitored = True,sim=s) 901 usetime = 5 902 timeout = 3 903 j1 = JobTO(server = res, name = 'Job_1',sim=s) 904 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime)) 905 j2 = JobTO(server = res, name = 'Job_2',sim=s) 906 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime)) 907 s.simulate(until = 2 * usetime) 908 assert(s.now() == usetime),'time not == usetime' 909 assert(j1.gotResource),'Job_1 did not get resource' 910 assert(not j2.gotResource),'Job_2 did not renege' 911 assert not (res.waitQ or res.activeQ),\ 912 'job waiting or using resource'
913
914 - def testTimeout2():
915 """Test that timeout occurs when resource has no capacity free 916 """ 917 s=Simulation() 918 s.initialize() 919 res = Resource(name = 'Server', capacity = 0,sim=s) 920 usetime = 5 921 timeout = 3 922 j1 = JobTO(server = res, name = 'Job_1',sim=s) 923 s.activate(j1, j1.execute(timeout = timeout, usetime = usetime)) 924 j2 = JobTO(server = res, name = 'Job_2',sim=s) 925 s.activate(j2, j2.execute(timeout = timeout, usetime = usetime)) 926 s.simulate(until = 2 * usetime) 927 assert s.now() == timeout, 'time %s not == timeout'%now() 928 assert not j1.gotResource, 'Job_1 got resource' 929 assert not j2.gotResource, 'Job_2 got resource' 930 assert not (res.waitQ or res.activeQ),\ 931 'job waiting or using resource'
932 933 ## ------------------------------------------------------------------ 934 ## TEST 'yield (request, self, res),(waitevent, self, event)' 935 ## == event renege 936 ## ------------------------------------------------------------------
937 - class JobEvt(Process):
938 """ Job class for testing event reneging 939 """
940 - def __init__(self, server = None, name = '',sim=None):
941 Process.__init__(self, name,sim=sim) 942 self.res = server 943 self.gotResource = None
944
945 - def execute(self, event, usetime):
946 yield (request, self, self.res),(waitevent, self, event) 947 if self.acquired(self.res): 948 self.gotResource = True 949 yield hold, self, usetime 950 yield release, self, self.res 951 else: 952 self.gotResource = False
953
954 - class JobEvtMulti(Process):
955 """ Job class for testing event reneging with multi - event lists 956 """
957 - def __init__(self, server = None, name = '',sim=None):
958 Process.__init__(self, name,sim=sim) 959 self.res = server 960 self.gotResource = None
961
962 - def execute(self, eventlist, usetime):
963 yield (request, self, self.res),(waitevent, self, eventlist) 964 if self.acquired(self.res): 965 self.gotResource = True 966 yield hold, self, usetime 967 yield release, self, self.res 968 else: 969 self.gotResource = False
970
971 - class FireEvent(Process):
972 """Fires reneging event 973 """
974 - def __init__(self,**vars):
975 Process.__init__(self,**vars)
976 - def fire(self, fireDelay, event):
977 yield hold, self, fireDelay 978 event.signal()
979
980 - def testNoEvent():
981 """Test that processes acquire resource normally if no event fires 982 """ 983 s=Simulation() 984 s.initialize() 985 res = Resource(name = 'Server', capacity = 1,sim=s) 986 event = SimEvent(name='Renege_trigger',sim=s) #never gets fired 987 usetime = 5 988 j1 = JobEvt(server = res, name = 'Job_1',sim=s) 989 s.activate(j1, j1.execute(event = event, usetime = usetime)) 990 j2 = JobEvt(server = res, name = 'Job_2',sim=s) 991 s.activate(j2, j2.execute(event = event, usetime = usetime)) 992 s.simulate(until = 2 * usetime) 993 # Both jobs should get server (in sequence) 994 assert s.now() == 2 * usetime, 'time not == 2 * usetime' 995 assert j1.gotResource and j2.gotResource,\ 996 'at least one job failed to get resource' 997 assert not (res.waitQ or res.activeQ),\ 998 'job waiting or using resource'
999
1000 - def testWaitEvent1():
1001 """Test that signalled event leads to renege when resource busy 1002 """ 1003 s=Simulation() 1004 s.initialize() 1005 res = Resource(name = 'Server', capacity = 1,sim=s) 1006 event = SimEvent('Renege_trigger',sim=s) 1007 usetime = 5 1008 eventtime = 1 1009 j1 = JobEvt(server = res, name = 'Job_1',sim=s) 1010 s.activate(j1, j1.execute(event = event, usetime = usetime)) 1011 j2 = JobEvt(server = res, name = 'Job_2',sim=s) 1012 s.activate(j2, j2.execute(event = event, usetime = usetime)) 1013 f = FireEvent(name = 'FireEvent',sim=s) 1014 s.activate(f, f.fire(fireDelay = eventtime, event = event)) 1015 s.simulate(until = 2 * usetime) 1016 # Job_1 should get server, Job_2 renege 1017 assert(s.now() == usetime),'time not == usetime' 1018 assert(j1.gotResource),'Job_1 did not get resource' 1019 assert(not j2.gotResource),'Job_2 did not renege' 1020 assert not (res.waitQ or res.activeQ),\ 1021 'job waiting or using resource'
1022
1023 - def testWaitEvent2():
1024 """Test that renege - triggering event can be one of an event list 1025 """ 1026 s=Simulation() 1027 s.initialize() 1028 res = Resource(name = 'Server', capacity = 1,sim=s) 1029 event1 = SimEvent('Renege_trigger_1',sim=s) 1030 event2 = SimEvent('Renege_trigger_2',sim=s) 1031 usetime = 5 1032 eventtime = 1 #for both events 1033 j1 = JobEvtMulti(server = res, name = 'Job_1',sim=s) 1034 s.activate(j1, j1.execute(eventlist = [event1, event2],usetime = usetime)) 1035 j2 = JobEvtMulti(server = res, name = 'Job_2',sim=s) 1036 s.activate(j2, j2.execute(eventlist = [event1, event2],usetime = usetime)) 1037 f1 = FireEvent(name = 'FireEvent_1',sim=s) 1038 s.activate(f1, f1.fire(fireDelay = eventtime, event = event1)) 1039 f2 = FireEvent(name = 'FireEvent_2',sim=s) 1040 s.activate(f2, f2.fire(fireDelay = eventtime, event = event2)) 1041 s.simulate(until = 2 * usetime) 1042 # Job_1 should get server, Job_2 should renege 1043 assert(s.now() == usetime),'time not == usetime' 1044 assert(j1.gotResource),'Job_1 did not get resource' 1045 assert(not j2.gotResource),'Job_2 did not renege' 1046 assert not (res.waitQ or res.activeQ),\ 1047 'job waiting or using resource'
1048 1049 testNoTimeout() 1050 testTimeout1() 1051 testTimeout2() 1052 testNoEvent() 1053 testWaitEvent1() 1054 testWaitEvent2() 1055 test_demo() 1056 test_interrupt() 1057 testSimEvents() 1058 testwaituntil() 1059