Неправильное использование ресурсов Simpy

#python #simpy

Вопрос:

В настоящее время я пытаюсь смоделировать хранилище в режиме дискретного моделирования событий, используя Simply. Из-за того, что мои знания python ограничены, я столкнулся с проблемой. Магазин ремонтирует сломанные продукты для своих клиентов, однако моя симуляция работает не так, как предполагалось. Я пытался найти аналогичную проблему с переполнением стека, но не смог ее найти, поэтому спрашиваю. Если аналогичный вопрос уже был решен, не могли бы вы предоставить ссылку?

Я моделирую магазин, в который клиенты приходят со своим сломанным товаром, кассир помогает им, меняя сломанный товар на отремонтированный. Клиент уходит с отремонтированным товаром, а сломанный товар позже ремонтируется в магазине. После ремонта его можно использовать для помощи другому клиенту.

Проблема, с которой я сталкиваюсь, заключается в узком месте моей симуляции, а именно в ремонте продукта. Когда запрашивается ремонтник, python немедленно предполагает, что запрошенный ремонтник занят. Однако в моей симуляции есть некоторые условия, которые необходимо выполнить, прежде чем ремонтник сможет приступить к ремонту. Одним из этих условий является то, что ремонтник должен быть доступен для ремонта продукта, однако, поскольку python предполагает, что ремонтник занят после запроса, процесс не может начаться, поскольку условие не может быть выполнено. Если бы я не запросил ремонтника до моего заявления while, очередь не была бы записана правильно, поэтому я решил сделать это таким образом.

Я предоставил уменьшенный фрагмент всего моего кода, в котором возникает проблема. В приведенном коде проблема возникает, когда необходимо отремонтировать третий сломанный продукт. Это связано с тем, что в симуляторе доступно 3 ремонтника. Если бы у меня было 8 ремонтников, проблема возникла бы с восьмым сломанным продуктом.

Есть ли способы решить или обойти мою проблему? Заранее спасибо.

 import simply
class Store(object):
def __init__(self, env, num_cashiers, num_repairman):
    self.env = env
    
    self.num_cashiers = num_cashiers
    self.num_repairman = num_repairman
    self.cashiers = simpy.Resource(env, capacity=num_cashiers)
    self.repairman = simpy.Resource(env, capacity=num_repairman)
    
    self.total_customers = 0
    self.broken_product = 0
    self.repaired_product = 25
    
    self.resource_data = []
    
    


def customer_generator(self, env):

    customer_buffer = 5
    product_id = 1
    customer_interarrival_time = 100
    
    while True: #always generate customers during simulation

        if len(self.cashiers.queue) >= customer_buffer:     #if customer queue is full, customer is lost
            self.lost_customers  =1
    
        else: #If queue not full, start the process in the store
           
            env.process(self.store_operations(env, product_id))

        yield env.timeout(customer_interarrival_time) #wait for next customer
        product_id =1
        self.total_customers  =1

def serve_customer(self, product_id):
    
    yield self.env.timeout(90)
    self.broken_product  =1
    self.repaired_product -=1
       
def repair_product(self, product_id):
    
    yield self.env.timeout(300)
    self.broken_product -=1
    self.repaired_product  =1

def store_operations(self, env, product_id):
    '''This function is where the problem persists
    While statement 1 exists the check if there are repaired products and a cashier available to help the customer
    otherwise timeout until both are available
    
    if statement 1 helps the customer by swapping a broken product for a repaired product
    
    while statement 2 exists to check if there are broken product to be repaired and whether a repairman is available
    otherwise timeout until both are available
    
    if statement 2 is where the repairman repairs a broken product'''
    
    # print(env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy cashiers', self.cashiers.count, ', cashier q', len(self.cashiers.queue))
    request_cashier = self.cashiers.request()
    yield request_cashier
    # print(env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy cashiers', self.cashiers.count, ', cashier q', len(self.cashiers.queue))
    while self.repaired_product==0 or self.cashiers.count==self.num_cashiers: 
        yield env.timeout(1)
    
    if self.repaired_product>0 and self.cashiers.count<self.num_cashiers:
        # print(env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy cashiers', self.cashiers.count, ', cashier q', len(self.cashiers.queue))
        yield env.process(self.serve_customer(product_id))
        self.cashiers.release(request_cashier)
        # print(env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy cashiers', self.cashiers.count, ', cashier q', len(self.cashiers.queue))

    print('product', product_id, 't before req', env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy repairmen', self.repairman.count, ', cashier q', len(self.repairman.queue))
    request_repair = self.repairman.request()
    yield request_repair                     
    print('product', product_id,'t after req', env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy repairmen', self.repairman.count, ', cashier q', len(self.repairman.queue))
    
    while self.broken_product==0 or self.repairman.count==self.num_repairman:
        yield env.timeout(1)
        print(env.now,'product', product_id,  'wait for repairman')         
    
    if self.broken_product>0 and self.repairman.count<self.num_repairman:

        print('product', product_id,'t before repair proces', env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy repairmen', self.repairman.count, ', cashier q', len(self.repairman.queue))
        yield env.process(self.repair_product(product_id))
        self.repairman.release(request_repair)
        print('product', product_id,'t after repair process',env.now, ', RP inv', self.repaired_product, ', BP inv', self.broken_product, ', busy repairmen', self.repairman.count, ', cashier q', len(self.repairman.queue))
    
    self.resource_data.append({
        'time' : env.now,
        'broken product inv' : self.broken_product,
        'repaired product inv': self.repaired_product,
        'busy cashiers' : self.cashiers.count,
        'cashier queue' : len(self.cashiers.queue),
        'busy repairmen' : self.repairman.count,
        'repairmen queue' : len(self.repairman.queue)})
    
    return self.resource_data


    env = simpy.Environment()

    store1 = Store(env, 3, 3)
    env.process(store1.customer_generator(env))


    env.run(500)


    print(store1.resource_data[0])
    print(store1.resource_data[1])
    print(store1.resource_data[2])
    print(store1.resource_data[3])
    print(store1.resource_data[4])
    print(store1.resource_data[5])
    print(store1.resource_data[6])


      
 

Ответ №1:

Я разбил это на два независимых процесса. Один из процессов-ремонтники, ремонтирующие сломанные лампы, которые появляются в их очереди на ремонт. Другой-кассир, который забирает у клиентов сломанные лампы и ставит их в очередь на ремонт, а также раздает отремонтированные лампы из фиксированной очереди. Таким образом, это показывает, как два независимых процесса могут использовать хранилища для передачи работы туда и обратно

 """
Quick simulation of a lamp exchange shop

Customer arrives with a broke lamp
If a repaired lamp is avalable, 
customer exchnage boken for repaired lamp and leaves
If a repaired lamp is not avalable, 
customer drops off boken lamp for repair and gets a raincheck returns later

Customer arrives with a raincheck after dropping of a broken lamp
if a repaired lamp is availale, customer takes repaired lamp and leaves
if a repaired lamp is not available customer returns later

When a broken lamp is droped off 
a resource pool of repair workers repairs the lamp and makes
the repaired lamp available for exchange

Programmer: Michael R. Gibbs
"""

import simpy
import random

class Lamp():
    """
    Quick class to collect stats on lamp rapair
    """

    lamp_cnt = 0 # used to gnerate unique lamp ids

    def __init__(self, state='broken'):
        """
        Init class vars, gen new id
        """

        Lamp.lamp_cnt  = 1
        self.lamp_id = Lamp.lamp_cnt
        self.state = state # possible states: broken, repaired

class Lamp_Store():
    """
    Models the serving of customers with cashiers
    and the repairing of broken lamps with repair workers
    """
    
    def __init__(self, env, num_cashieres=2, num_repair_workers=3, starting_lamp_stock=2, max_queue_len=5):
        """
        Sets up the resurce pools and stock of repaired lamps
        """

        self.env = env

        self.max_queue_len = max_queue_len

        # cashiers that service customers with lamps
        self.cashiers = simpy.Resource(env, capacity=num_cashieres)

        # start repair workers repairing lamps (save for future enchancements)
        self.reqair_workers = [env.process(self.repair_lamps(id   1)) for id in range(num_repair_workers)]

        # stores for broken and repair lamps
        self.broken_lamps = simpy.Store(env)
        self.repaired_lamps = simpy.Store(env)


        # sead the store of repaired lamps with some lamps
        self.repaired_lamps.items = [Lamp(state='repaired') for _ in range(starting_lamp_stock)]

    def customer_arrives(self, customer):
        """
        Start the processing of the customer by
        putting them in a queue for a cashier
        """

        if len(self.cashiers.queue) >= self.max_queue_len:
            # line too long, lost customer
            print(f'{self.env.now} customer {customer.customer_id} has arrived queue has {len(self.cashiers.queue)} customers, too long, leaving')

        else:
            while customer.lamp is None or customer.lamp.state == 'broken':
                # still need a repaired lamp

                print(f'{self.env.now} customer {customer.customer_id} has arrived queue has {len(self.cashiers.queue)} customers')

                # chekc queue size
                if len(self.cashiers.queue) >= self.max_queue_len:
                    # queue too long
                    # come back later
                    print(f'{self.env.now} customer {customer.customer_id} line too long, come back later')
                else:
                    # get service
                
                    # still needs a repaired lamp
                    yield self.env.process(self.cashier_serves_customer(customer))
                
                if customer.lamp is None or customer.lamp.state == 'broken':
                    # need to come back for lamp
                    yield self.env.timeout(random.randint(5,15))
                    print(f'{self.env.now} customer {customer.customer_id} return to shop')

    def cashier_serves_customer(self, customer):
        """
        wait in queue for cashier
        exchange lamp, or get raincheck
        """

        # wait for next available cashier
        with self.cashiers.request() as req:
            yield req

            # got cashier
            if customer.lamp is not None:
                #drop off broken lamp
                yield env.timeout(random.randint(4,10))
                lamp = customer.lamp
                self.broken_lamps.put(customer.lamp)
                customer.lamp = None
                print(f'{self.env.now} customer {customer.customer_id} dropped off borken lamp {lamp.lamp_id}')

            if len(self.repaired_lamps.items) > 0:
                # have a repaired lamp to exchnge
                customer.lamp = yield self.repaired_lamps.get()
                print(f'{self.env.now} customer {customer.customer_id} got repaired lamp {customer.lamp.lamp_id}')

            else:
                # no lamps, rain check
                customer.has_raincheck = True

    def repair_lamps(self, worker_id):
        """
        Models a repair worker wainting for a broken lamp
        and fixing it
        """

        while True:
            # wait for next borken lamp
            lamp = yield self.broken_lamps.get()
            print(f'{self.env.now} worker {worker_id} has started to repair lamp {lamp.lamp_id}')

            # fix broken lamp
            yield self.env.timeout(int(random.triangular(5,14,15))   1)

            # make fixed lamp available for exchange
            lamp.state = 'repaired'

            self.repaired_lamps.put(lamp)
            print(f'{self.env.now} worker {worker_id} has finished repairing lamp {lamp.lamp_id}')

            
class Customer():
    """
    Customer trying to exchange a broken lamp
    """
    customer_cnt = 0

    def __init__(self):

        Customer.customer_cnt  = 1
        self.customer_id = Customer.customer_cnt

        self.lamp = Lamp(state='broken')

def gen_customers(env,store):
    """
    Generate customers with broken lamps and send to store
    """

    while True:
        yield env.timeout(random.randint(1,3))
        cust = Customer()

        env.process(store.customer_arrives(cust))


# boot up the simulation
env = simpy.Environment()
store = Lamp_Store(env)    

env.process(gen_customers(env, store))

env.run(200)