Search code examples
pythonwhile-loopconcurrencygeneratorsimpy

in python, how can i run concurrent generator loops that pause or terminate when one of them yields?


i want to model the maintenace process on a piece of production equipment. this machine may be down for maintenace due to one of three reasons, namely (i) it may breakdown according to an exponentially distributed probability described by expovariate of 1/MTBF, where MTBF is the mean time between failures, (ii) or it may eventually become due for a time based service as per defined schedule, say after 4, 13, 26 or 52 weeks since the previous service, the servicing of which re-sets the breakdown probability to start all over again (iii) or it may be taken down for routine inspections and any arising repair or restoration works following the findings of the inspections, which again resets the breakdowns expovariate function. is it possible to set up three while True loops that will run their respective timeout functions concurrently but all stop as soon as any one of the three processes actually times out? preferably within the simpy environment? something like...

         
    import simpy
    import random

    mtbf = 150 #hours
    service_interval = 600 #hours
    inspect_repair_interval = 300 #hours


    def run_to_failure():
        while True:
            failed = random.expovariate(1/mtbf)
            yield env.timeout(failed)
        
    def run_to_service():
        while True:
            stopped = service_interval
            yield env.timeout(stopped)
        
    def run_to_repair():
        while True:
            paused = inspect_repair_interval
            yield env.timeout(paused)

now, how do i run all 3 functions concurrently but be able to stop and reset all once any one of them yields? the yielded value will serve as an input to subsequent processes, and which of those subsequent processes run also depends on which function yielded the value


Solution

  • Here is a example of a machine with three process running at the same time: the main machine task, a loop of regular maintenance, and breakdown events

    """
    quick sim of a machine with breakdowns
    
    programmer: Michael R. Gibbs
    """
    
    import simpy
    import random
    
    class Machine():
        """
        A machine with a main processing loop that can be
        interupted with breakdowns
        """
    
        def __init__(self, env):
            """
            Initializes the machine and starts
            processing and breadowns
            """
    
            self.env = env
    
            # start main task
            self.startup()
    
            # start schedule for service
            self.service = self.env.process(self.wait_for_service())
    
        def startup(self):
            """
            Starts up the machine
            Also starts up the breakdown schedule
            """
            self.task = self.env.process(self.main_task())
            self.breakdown = self.env.process(self.wait_for_breakdown())
    
        def shut_down(self):
            """
            Shuts down the machine
            Also kills the pending breakdown
            """
            
            if self.task is not None:
                # only shutdown if not running
                self.task.interrupt()
                self.task = None
    
    
            if self.breakdown is not None:
                # only cancel pending breakdown if it is still pending
                self.breakdown.interrupt()
    
        def main_task(self):
            """
            The main processing loop of the machine
            """
    
            print(f'{self.env.now} - starting up machine')
    
            try:
                while True:
                    yield self.env.timeout(3)
                    print(f'{self.env.now} - did some work')
            except simpy.Interrupt as i:
                print(f'{self.env.now} - processing has been interupted')
    
            print(f'{self.env.now} - shutting down machine')
    
        def wait_for_service(self):
            """
            Schedules and executes maintaince 
    
            runs on a loop
            breakdowns do not delay maintenance schedule
            """
            while True:
                yield self.env.timeout(21)
                # time for maintenance
                print(f'{self.env.now} - time for service')
    
                if self.task is None:
                    # already processing a breakdown
                    print( "-------already shutdown ")
    
                else:
                    self.shut_down()
    
                    # do maintenance
                    yield self.env.timeout(2)
                    print(f'{self.env.now} - finished service')
    
                    # restart machaine
                    self.startup()
    
        def wait_for_breakdown(self):
            """
            Creates a one time breakdown
            """
    
            print(f'{self.env.now} - breakdow clock started')
    
            try:
    
                yield self.env.timeout(random.randint(4,10))
                # time for a breakdown
    
                self.breakdown = None
    
                print(f'{self.env.now} - breakdown')
    
                # shutdown and fix machine
                self.shut_down()
    
                yield self.env.timeout(7)
    
                # machine fixed, start it back up
                print(f'{self.env.now} - breakdow fixed')
    
                self.startup()
            except simpy.Interrupt as i:
                print(f'{self.env.now} - breakdow clock stopped')
    
    
    
    env = simpy.Environment()
    machine = Machine(env)
    env.run(100)