#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)