Search code examples
pythonsimulationtry-exceptsimpy

Simpy; how to incorporate an unknown amount of interrupts


I am using python and simpy for a simulation. In the simulation one instance (interrupted) can be interrupted by another (interruptor). I use nested try except statements for each interruption. Nested try except statements work if I know the maximum number of interruptions.

The problem is that I don't know how many interruptions will occur (could be 1,2,3, ...). I don't know how to handle an objects which are interrupted an unknown number of times.

The code below works for three interruptions, however breaks down if a fourth interruption is included (due to the three nested try except statements).

Is it possible to make the code more generic so it can handle an unknown number of interruptions?

Any help is much appreciated.

Code:

import simpy
import random

class Interupted(object):

    def __init__(self, env):
        self.env = env
        self.isInterrupted = False
        self.action = env.process(self.run())

    def run(self):
        self.isInterrupted = False
        try:
            print('uninterrupted at %s' % (self.env.now))
            yield self.env.timeout(3)
        except simpy.Interrupt as interrupt:
            print(interrupt.cause)
            try:
                self.isInterrupted = True
                print('interrupted at %s' % (self.env.now))
                yield self.env.timeout(10)
            except simpy.Interrupt as interrupt:
                print(interrupt.cause)
                try:
                    self.isInterrupted = True
                    print('interrupted at %s' % (self.env.now))
                    yield self.env.timeout(10)
                except simpy.Interrupt as interrupt:
                    print(interrupt.cause)
                    self.isInterrupted = True
                    print('interrupted at %s' % (self.env.now))
                    yield self.env.timeout(10)

class Interruptor(object):

    def __init__(self, env, interrupted):
        self.env = env
        self.interrupted = interrupted
        self.action = env.process(self.run(interrupted))

    def run(self, interrupted):
        yield self.env.timeout(1)
        interrupted.action.interrupt("first interrupt")
        yield self.env.timeout(1)
        interrupted.action.interrupt("second interrupt")
        yield self.env.timeout(1)
        interrupted.action.interrupt("third interrupt")
        yield self.env.timeout(1)
        interrupted.action.interrupt("fourth interrupt")

env = simpy.Environment()
interrupted = Interupted(env)
interruptor = Interruptor(env, interrupted)
env.run(until=15)

Output:

uninterrupted at 0
first interrupt
interrupted at 1  
second interrupt
interrupted at 2
third interrupt
interrupted at 3
Traceback (most recent call last):
File "interrupt.py", line 58, in <module>
    env.run(until=15)
File "/usr/local/lib/python2.7/dist-packages/simpy/core.py", line 137, in run
    self.step()
File "/usr/local/lib/python2.7/dist-packages/simpy/core.py", line 229,     in step
    raise exc
simpy.events.Interrupt: Interrupt('fourth interrupt')

Versions used:

  • Python: 2.7.3
  • Simpy: 3.0.7

Solution

  • I made some progress and came up with a solution.

    Multiple interrupts do not require nested try except statements. Separate statements also seem to work. After some trail and error I found out that it is also possible to use separate try except statement.

    The first interrupt starts a counter. Every interrupt increases the counter and the while loop ensures all interrupts are taken care of. This works as long as the except doesn't contain an additional yield statement as above.

    Code:

    import simpy
    import random
    
    class Interupted(object):
    
        def __init__(self, env):
            self.env = env
            self.isInterrupted = False
            self.interruptions = 0
            self.action = env.process(self.run())
    
        def run(self):
            print('start at time %s' % (self.env.now))
            try:
                yield self.env.timeout(10)
            except simpy.Interrupt as interrupt:
                self.isInterrupted = not self.isInterrupted
                self.interruptions += 1
                print('interrupted at time %s interrupted: %s interrupted by: %s' % (self.env.now, self.isInterrupted, interrupt.cause))
            while (self.interruptions > 0):
                self.interruptions = self.interruptions - 1
                try:
                    yield self.env.timeout(5)
                except simpy.Interrupt as interrupt:
                    self.interruptions += 1
                    print('interrupted at time %s interrupted: %s interrupted by: %s' % (self.env.now, self.isInterrupted, interrupt.cause))
            print('end at time %s' % (self.env.now))   
    
    
    class Interruptor(object):
    
        def __init__(self, env, interrupted):
            self.env = env
            self.interrupted = interrupted
            self.action = env.process(self.run(interrupted))
    
        def run(self, interrupted):
            for i in range(16):
                yield self.env.timeout(5)
                if(not interrupted.action.processed):
                    interrupted.action.interrupt("interrupt nr: %s" % i)
    
    env = simpy.Environment()
    interrupted = Interupted(env)
    interruptor = Interruptor(env, interrupted)
    env.run(until=100)