-1

I am trying to make resources unavailable for a certain time in simpy. The issue is with timeout I find the resource is still active and serving during the time it should be unavailable. Can anyone help me with this in case you have encountered such a problem. Thanks a lot!

import numpy as np
import simpy

def interarrival():
    return(np.random.exponential(10))

def servicetime():
    return(np.random.exponential(20))


def servicing(env, servers_1):
    i = 0
    while(True):
        i = i+1
        yield env.timeout(interarrival())
        print("Customer "+str(i)+ " arrived in the process at "+str(env.now))
        state = 0
        env.process(items(env, i, servers_array, state))

def items(env, customer_id, servers_array, state):
    with servers_array[state].request() as request:
        yield request
        t_arrival = env.now
        print("Customer "+str(customer_id)+ " arrived in "+str(state)+ "  at "+str(t_arrival))
        yield env.timeout(servicetime())
        t_depart = env.now
        print("Customer "+str(customer_id)+ " departed from "+str(state)+ "  at "+str(t_depart))
        if (state == 1):
            print("Customer exists")
        else:
            state = 1
            env.process(items(env, customer_id, servers_array, state))

def delay(env, servers_array):
    while(True):
        if (env.now%1440 >= 540 and env.now <= 1080):
            yield(1080 - env.now%1440)
        else:
            print(str(env.now), "resources will be blocked")
            resource_unavailability_dict = dict()
            resource_unavailability_dict[0] = []
            resource_unavailability_dict[1] = []
            for nodes in resource_unavailability_dict:
                for _ in range(servers_array[nodes].capacity):
                    resource_unavailability_dict[nodes].append(servers_array[nodes].request())
            print(resource_unavailability_dict)
            for nodes in resource_unavailability_dict:
                yield env.all_of(resource_unavailability_dict[nodes])
            if (env.now < 540):
                yield env.timeout(540)
            else:
                yield env.timeout((int(env.now/1440)+1)*1440+540 - env.now)
            for nodes in resource_unavailability_dict:
                for request in resource_unavailability_dict[nodes]:
                    servers_array[nodes].release(request)
            print(str(env.now), "resources are released")
            

env = simpy.Environment()
servers_array = []
servers_array.append(simpy.Resource(env, capacity = 5))
servers_array.append(simpy.Resource(env, capacity = 7))
env.process(servicing(env, servers_array))
env.process(delay(env,servers_array))
env.run(until=2880)

The code is given above. Actually, I have two nodes 0 and 1 where server capacities are 5 and 7 respectively. The servers are unavailable before 9AM (540 mins from midnight) and after 6 PM everyday. I am trying to create the unavailability using timeout but not working. Can you suggest how do I modify the code to incorporate it.

I am getting the error AttributeError: 'int' object has no attribute 'callbacks'which I can't figure out why ?

2 Answers2

0

One way to do this is with preemptive resources. When it is time to make resources unavailable, issue a bunch of requests with the highest priority to seize idle resources, and to preempt resources currently in use. These requests would then release the resources when its time to make the resources available again. Note that you will need to add some logic on how the preempted processes resume once the resources become available again. If you do not need to preempt processes, you can just use priority resources instead of preemptive resources

Michael
  • 1,671
  • 2
  • 4
  • 8
0

So the problem with simpy resources is the capacity is a read only attribute. To get around this you need something to seize and hold the resource off line. So in essence, I have two types of users, the ones that do "real work" and the ones that control the capacity. I am using a simple resource, which means that the queue at the schedule time will get processed before the capacity change occurs. Using a priority resource means the current users of a resource can finish their processes before the capacity change occurs , or you can use a pre-emptive resource to interrupt users with resources at the scheduled time. here is my code

"""
one way to change a resouce capacity on a schedule
note the the capacity of a resource is a read only atribute

Programmer: Michael R. Gibbs
"""

import simpy
import random

def schedRes(env, res):
    """
    Performs maintenance at time 100 and 200

    waits till all the resources have been seized 
    and then spend 25 time units doing maintenace 
    and then release

    since I am using a simple resource, maintenance
    will wait of all request that are already in 
    the queue when maintenace starts to finish

    you can change this behavior with a priority resource 
    or pre-emptive resource
    """

    # wait till first scheduled maintenance
    yield env.timeout(100)

    # build a list of requests for each resource
    # then wait till all requests are filled
    res_maint_list = []
    print(env.now, "Starting maintenance")
    for _ in range(res.capacity):
        res_maint_list.append(res.request())

    yield env.all_of(res_maint_list)
    print(env.now, "All resources seized for maintenance")

    # do maintenance
    yield env.timeout(25)
    print(env.now, "Maintenance fisish")

    # release all the resources
    for req in res_maint_list:
        res.release(req)

    print(env.now,"All resources released from maint")

    # wait till next scheduled maintenance
    dur_to_next_maint = 200 -env.now

    if dur_to_next_maint > 0:
        yield env.timeout(dur_to_next_maint)

    # do it all again
    res_maint_list = []
    print(env.now, "Starting maintenance")
    for _ in range(res.capacity):
        res_maint_list.append(res.request())

    yield env.all_of(res_maint_list)
    print(env.now, "All resources seized for maintenance")

    yield env.timeout(25)
    print(env.now, "Maintenance fisish")

    for req in res_maint_list:
        res.release(req)

    print(env.now,"All resources released from maint")


def use(env, res, dur):
    """
    Simple process of a user seizing a resource 
    and keeping it for a little while
    """
    with res.request() as req:
        print(env.now, f"User is in queue of size {len(res.queue)}")
        yield req
        print(env.now, "User has seized a resource")
        yield env.timeout(dur)
    print(env.now, "User has released a resource")

def genUsers(env,res):
    """
    generate users to seize resources
    """
    while True:
        yield env.timeout(10)
        env.process(use(env,res,21))
    
# set up 
env = simpy.Environment()
res = simpy.Resource(env,capacity=2) # may want to use a priority or preemtive resource
env.process(genUsers(env,res))
env.process(schedRes(env, res))

# start
env.run(300)
Michael
  • 1,671
  • 2
  • 4
  • 8
  • Thanks a lot Michael. This is gold. Let me try this out myself and also in my context. In the meantime, can you please give me an example of priority or preemptive resource as well in the form of a code blob. – Coding Practice Nov 20 '21 at 01:53
  • replace: res = simpy.Resource(env,capacity=2) with: res = simpy.PriorityResource(env, capacity=2) in the user procedure replace with res.request() as req: with with resource.request(priority=2) as req: and in schedRes replace both res_maint_list.append(res.request()) with res_maint_list.append(res.request(priority=1)) – Michael Nov 20 '21 at 02:30
  • if you like the answer can you select it as correct? – Michael Nov 20 '21 at 02:31
  • Hi Michael, I am getting the error AttributeError: 'int' object has no attribute 'callbacks' when I run the modified code given in the body of the question based on your suggestion. Can you please look at it once. – Coding Practice Nov 22 '21 at 10:24