GT1992
GT1992

Reputation: 119

How can I ensure that my simulation starts a new "time to failure" countdown only after the repair is complete?

I am creating a simulation of a 2 machine system with a buffer in between with fixed processing speeds, failure rate and maintenance speeds (later I will change this to having some sort of distribution, but for now baby steps first). The steps are as follows:

I have gotten quite far with all of the above points accounted for. The only thing is that the failures keep counting the failure time per multiple of the unit I give it and it does not start counting after the machine has been fixed again. So it might break at 7, is fixed at 11 but then breaks at 14 again instead of 11+7=18.

It seems like the failure is just on an infinite loop. Does anyone know how I can change this? My code is as follows:

import simpy
import random

# Machine 1
speed_1 = 2          # Avg. processing time of Machine 1 in minutes
# speed_1_stdev = 0.6  # St. dev. of processing time of Machine 1
MTTF_1 = 10          # Mean time to failure Machine 1
# fail_1 = 1/MTTF_1    # Parameter for exp. distribution
repair_1 = 3         # Time it takes to repair Machine 1

# Machine 2
speed_2 = 3          # Processing time of Machine 2 in minutes
# speed_2_stdev = 0.6  # St. dev. of processing time of Machine 2
MTTF_2 = 7           # Mean time to failure Machine 1
# fail_2 = 1/MTTF_2    # Parameter for exp. distribution
repair_2 = 4         # Time it takes to repair Machine 2

# Simulation time
time = 120           # Sim time in minutes

#---------------------------------------------------------------------
# Class setup for a Machine
class Machine(object):
    """
    A machine produces units at a fixed processing speed, 
    takes units from a store before and puts units into a store after.

    Machine has a *name*, a processing speed *speed*, a preceeding buffer *in_q*,
    and a proceeding buffer *out_q*.
    
    Next steps: 
    - Machine produces units at distributed processing speeds.
    - A machine fails at fixed intervals and is repaired at a fixed time.
    - Failure and repair times are distributed.
    """
    def __init__(self, env, name, in_q, out_q, speed, mttf, repair):
        self.env = env
        self.name = name
        self.in_q = in_q
        self.out_q = out_q
        self.speed = speed
        self.mttf = mttf
        self.repair = repair
        self.broken = False

        # Start the producing process
        self.process = env.process(self.produce())
        # Start the failure process
        env.process(self.fail_machine())
    
    def produce(self):
        """
        Produce parts as long as the simulation runs.
        """
        while True:
            part = yield self.in_q.get()
            try:
                # If want to see time {self.env.now:.2f} 
                print(f'{self.name} has got a part')

                yield env.timeout(self.speed)
                if len(self.out_q.items) < self.out_q.capacity:
                    print(f'{self.name} finish a part next buffer has {len(self.out_q.items)} and capacity of {self.out_q.capacity}')
                else:
                    print(f'{self.env.now:.2f}  {self.name} output buffer full!!!')

                yield self.out_q.put(part)
                print(f'{self.name} pushed part to next buffer')
            
            except simpy.Interrupt:
                self.broken = True
                yield self.env.timeout(self.repair)
                print(f'{self.env.now:.2f} {self.name} is in fixed')
                self.broken = False
        
    def fail_machine(self):
        """
        The machine is prone to break down every now and then.
        """
        while True:
            yield self.env.timeout(self.mttf)
            print(f'{self.env.now:.2f} {self.name} is in failure.')
            if not self.broken:
                # Machine only fails if currently working.
                self.process.interrupt(self.mttf)
#---------------------------------------------------------------------
# Generating the arrival of parts in the entry buffer to be used by machine 1
def gen_arrivals(env, entry_buffer):
    """
    Start the process for each part by putting
    the part in the starting buffer
    """
    while True:
        yield env.timeout(random.uniform(0,0.001))
        # print(f'{env.now:.2f} part has arrived')
        part = object() # Too lazy to make a real part class, also isn't necessary

        yield entry_buffer.put(part)

#---------------------------------------------------------------------
# Create environment and start the setup process
env = simpy.Environment()
bufferStart = simpy.Store(env)  # Buffer with unlimited capacity
buffer1 = simpy.Store(env, capacity = 8) # Buffer between machines with limited capacity
bufferEnd = simpy.Store(env)  # Last buffer with unlimited capacity

# The machines __init__ starts the machine process so no env.process() is needed here
machine_1 = Machine(env, 'Machine 1', bufferStart, buffer1, speed_1, MTTF_1, repair_1)
machine_2 = Machine(env, 'Machine 2', buffer1, bufferEnd, speed_2, MTTF_2, repair_2)

env.process(gen_arrivals(env, bufferStart))

# Execute
env.run(until = time)

Upvotes: 0

Views: 122

Answers (1)

Michael
Michael

Reputation: 1969

changed the fail_machine method from a infinite loop to generate just the next breakdown. fail_machine still needs to be called from the init startup, but also now needs to be called from the end of the except block that handles the breakdown, restarting the countdown to next failure.

import simpy
import random

# Machine 1
speed_1 = 2          # Avg. processing time of Machine 1 in minutes
# speed_1_stdev = 0.6  # St. dev. of processing time of Machine 1
MTTF_1 = 10          # Mean time to failure Machine 1
# fail_1 = 1/MTTF_1    # Parameter for exp. distribution
repair_1 = 3         # Time it takes to repair Machine 1

# Machine 2
speed_2 = 3          # Processing time of Machine 2 in minutes
# speed_2_stdev = 0.6  # St. dev. of processing time of Machine 2
MTTF_2 = 7           # Mean time to failure Machine 1
# fail_2 = 1/MTTF_2    # Parameter for exp. distribution
repair_2 = 4         # Time it takes to repair Machine 2

# Simulation time
time = 120           # Sim time in minutes

#---------------------------------------------------------------------
# Class setup for a Machine
class Machine(object):
    """
    A machine produces units at a fixed processing speed, 
    takes units from a store before and puts units into a store after.

    Machine has a *name*, a processing speed *speed*, a preceeding buffer *in_q*,
    and a proceeding buffer *out_q*.
    
    Next steps: 
    - Machine produces units at distributed processing speeds.
    - A machine fails at fixed intervals and is repaired at a fixed time.
    - Failure and repair times are distributed.
    """
    def __init__(self, env, name, in_q, out_q, speed, mttf, repair):
        self.env = env
        self.name = name
        self.in_q = in_q
        self.out_q = out_q
        self.speed = speed
        self.mttf = mttf
        self.repair = repair
        self.broken = False

        # Start the producing process
        self.process = env.process(self.produce())
        # Start the failure process
        env.process(self.fail_machine())
    
    def produce(self):
        """
        Produce parts as long as the simulation runs.
        """
        while True:
            part = yield self.in_q.get()
            try:
                # If want to see time {self.env.now:.2f} 
                print(f'{self.name} has got a part')

                yield env.timeout(self.speed)
                if len(self.out_q.items) < self.out_q.capacity:
                    print(f'{self.name} finish a part next buffer has {len(self.out_q.items)} and capacity of {self.out_q.capacity}')
                else:
                    print(f'{self.env.now:.2f}  {self.name} output buffer full!!!')

                yield self.out_q.put(part)
                print(f'{self.name} pushed part to next buffer')
            
            except simpy.Interrupt:
                self.broken = True
                yield self.env.timeout(self.repair)
                print(f'{self.env.now:.2f} {self.name} is in fixed')
                self.broken = False

                # change to restart next fail here instead of in a infinite loop
                self.env.process(self.fail_machine())
        
    def fail_machine(self):
        """
        The machine is prone to break down every now and then.
        """
    
        # no longer a loop, relies on breakdow logic to restart
        yield self.env.timeout(self.mttf)
        print(f'{self.env.now:.2f} {self.name} is in failure.')
        if not self.broken:
            # Machine only fails if currently working.
            self.process.interrupt(self.mttf)

#---------------------------------------------------------------------
# Generating the arrival of parts in the entry buffer to be used by machine 1
def gen_arrivals(env, entry_buffer):
    """
    Start the process for each part by putting
    the part in the starting buffer
    """
    while True:
        yield env.timeout(random.uniform(0,0.001))
        # print(f'{env.now:.2f} part has arrived')
        part = object() # Too lazy to make a real part class, also isn't necessary

        yield entry_buffer.put(part)

#---------------------------------------------------------------------
# Create environment and start the setup process
env = simpy.Environment()
bufferStart = simpy.Store(env)  # Buffer with unlimited capacity
buffer1 = simpy.Store(env, capacity = 8) # Buffer between machines with limited capacity
bufferEnd = simpy.Store(env)  # Last buffer with unlimited capacity

# The machines __init__ starts the machine process so no env.process() is needed here
machine_1 = Machine(env, 'Machine 1', bufferStart, buffer1, speed_1, MTTF_1, repair_1)
machine_2 = Machine(env, 'Machine 2', buffer1, bufferEnd, speed_2, MTTF_2, repair_2)

env.process(gen_arrivals(env, bufferStart))

# Execute
env.run(until = time)

Upvotes: 0

Related Questions