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

# Source Code for Module SimPy.SimulationRT

      1  #!/usr / bin / env python
      2  # coding=utf-8
      3  # $Revision: 485 $ $Date: 2010-05-10 09:23:24 +0200 (Mon, 10 May 2010) $ kgm
      4  """SimulationRT 2.1 Provides synchronization of real time and SimPy simulation time.
      5  Implements SimPy Processes, resources, and the backbone simulation scheduling
      6  by coroutine calls.
      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  import time
     29
     30  from SimPy.Simulation import *
     31
     32
     33  __TESTING = False
     34  version = __version__ = '2.1 $Revision: 485 $ $Date: 2010-05-10 09:23:24 +0200 (Mon, 10 May 2010) $'
     35  if __TESTING:
     36      print 'SimPy.SimulationRT %s' %__version__,
     37      if __debug__:
     38          print '__debug__ on'
     39      else:
     40          print
     41
     42 -class SimulationRT(Simulation):
     43 -    def __init__(self):
     44          if sys.platform == 'win32':  #take care of differences in clock accuracy
     45              self.wallclock = time.clock
     46          else:
     47              self.wallclock = time.time
     48          Simulation.__init__(self)
     49
     50 -    def rtnow(self):
     51          return self.wallclock() - self.rtstart
     52
     53 -    def rtset(self, rel_speed=1):
     54          """
     55          Resets the ratio simulation time over clock time(seconds).
     56          """
     57          # Ensure relative speed is a float.
     58          self.rel_speed = float(rel_speed)
     59
     60 -    def simulate(self, until=0, real_time=False, rel_speed=1):
     61          """
     62          Simulates until simulation time reaches ``until``. If ``real_time`` is
     63          ``True`` a simulation time unit is matched with real time by the factor
     64          1 / ``rel_speed``.
     65          """
     66          try:
     67              self.rtstart = self.wallclock()
     68              self.rtset(rel_speed)
     69
     70              while self._timestamps and not self._stop:
     71                  next_event_time = self.peek()
     72                  if next_event_time > until: break
     73
     74                  if real_time:
     75                      delay = (
     76                              next_event_time / self.rel_speed -
     77                              (self.wallclock() - self.rtstart)
     78                      )
     79                      if delay > 0: time.sleep(delay)
     80
     81                  self.step()
     82
     83              # There are still events in the timestamps list and the simulation
     84              # has not been manually stopped. This means we have reached the stop
     85              # time.
     86              if not self._stop and self._timestamps:
     87                  self._t = until
     88                  return 'SimPy: Normal exit'
     89              else:
     90                  return 'SimPy: No activities scheduled'
     91          except Simerror, error:
     92              return 'SimPy: ' + error.value
     93          finally:
     94              self._stop = True
     95
     96
     97
     98  # For backward compatibility
     99  Globals.sim = SimulationRT()
    100
    101 -def rtnow():
    102      return Globals.sim.rtnow()
    103
    104  rtset =  Globals.sim.rtset
    105
    106 -def simulate(until = 0, real_time = False, rel_speed = 1):
    107      return Globals.sim.simulate(until = until, real_time = real_time, rel_speed = rel_speed)
    108
    109  wallclock = Globals.sim.wallclock
    110  # End backward compatibility
    111
    112  if __name__ == '__main__':
    113      print 'SimPy.SimulationRT %s' %__version__
    114      ############# Test / demo functions #############
    115 -    def test_demo():
    116          class Aa(Process):
    117              sequIn = []
    118              sequOut = []
    119              def __init__(self, holdtime, name):
    120                  Process.__init__(self, name)
    121                  self.holdtime = holdtime
    122
    123              def life(self, priority):
    124                  for i in range(1):
    125                      Aa.sequIn.append(self.name)
    126                      print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
    127                            len(rrr.activeQ)
    128                      print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
    129                      print 'activeQ: ',[(k.name, k._priority[rrr]) \
    130                                 for k in rrr.activeQ]
    131                      assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
    132                     'Inconsistent resource unit numbers'
    133                      print now(),self.name, 'requests 1 ', rrr.unitName
    134                      yield request, self, rrr, priority
    135                      print now(),self.name, 'has 1 ', rrr.unitName
    136                      print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
    137                            len(rrr.activeQ)
    138                      print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
    139                            len(rrr.activeQ)
    140                      assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
    141                     'Inconsistent resource unit numbers'
    142                      yield hold, self, self.holdtime
    143                      print now(),self.name, 'gives up 1', rrr.unitName
    144                      yield release, self, rrr
    145                      Aa.sequOut.append(self.name)
    146                      print now(),self.name, 'has released 1 ', rrr.unitName
    147                      print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
    148                      print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
    149                            len(rrr.activeQ)
    150                      assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
    151                             'Inconsistent resource unit numbers'
    152
    153          class Observer(Process):
    154              def __init__(self):
    155                  Process.__init__(self)
    156
    157              def observe(self, step, processes, res):
    158                  while now() < 11:
    159                      for i in processes:
    160                          print ' %s %s: act:%s, pass:%s, term: %s, interr:%s, qu:%s'\
    161                                %(now(),i.name, i.active(),i.passive(),i.terminated()\
    162                              ,i.interrupted(),i.queuing(res))
    163                      print
    164                      yield hold, self, step
    165
    166          print'\n+++test_demo output'
    167          print '****First case == priority queue, resource service not preemptable'
    168          initialize()
    169          rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
    170                       preemptable = 0)
    171          procs = []
    172          for i in range(10):
    173              z = Aa(holdtime = i, name = 'Car ' + str(i))
    174              procs.append(z)
    175              activate(z, z.life(priority = i))
    176          o = Observer()
    177          activate(o, o.observe(1, procs, rrr))
    178          a = simulate(until = 10000, real_time = True, rel_speed = 1)
    179          print a
    180          print 'Input sequence: ', Aa.sequIn
    181          print 'Output sequence: ', Aa.sequOut
    182
    183          print '\n****Second case == priority queue, resource service preemptable'
    184          initialize()
    185          rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
    186                       preemptable = 1)
    187          procs = []
    188          for i in range(10):
    189              z = Aa(holdtime = i, name = 'Car ' + str(i))
    190              procs.append(z)
    191              activate(z, z.life(priority = i))
    192          o = Observer()
    193          activate(o, o.observe(1, procs, rrr))
    194          Aa.sequIn = []
    195          Aa.sequOut = []
    196          a = simulate(until = 10000)
    197          print a
    198          print 'Input sequence: ', Aa.sequIn
    199          print 'Output sequence: ', Aa.sequOut
    200
    201 -    def test_interrupt():
    202          class Bus(Process):
    203              def __init__(self, name):
    204                  Process.__init__(self, name)
    205
    206              def operate(self, repairduration = 0):
    207                  print now(),rtnow(),'>> %s starts' % (self.name)
    208                  tripleft = 1000
    209                  while tripleft > 0:
    210                      yield hold, self, tripleft
    211                      if self.interrupted():
    212                          print 'interrupted by %s' %self.interruptCause.name
    213                          print '%s(%s): %s breaks down ' %(now(),rtnow(),self.name)
    214                          tripleft = self.interruptLeft
    215                          self.interruptReset()
    216                          print 'tripleft ', tripleft
    217                          reactivate(br, delay = repairduration) # breakdowns only during operation
    218                          yield hold, self, repairduration
    219                          print now(),rtnow(),' repaired'
    220                      else:
    221                          break # no breakdown, ergo bus arrived
    222                  print now(),'<< %s done' % (self.name)
    223
    224          class Breakdown(Process):
    225              def __init__(self, myBus):
    226                  Process.__init__(self, name = 'Breakdown ' + myBus.name)
    227                  self.bus = myBus
    228
    229              def breakBus(self, interval):
    230
    231                  while True:
    232                      yield hold, self, interval
    233                      if self.bus.terminated(): break
    234                      self.interrupt(self.bus)
    235
    236          print'\n\n+++test_interrupt'
    237          initialize()
    238          b = Bus('Bus 1')
    239          activate(b, b.operate(repairduration = 20))
    240          br = Breakdown(b)
    241          activate(br, br.breakBus(200))
    242          print simulate(until = 4000, real_time = True, rel_speed = 200)
    243
    244 -    def testSimEvents():
    245          class Waiter(Process):
    246              def waiting(self, theSignal):
    247                  while True:
    248                      yield waitevent, self, theSignal
    249                      print '%s: process \'%s\' continued after waiting for %s' % (now(),self.name, theSignal.name)
    250                      yield queueevent, self, theSignal
    251                      print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name)
    252
    253          class ORWaiter(Process):
    254              def waiting(self, signals):
    255                  while True:
    256                      yield waitevent, self, signals
    257                      print now(),'one of %s signals occurred' % [x.name for x in signals]
    258                      print '\t%s (fired / param)'%[(x.name, x.signalparam) for x in self.eventsFired]
    259                      yield hold, self, 1
    260
    261          class Caller(Process):
    262              def calling(self):
    263                  while True:
    264                      signal1.signal('wake up!')
    265                      print '%s: signal 1 has occurred'%now()
    266                      yield hold, self, 10
    267                      signal2.signal('and again')
    268                      signal2.signal('sig 2 again')
    269                      print '%s: signal1, signal2 have occurred'%now()
    270                      yield hold, self, 10
    271          print'\n\n+++testSimEvents output'
    272          initialize()
    273          signal1 = SimEvent('signal 1')
    274          signal2 = SimEvent('signal 2')
    275          signal1.signal('startup1')
    276          signal2.signal('startup2')
    277          w1 = Waiter('waiting for signal 1')
    278          activate(w1, w1.waiting(signal1))
    279          w2 = Waiter('waiting for signal 2')
    280          activate(w2, w2.waiting(signal2))
    281          w3 = Waiter('also waiting for signal 2')
    282          activate(w3, w3.waiting(signal2))
    283          w4 = ORWaiter('waiting for either signal 1 or signal 2')
    284          activate(w4, w4.waiting([signal1, signal2]),prior = True)
    285          c = Caller('Caller')
    286          activate(c, c.calling())
    287          print simulate(until = 100)
    288
    289 -    def testwaituntil():
    290          """
    291          Demo of waitUntil capability.
    292
    293          Scenario:
    294          Three workers require sets of tools to do their jobs. Tools are shared, scarce
    295          resources for which they compete.
    296          """
    297
    298
    299          class Worker(Process):
    300              def __init__(self, name, heNeeds = []):
    301                  Process.__init__(self, name)
    302                  self.heNeeds = heNeeds
    303              def work(self):
    304
    305                  def workerNeeds():
    306                      for item in self.heNeeds:
    307                          if item.n == 0:
    308                              return False
    309                      return True
    310
    311                  while now() < 8 * 60:
    312                      yield waituntil, self, workerNeeds
    313                      for item in self.heNeeds:
    314                          yield request, self, item
    315                      print '%s %s has %s and starts job' % (now(),self.name,
    316                          [x.name for x in self.heNeeds])
    317                      yield hold, self, random.uniform(10, 30)
    318                      for item in self.heNeeds:
    319                          yield release, self, item
    320                      yield hold, self, 2 #rest
    321
    322          print '\n+++\nwaituntil demo output'
    323          initialize()
    324          brush = Resource(capacity = 1, name = 'brush')
    325          ladder = Resource(capacity = 2, name = 'ladder')
    326          hammer = Resource(capacity = 1, name = 'hammer')
    327          saw = Resource(capacity = 1, name = 'saw')
    328          painter = Worker('painter',[brush, ladder])
    329          activate(painter, painter.work())
    330          roofer = Worker('roofer',[hammer, ladder, ladder])
    331          activate(roofer, roofer.work())
    332          treeguy = Worker('treeguy',[saw, ladder])
    333          activate(treeguy, treeguy.work())
    334          for who in (painter, roofer, treeguy):
    335              print '%s needs %s for his job' % (who.name,[x.name for x in who.heNeeds])
    336          print
    337          print simulate(until = 9 * 60)
    338      test_demo()
    339      # Run tests
    340      test_interrupt()
    341      #testSimEvents()
    342      #testwaituntil()
    343