Search code examples
processresourcessimpy

Model single activity requiring different resource types


Process 3 requires both resources A and B, and can only start after process 1 (by resource A) and process 2 (by resource B) have been completed. The code below seems to perform as desired, but the if / else statement looks very inelegant. Is there a 'better' way of accomplishing the same behavior?

def orchestration(self):
    # Request resources A and B
    req_resourceA = self.resourceA.request()
    req_resourceB = self.resourceB.request()

    # Wait until first resource is available (will change throughout simulation)
    available_rec = yield.self.env.any_of([req_resourceA, req_resourceB])

    if list(available_rec.keys())[0] == resourceA:
        proc1 = self.env.process(self.process1())
        yield resourceB
        proc2 = self.env.process(self.process2())
    else:
        proc2 = self.env.process(self.process2())
        yield resourceA
        proc1 = self.env.process(self.process1())
  
    # Start process 3 only after processes 1 and 2 have been completed
    yield proc1 & proc2
    yield self.env.timeout(process3_time)

    # Manually release both resource requests
    self.resourceA.release(req_resourceA)
    self.resourceB.release(req_resourceB)

Solution

  • In this solution I run process 1 and 2 in parallel instead of in sequence and used a all_of to wait for both processes to finish. This should reduce some idle time for the resources. Process 1 and 2 also grabs the resource they need, but do not release it. Instead they return the request so it can be passed to process 3. I pass the resource requests to process 3 thinking process 3 would know when to release each resource. for example process 3 could release resource A, do some more stuff, then release resource B

    """
    One way steps can be sequcenced
    
    In this example the pre steps are run in parallel 
    rather the sequentially
    
    Programmer: Michael R. Gibbs
    """
    
    from urllib import request
    import simpy
    import random
    
    def process1(env, resPool):
        """
        Simple process that grabs a resource and does some processing
    
        The resource request is not released, but returned to the calling process
        """
    
        resRequest = resPool.request()
    
        yield resRequest
    
        print(f'{env.now:0.2f} process 1 has resource and started')
    
        yield env.timeout(random.randint(1,5))
    
        print(f'{env.now:0.2f} process 1 has finish')
    
        return resRequest
        
    def process2(env, resPool):
        """
        another simple process that grabs a resource and does some processing
    
        The resource request is not released, but returned to the calling process
        """
    
        resRequest = resPool.request()
    
        yield resRequest
    
        print(f'{env.now:0.2f} process 2 has resource and started')
    
        yield env.timeout(random.randint(1,5))
    
        print(f'{env.now:0.2f} process 2 has finish')
    
        return resRequest
    
    def process3(env, resourceAPool, requestA, resourceBPool, requestB):
        """
        Final process that reuses resouces from process1 and process2
        which this process is dependant on
        """
    
        print(f'{env.now:0.2f} process 3 started')
    
        yield env.timeout(random.randint(1,5))
    
        resourceAPool.release(requestA)
        resourceBPool.release(requestB)
    
        print(f'{env.now:0.2f} process 3 finish')
    
    def endToEndProcessing(env, resourceAPool, resourceBPool):
        """
            the end to end processes
            where process1 and process2 must complete before process 3 can start
        """
    
        while True:
            # start each pre process, but do not yeild so they runn in parallel
            proc1 = env.process(process1(env,resourceAPool))
            proc2 = env.process(process2(env,resourceBPool))
    
            # wait for both processes to finish
            preProcesses = yield env.all_of([proc1, proc2])
    
            # get resource requests and pass to process 3
            reqA = preProcesses[proc1]
            reqB = preProcesses[proc2]
    
            yield env.process(process3(env, resourceAPool, reqA, resourceBPool, reqB))
    
    
    env = env = simpy.Environment()
    resourceAPool = simpy.Resource(env, capacity=1)
    resourceBPool = simpy.Resource(env, capacity=1)
    
    env.process(endToEndProcessing(env, resourceAPool, resourceBPool))
    
    env.run(100)