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

Source Code for Module SimPy.SimulationStep

  1  #!/usr / bin / env python 
  2  # $Revision: 163 $ $Date: 2008-12-15 12:47:44 +0100 (Mo, 15 Dez 2008) $ kgm 
  3  """SimulationStep 2.0 Supports stepping through SimPy simulation event - by - event. 
  4  Based on generators (Python 2.3 and later; not 3.0) 
  5   
  6  LICENSE: 
  7  Copyright (C) 2002, 2005, 2006, 2007, 2008  Klaus G. Muller, Tony Vignaux 
  8  mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz 
  9   
 10      This library is free software; you can redistribute it and / or 
 11      modify it under the terms of the GNU Lesser General Public 
 12      License as published by the Free Software Foundation; either 
 13      version 2.1 of the License, or (at your option) any later version. 
 14   
 15      This library is distributed in the hope that it will be useful, 
 16      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 18      Lesser General Public License for more details. 
 19   
 20      You should have received a copy of the GNU Lesser General Public 
 21      License along with this library; if not, write to the Free Software 
 22      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111 - 1307  USA 
 23  END OF LICENSE 
 24   
 25  **Change history:** 
 26   
 27      Started out as SiPy 0.9 
 28       
 29      5 / 9/2002: SiPy 0.9.1 
 30       
 31          - Addition of '_cancel' method in class Process and supporting '_unpost' method in  
 32            class __Evlist. 
 33           
 34          - Removal of redundant 'Action' method in class Process. 
 35           
 36      12 / 9/2002: 
 37       
 38          - Addition of resource class 
 39           
 40          - Addition of '_request' and '_release' coroutine calls 
 41           
 42      15 / 9/2002: moved into SimPy package 
 43       
 44      16 / 9/2002: 
 45          - Resource attributes fully implemented (resources can now have more 
 46            than 1 shareable resource units) 
 47           
 48      17 / 9/2002: 
 49       
 50          - corrected removal from waitQ (Vignaux) 
 51           
 52      17 / 9/2002: 
 53       
 54          - added test for queue discipline in 'test_demo()'. Must be FIFO 
 55           
 56      26 / 9/02: Version 0.2.0 
 57       
 58          - cleaned up code; more consistent naming 
 59           
 60          - prefixed all Simulation - private variable names with '_'. 
 61           
 62          - prefixed all class - private variable names with '__'. 
 63           
 64          - made normal exit quiet (but return message from scheduler() 
 65           
 66      28 / 9/02: 
 67       
 68          - included stopSimulation() 
 69           
 70      15 / 10 / 02: Simulation version 0.3 
 71       
 72          - Version printout now only if __TESTING 
 73           
 74          - '_stop' initialized to True by module load, and set to False in  
 75        initialize() 
 76           
 77          - Introduced 'simulate(until = 0)' instead of 'scheduler(till = 0)'.  
 78        Left 'scheduler()' in for backward compatibility, but marked 
 79        as deprecated. 
 80           
 81          - Added attribute 'name' to class Process; default == 'a_process' 
 82           
 83          - Changed Resource constructor to  
 84        'def __init__(self, capacity = 1, name = 'a_resource', unitName = 'units''. 
 85           
 86      13 / 11 / 02: Simulation version 0.6 
 87       
 88          - Major changes to class Resource: 
 89           
 90              - Added two queue types for resources, FIFO (default) and PriorityQ 
 91               
 92              - Changed constructor to allow selection of queue type. 
 93               
 94              - Introduced preemption of resources (to be used with PriorityQ 
 95                queue type) 
 96               
 97              - Changed constructor of class Resource to allow selection of preemption 
 98               
 99              - Changes to class Process to support preemption of service 
100               
101              - Cleaned up 'simulate' by replacing series of if - statements by dispatch table. 
102   
103      19 / 11 / 02: Simulation version 0.6.1 
104          - Changed priority schemes so that higher values of Process  
105            attribute 'priority' represent higher priority. 
106   
107      20 / 11 / 02: Simulation version 0.7 
108          - Major change of priority approach: 
109   
110              - Priority set by 'yield request, self, res, priority' 
111   
112              - Priority of a Process instance associated with a specific  
113                resource 
114   
115      25 / 11 / 02: Simulation version 0.7.1 
116   
117          - Code cleanup and optimization 
118   
119          - Made process attributes remainService and preempted private  
120           (_remainService and _preempted) 
121   
122      11 / 12 / 2002: First process interrupt implementation 
123   
124          - Addition of user methods 'interrupt' and 'resume' 
125   
126          - Significant code cleanup to maintain process state 
127   
128      20 / 12 / 2002: Changes to 'interrupt'; addition of boolean methods to show 
129                       process states 
130   
131      16 / 3/2003: Changed hold (allowing posting events past _endtime) 
132       
133      18 / 3/2003: Changed _nextev to prevent _t going past _endtime 
134   
135      23 / 3/2003: Introduced new interrupt construct; deleted 'resume' method 
136       
137      25 / 3/2003: Expanded interrupt construct: 
138       
139          - Made 'interrupt' a method  of Process 
140   
141          - Added 'interruptCause' as an attribute of an interrupted process 
142   
143          - Changed definition of 'active' to  
144           'self._nextTime <> None and not self._inInterrupt' 
145   
146          - Cleaned up test_interrupt function 
147   
148      30 / 3/2003: Modification of 'simulate': 
149   
150          - error message if 'initialize' not called (fatal) 
151   
152          - error message if no process scheduled (warning) 
153   
154          - Ensured that upon exit from 'simulate', now() == _endtime is  
155            always valid 
156   
157      2 / 04 / 2003: 
158   
159          - Modification of 'simulate': leave _endtime alone (undid change 
160            of 30 Mar 03) 
161   
162          - faster '_unpost' 
163   
164      3 / 04 / 2003: Made 'priority' private ('_priority') 
165   
166      4 / 04 / 2003: Catch activation of non - generator error 
167   
168      5 / 04 / 2003: Added 'interruptReset()' function to Process. 
169   
170      7 / 04 / 2003: Changed '_unpost' to ensure that process has 
171                     _nextTime == None (is passive) afterwards. 
172   
173      8 / 04 / 2003: Changed _hold to allow for 'yield hold, self'  
174                     (equiv to 'yield hold, self, 0') 
175   
176      10 / 04 / 2003: Changed 'cancel' syntax to 'Process().cancel(victim)' 
177   
178      12 / 5/2003: Changed eventlist handling from dictionary to bisect 
179       
180      9 / 6/2003: - Changed eventlist handling from pure dictionary to bisect- 
181                  sorted 'timestamps' list of keys, resulting in greatly  
182                  improved performance for models with large 
183                  numbers of event notices with differing event times. 
184                  ========================================================= 
185                  This great change was suggested by Prof. Simon Frost.  
186                  Thank you, Simon! This version 1.3 is dedicated to you! 
187                  ========================================================= 
188                - Added import of Lister which supports well - structured  
189                  printing of all attributes of Process and Resource instances. 
190   
191      Oct 2003: Added monitored Resource instances (Monitors for activeQ and waitQ) 
192   
193      13 Dec 2003: Merged in Monitor and Histogram 
194   
195      27 Feb 2004: Repaired bug in activeQ monitor of class Resource. Now actMon 
196                   correctly records departures from activeQ. 
197                    
198      19 May 2004: Added erroneously omitted Histogram class. 
199   
200      5 Sep 2004: Added SimEvents synchronization constructs 
201       
202      17 Sep 2004: Added waituntil synchronization construct 
203       
204      01 Dec 2004: SimPy version 1.5 
205                   Changes in this module: Repaired SimEvents bug re proc.eventsFired 
206                    
207      12 Jan 2005: SimPy version 1.5.1 
208                   Changes in this module: Monitor objects now have a default name 
209                                           'a_Monitor' 
210                                            
211      29 Mar 2005: Start SimPy 1.6: compound 'yield request' statements 
212       
213      05 Jun 2005: Fixed bug in _request method -- waitMon did not work properly in 
214                   preemption case 
215                    
216      09 Jun 2005: Added test in 'activate' to see whether 'initialize()' was called first. 
217       
218      23 Aug 2005: - Added Tally data collection class 
219                   - Adjusted Resource to work with Tally 
220                   - Redid function allEventNotices() (returns prettyprinted string with event 
221                     times and names of process instances 
222                   - Added function allEventTimes (returns event times of all scheduled events) 
223                    
224      16 Mar 2006: - Added Store and Level classes 
225                   - Added 'yield get' and 'yield put' 
226                    
227      10 May 2006: - Repaired bug in Store._get method 
228                   - Repaired Level to allow initialBuffered have float value 
229                   - Added type test for Level get parameter 'nrToGet' 
230                    
231      06 Jun 2006: - To improve pretty - printed output of 'Level' objects, changed attribute 
232                     _nrBuffered to nrBuffered (synonym for amount property) 
233                   - To improve pretty - printed output of 'Store' objects, added attribute 
234                     buffered (which refers to _theBuffer) 
235                      
236      25 Aug 2006: - Start of version 1.8 
237                   - made 'version' public 
238                   - corrected condQ initialization bug 
239                    
240      30 Sep 2006: - Introduced checks to ensure capacity of a Buffer > 0 
241                   - Removed from __future__ import (so Python 2.3 or later needed) 
242                   
243      15 Oct 2006: - Added code to register all Monitors and all Tallies in variables 
244                     'allMonitors' and 'allTallies' 
245                   - Added function 'startCollection' to activate Monitors and Tallies at a 
246                     specified time (e.g. after warmup period) 
247                   - Moved all test / demo programs to after 'if __name__ == '__main__':'. 
248                   
249      17 Oct 2006: - Added compound 'put' and 'get' statements for Level and Store. 
250       
251      18 Oct 2006: - Repaired bug: self.eventsFired now gets set after an event fires 
252                     in a compound yield get / put with a waitevent clause (reneging case). 
253                      
254      21 Oct 2006: - Introduced Store 'yield get' with a filter function. 
255                   
256      22 Oct 2006: - Repaired bug in prettyprinting of Store objects (the buffer  
257                     content==._theBuffer was not shown) by changing ._theBuffer  
258                     to .theBuffer. 
259                   
260      04 Dec 2006: - Added printHistogram method to Tally and Monitor (generates 
261                     table - form histogram) 
262                       
263      07 Dec 2006: - Changed the __str__ method of Histogram to print a table  
264                     (like printHistogram). 
265       
266      18 Dec 2006: - Added trace printing of Buffers' 'unitName' for yield get and put. 
267       
268      09 Jun 2007: - Cleaned out all uses of 'object' to prevent name clash. 
269       
270      18 Nov 2007: - Start of 1.9 development 
271                   - Added 'start' method (alternative to activate) to Process 
272                    
273      22 Nov 2007: - Major change to event list handling to speed up larger models: 
274                      * Drop dictionary 
275                      * Replace bisect by heapq 
276                      * Mark cancelled event notices in unpost and skip them in 
277                        nextev (great idea of Tony Vignaux)) 
278                         
279      4 Dec 2007: - Added twVariance calculation for both Monitor and Tally (gav) 
280       
281      5 Dec 2007: - Changed name back to timeVariance (gav) 
282       
283      1 Mar 2008: - Start of 1.9.1 bugfix release 
284                  - Delete circular reference in Process instances when event  
285                    notice has been processed (caused much circular garbage) 
286                  - Added capability for multiple preempts of a process 
287                   
288      10 Aug 2008: - Removed most classes / methods and imported them from 
289                     Simulation.py instead (Stefan Scherfke) 
290                   - Moved remaining functions to SimulationStep and added some 
291                     methods for backward compatibility 
292       
293  """ 
294  from SimPy.Simulation import * 
295   
296  __TESTING = False 
297  version = __version__ = '2.0 $Revision: 163 $ $Date: 2008-12-15 12:47:44 +0100 (Mo, 15 Dez 2008) $' 
298  if __TESTING:  
299      print 'SimPy.SimulationStep %s' %__version__, 
300      if __debug__: 
301          print '__debug__ on' 
302      else: 
303          print 
304   
305  _step = False 
306   
307 -class SimulationStep(Simulation):
308
309 - def __init__(self):
310 Simulation.__init__(self) 311 self._step = False
312
313 - def initialize(self):
314 Simulation.initialize(self) 315 self._step = False
316
317 - def startStepping(self):
318 """Application function to start stepping through simulation.""" 319 self._step = True
320
321 - def stopStepping(self):
322 """Application function to stop stepping through simulation.""" 323 self._step = False
324
325 - def simulate(self, callback = lambda :None, until = 0):
326 """Schedules Processes / semi - coroutines until time 'until'""" 327 328 """Gets called once. Afterwards, co - routines (generators) return by 329 'yield' with a cargo: 330 yield hold, self, <delay>: schedules the 'self' process for activation 331 after < delay > time units.If <,delay > missing, 332 same as 'yield hold, self, 0' 333 334 yield passivate, self : makes the 'self' process wait to be re - activated 335 336 yield request, self,<Resource > [,<priority>]: request 1 unit from < Resource> 337 with < priority > pos integer (default = 0) 338 339 yield release, self,<Resource> : release 1 unit to < Resource> 340 341 yield waitevent, self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]: 342 wait for one or more of several events 343 344 345 yield queueevent, self,<SimEvent>|[<Evt1>,<Evt2>,<Evt3), . . . ]: 346 queue for one or more of several events 347 348 yield waituntil, self, cond : wait for arbitrary condition 349 350 yield get, self,<buffer > [,<WhatToGet > [,<priority>]] 351 get < WhatToGet > items from buffer (default = 1); 352 <WhatToGet > can be a pos integer or a filter function 353 (Store only) 354 355 yield put, self,<buffer > [,<WhatToPut > [,priority]] 356 put < WhatToPut > items into buffer (default = 1); 357 <WhatToPut > can be a pos integer (Level) or a list of objects 358 (Store) 359 360 EXTENSIONS: 361 Request with timeout reneging: 362 yield (request, self,<Resource>),(hold, self,<patience>) : 363 requests 1 unit from < Resource>. If unit not acquired in time period 364 <patience>, self leaves waitQ (reneges). 365 366 Request with event - based reneging: 367 yield (request, self,<Resource>),(waitevent, self,<eventlist>): 368 requests 1 unit from < Resource>. If one of the events in < eventlist > occurs before unit 369 acquired, self leaves waitQ (reneges). 370 371 Get with timeout reneging (for Store and Level): 372 yield (get, self,<buffer>,nrToGet etc.),(hold, self,<patience>) 373 requests < nrToGet > items / units from < buffer>. If not acquired < nrToGet > in time period 374 <patience>, self leaves < buffer>.getQ (reneges). 375 376 Get with event - based reneging (for Store and Level): 377 yield (get, self,<buffer>,nrToGet etc.),(waitevent, self,<eventlist>) 378 requests < nrToGet > items / units from < buffer>. If not acquired < nrToGet > before one of 379 the events in < eventlist > occurs, self leaves < buffer>.getQ (reneges). 380 381 382 383 Event notices get posted in event - list by scheduler after 'yield' or by 384 'activate' / 'reactivate' functions. 385 386 Nov 9, 2003: Added capability to step through simulation event by event if 387 step == True. 'callback' gets called after every event. It can 388 cancel stepping or end run. API and semantics backwards 389 compatible with previous versions of simulate(). 390 391 """ 392 paused = False 393 self._stop = False 394 395 if self._e is None: 396 raise FatalSimerror('Simulation not initialized') 397 if self._e._isEmpty(): 398 message = 'SimPy: No activities scheduled' 399 return message 400 401 self._endtime = until 402 message = 'SimPy: Normal exit' 403 dispatch={hold:holdfunc, request:requestfunc, release:releasefunc, 404 passivate:passivatefunc, waitevent:waitevfunc, queueevent:queueevfunc, 405 waituntil:waituntilfunc, get:getfunc, put:putfunc} 406 commandcodes = dispatch.keys() 407 commandwords={hold:'hold', request:'request', release:'release', passivate:'passivate', 408 waitevent:'waitevent', queueevent:'queueevent', waituntil:'waituntil', 409 get:'get', put:'put'} 410 nextev = self._e._nextev ## just a timesaver 411 while not self._stop and self._t <= self._endtime: 412 try: 413 a = nextev() 414 if not a[0] is None: 415 ## 'a' is tuple '(<yield command>, <action>)' 416 if type(a[0][0]) == tuple: 417 ##allowing for yield (request, self, res),(waituntil, self, cond) 418 command = a[0][0][0] 419 else: 420 command = a[0][0] 421 if __debug__: 422 if not command in commandcodes: 423 raise FatalSimerror('Illegal command: yield %s'%command) 424 dispatch[command](a) 425 except FatalSimerror, error: 426 print 'SimPy: ' + error.value 427 sys.exit(1) 428 except Simerror, error: 429 message = 'SimPy: ' + error.value 430 self._stop = True 431 if self._step: 432 callback() 433 if self._wustep: 434 self._test() 435 self._stopWUStepping() 436 self.stopStepping() 437 self._e = None 438 return message
439
440 - def simulateStep(self, callback = lambda :None, until = 0):
441 """Schedules Processes / semi - coroutines until next event 442 443 Can be called repeatedly. 444 Behaves like 'simulate', but does execute only one event per call. 445 446 447 448 """ 449 status = 'resumable' 450 451 if self._e == None: 452 raise Simerror('Fatal SimPy error: Simulation not initialized') 453 if self._e._isEmpty(): 454 message = 'SimPy: No activities scheduled' 455 status = 'notResumable' 456 return message, status 457 458 self._endtime = until 459 message = 'SimPy: Normal exit' 460 dispatch={hold:holdfunc, request:requestfunc, release:releasefunc, 461 passivate:passivatefunc, waitevent:waitevfunc, queueevent:queueevfunc, 462 waituntil:waituntilfunc, get:getfunc, put:putfunc} 463 commandcodes = dispatch.keys() 464 commandwords={hold:'hold', request:'request', release:'release', passivate:'passivate', 465 waitevent:'waitevent', queueevent:'queueevent', waituntil:'waituntil', 466 get:'get', put:'put'} 467 if not self._stop and self._t <= self._endtime: 468 try: 469 a = self._e._nextev() 470 if not a[0] == None: 471 ## 'a' is tuple '(<yield command>, <action>)' 472 if type(a[0][0]) == tuple: 473 ##allowing for yield (request, self, res),(waituntil, self, cond) 474 command = a[0][0][0] 475 else: 476 command = a[0][0] 477 if __debug__: 478 if not command in commandcodes: 479 raise FatalSimerror('Fatal error: illegal command: yield %s'%command) 480 dispatch[command](a) 481 except FatalSimerror, error: 482 print 'SimPy: ' + error.value 483 sys.exit(1) 484 except Simerror, error: 485 message = 'SimPy: ' + error.value 486 self._stop = True 487 status = 'notResumable' 488 if self._step: 489 callback() 490 return message, status
491 492 # For backward compatibility 493 Globals.sim = SimulationStep()
494 -def startStepping():
495 Globals.sim.startStepping()
496
497 -def stopStepping():
498 Globals.sim.stopStepping()
499
500 -def simulate(callback = lambda :None, until = 0):
501 return Globals.sim.simulate(callback = callback, until = until)
502
503 -def simulateStep(callback = lambda :None, until = 0):
504 return Globals.sim.simulateStep(callback = callback, until = until)
505 # End backward compatibility 506 507 508 ################### end of Simulation module 509 510 if __name__ == '__main__': 511 print 'SimPy.SimulationStep %s' %__version__ 512 ################### start of test / demo programs 513
514 - def askCancel():
515 a = raw_input('[Time=%s] End run (e), Continue stepping (s), Run to end (r)'%now()) 516 if a == 'e': 517 stopSimulation() 518 elif a == 's': 519 return 520 else: 521 stopStepping()
522
523 - def test_demo():
524 class Aa(Process): 525 sequIn = [] 526 sequOut = [] 527 def __init__(self, holdtime, name): 528 Process.__init__(self, name) 529 self.holdtime = holdtime
530 531 def life(self, priority): 532 for i in range(1): 533 Aa.sequIn.append(self.name) 534 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 535 len(rrr.activeQ) 536 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ] 537 print 'activeQ: ',[(k.name, k._priority[rrr]) \ 538 for k in rrr.activeQ] 539 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 540 'Inconsistent resource unit numbers' 541 print now(),self.name, 'requests 1 ', rrr.unitName 542 yield request, self, rrr, priority 543 print now(),self.name, 'has 1 ', rrr.unitName 544 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 545 len(rrr.activeQ) 546 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 547 len(rrr.activeQ) 548 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 549 'Inconsistent resource unit numbers' 550 yield hold, self, self.holdtime 551 print now(),self.name, 'gives up 1', rrr.unitName 552 yield release, self, rrr 553 Aa.sequOut.append(self.name) 554 print now(),self.name, 'has released 1 ', rrr.unitName 555 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ] 556 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\ 557 len(rrr.activeQ) 558 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \ 559 'Inconsistent resource unit numbers' 560 561 class Destroyer(Process): 562 def __init__(self): 563 Process.__init__(self) 564 565 def destroy(self, whichProcesses): 566 for i in whichProcesses: 567 Process().cancel(i) 568 yield hold, self, 0 569 570 class Observer(Process): 571 def __init__(self): 572 Process.__init__(self) 573 574 def observe(self, step, processes, res): 575 while now() < 11: 576 for i in processes: 577 print ' %s %s: act:%s, pass:%s, term: %s, interr:%s, qu:%s'\ 578 %(now(),i.name, i.active(),i.passive(),i.terminated()\ 579 ,i.interrupted(),i.queuing(res)) 580 print 581 yield hold, self, step 582 583 class UserControl(Process): 584 def letUserInteract(self, when): 585 yield hold, self, when 586 startStepping() 587 588 print '****First case == priority queue, resource service not preemptable' 589 initialize() 590 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ, 591 preemptable = 0) 592 procs = [] 593 for i in range(10): 594 z = Aa(holdtime = i, name = 'Car ' + str(i)) 595 procs.append(z) 596 activate(z, z.life(priority = i)) 597 o = Observer() 598 activate(o, o.observe(1, procs, rrr)) 599 startStepping() 600 a = simulate(until = 10000, callback = askCancel) 601 print 'Input sequence: ', Aa.sequIn 602 print 'Output sequence: ', Aa.sequOut 603 604 print '\n****Second case == priority queue, resource service preemptable' 605 initialize() 606 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ, 607 preemptable = 1) 608 procs = [] 609 for i in range(10): 610 z = Aa(holdtime = i, name = 'Car ' + str(i)) 611 procs.append(z) 612 activate(z, z.life(priority = i)) 613 o = Observer() 614 activate(o, o.observe(1, procs, rrr)) 615 u = UserControl() 616 activate(u, u.letUserInteract(4)) 617 Aa.sequIn = [] 618 Aa.sequOut = [] 619 a = simulate(askCancel, until = 10000) 620 print a 621 print 'Input sequence: ', Aa.sequIn 622 print 'Output sequence: ', Aa.sequOut 623
624 - def test_interrupt():
625 class Bus(Process): 626 def __init__(self, name): 627 Process.__init__(self, name)
628 629 def operate(self, repairduration = 0): 630 print now(),'>> %s starts' % (self.name) 631 tripleft = 1000 632 while tripleft > 0: 633 yield hold, self, tripleft 634 if self.interrupted(): 635 print 'interrupted by %s' %self.interruptCause.name 636 print '%s: %s breaks down ' %(now(),self.name) 637 tripleft = self.interruptLeft 638 self.interruptReset() 639 print 'tripleft ', tripleft 640 reactivate(br, delay = repairduration) # breakdowns only during operation 641 yield hold, self, repairduration 642 print now(),' repaired' 643 else: 644 break # no breakdown, ergo bus arrived 645 print now(),'<< %s done' % (self.name) 646 647 class Breakdown(Process): 648 def __init__(self, myBus): 649 Process.__init__(self, name = 'Breakdown ' + myBus.name) 650 self.bus = myBus 651 652 def breakBus(self, interval): 653 654 while True: 655 yield hold, self, interval 656 if self.bus.terminated(): break 657 self.interrupt(self.bus) 658 659 print'\n\n+++test_interrupt' 660 initialize() 661 b = Bus('Bus 1') 662 activate(b, b.operate(repairduration = 20)) 663 br = Breakdown(b) 664 activate(br, br.breakBus(200)) 665 startStepping() 666 simulate(until = 4000) 667 668
669 - def testSimEvents():
670 class Waiter(Process): 671 def waiting(self, theSignal): 672 while True: 673 yield waitevent, self, theSignal 674 print '%s: process \'%s\' continued after waiting for %s' % (now(),self.name, theSignal.name) 675 yield queueevent, self, theSignal 676 print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name)
677 678 class ORWaiter(Process): 679 def waiting(self, signals): 680 while True: 681 yield waitevent, self, signals 682 print now(),'one of %s signals occurred' % [x.name for x in signals] 683 print '\t%s (fired / param)'%[(x.name, x.signalparam) for x in self.eventsFired] 684 yield hold, self, 1 685 686 class Caller(Process): 687 def calling(self): 688 while True: 689 signal1.signal('wake up!') 690 print '%s: signal 1 has occurred'%now() 691 yield hold, self, 10 692 signal2.signal('and again') 693 signal2.signal('sig 2 again') 694 print '%s: signal1, signal2 have occurred'%now() 695 yield hold, self, 10 696 print'\n+++testSimEvents output' 697 initialize() 698 signal1 = SimEvent('signal 1') 699 signal2 = SimEvent('signal 2') 700 signal1.signal('startup1') 701 signal2.signal('startup2') 702 w1 = Waiter('waiting for signal 1') 703 activate(w1, w1.waiting(signal1)) 704 w2 = Waiter('waiting for signal 2') 705 activate(w2, w2.waiting(signal2)) 706 w3 = Waiter('also waiting for signal 2') 707 activate(w3, w3.waiting(signal2)) 708 w4 = ORWaiter('waiting for either signal 1 or signal 2') 709 activate(w4, w4.waiting([signal1, signal2]),prior = True) 710 c = Caller('Caller') 711 activate(c, c.calling()) 712 print simulate(until = 100) 713
714 - def testwaituntil():
715 """ 716 Demo of waitUntil capability. 717 718 Scenario: 719 Three workers require sets of tools to do their jobs. Tools are shared, scarce 720 resources for which they compete. 721 """ 722 723 724 class Worker(Process): 725 def __init__(self, name, heNeeds = []): 726 Process.__init__(self, name) 727 self.heNeeds = heNeeds
728 def work(self): 729 730 def workerNeeds(): 731 for item in self.heNeeds: 732 if item.n == 0: 733 return False 734 return True 735 736 while now() < 8 * 60: 737 yield waituntil, self, workerNeeds 738 for item in self.heNeeds: 739 yield request, self, item 740 print '%s %s has %s and starts job' % (now(),self.name, 741 [x.name for x in self.heNeeds]) 742 yield hold, self, random.uniform(10, 30) 743 for item in self.heNeeds: 744 yield release, self, item 745 yield hold, self, 2 #rest 746 747 print '\n+++ nwaituntil demo output' 748 initialize() 749 brush = Resource(capacity = 1, name = 'brush') 750 ladder = Resource(capacity = 2, name = 'ladder') 751 hammer = Resource(capacity = 1, name = 'hammer') 752 saw = Resource(capacity = 1, name = 'saw') 753 painter = Worker('painter',[brush, ladder]) 754 activate(painter, painter.work()) 755 roofer = Worker('roofer',[hammer, ladder, ladder]) 756 activate(roofer, roofer.work()) 757 treeguy = Worker('treeguy',[saw, ladder]) 758 activate(treeguy, treeguy.work()) 759 for who in (painter, roofer, treeguy): 760 print '%s needs %s for his job' % (who.name,[x.name for x in who.heNeeds]) 761 print 762 print simulate(until = 9 * 60) 763 764 765 766 767 test_demo() 768 test_interrupt() 769 testSimEvents() 770 testwaituntil() 771