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