본문 바로가기
Python

[Simulation] SimPy 패키지로 스타벅스 예제 만들기 (2)

by 하응 2021. 8. 29.

지난 포스팅에서 SimPy를 활용하여, 매우 간단한 Simulator를 만들어보았다. 

2021.08.29 - [Python] - [Simulation] SimPy 패키지로 스타벅스 예제 만들기 (1)

 

[Simulation] SimPy 패키지로 스타벅스 예제 만들기 (1)

이번 시간에는 스타벅스 시뮬레이션 예제를 만들어보며 SimPy 패키지 활용법을 알아보고자 한다. * 그냥 카페 시뮬레이션인데, 스타벅스 예제라고 이름 붙였다. (●'◡'●) 1. Python SimPy 패키지 프

studying-haeung.tistory.com

 

이번에는 카페 상황을 약간 바꾸고, 지난 번에는 Customer 클래스 안에 모든 내용을 다 넣었다면, 이번에는 Customer, Activity, Simulator 클래스를 따로 작성하여 내용을 구분해보려고 한다. 

 


 

1. Starbucks-02 시뮬레이션 예제 구현

 

Starbucks-02 예제의 상황은 아래와 같으며, 지난 번처럼 VSCode로 구현하였다. 
지난 번 구현했던 Starbucks-01 예제와 달리 '음료를 제조하는 바리스타 직원의 수'를 1명으로 제한하였다. 

 

  • 고객의 프로세스
    • 고객은 10초에 1명씩 총 10명이 도착한다. 
    • 도착한 고객은 주문받는 직원이 있는 경우, 30초 동안 커피를 주문한다. 
    • 고객이 주문을 완료하면, 음료를 제조하는 직원은 30초 동안 커피를 준비한다. 
      (음료를 제조하는 직원이 앞 고객의 커피를 준비하고 있으면, 음료 제조는 그만큼 지연된다.)
    • 커피가 완성되면 고객은 커피를 들고 카페를 나간다.  
  • 카페 상황 
    • 주문을 받는 직원은 2명 뿐이며, 음료를 제조하는 바리스타 직원은 1명 뿐이다. 
    • 주문 받는 직원이 없는 경우와 음료 제조 직원이 없는 경우, 고객은 기다려야 한다. 
2.1 Customer 클래스 
class Customer(object):
    def __init__(self, env, number):
        self.env = env
        self.number = number 
        #분포에 따라 customer 도착
        self.action = env.process(Activity(self.env).customer_generate(self.number))

Customer라는 클래스는 env 객체와, 고객의 총 인원을 의미하는 number 값을 입력받는다.
Customer 객체가 생성되면 Activity 클래스의 customer_generate 함수가 수행된다. 

 

 

2.2 Activity 클래스

class Activity(object):
    def __init__(self, env):
        self.env = env


    def customer_generate(self, number):
        for i in range(number):
            name = 'Customer-%s'%i      
            print(name, '%8.3f' %self.env.now,'카페도착')

            #도착한 고객은 주문하러 이동 
            self.env.process(self.order_coffee(name, simulator.counter))

            interval_time = 10
            yield self.env.timeout(interval_time)


    def order_coffee(self, name, counter):
        #카운터 직원 요청 
        with counter.request() as req:
            yield req

            #직원에게 30초동안 주문 
            ordering_duration = 30
            yield self.env.timeout(ordering_duration)
            print(name, '%8.3f'%self.env.now, '주문완료')
            
        #주문한 고객은 커피 수령을 위해 대기
        yield self.env.process(self.wait_for_coffee(name, simulator.barista))


    def wait_for_coffee(self, name, barista):
        #바리스타 직원 요청 
        with barista.request() as req:
            yield req

            #30초 커피 제조 
            waiting_duration = 30
            yield(self.env.timeout(waiting_duration))
            print(name, '%8.3f' %self.env.now,'커피수령')

고객 행동과 관련된 함수들을 Activity 클래스로 따로 구분했으며, customer_generate, order_coffee, wait_for_coffee 함수 자체는 01 예제와 유사하다. 
wait_for_coffee 함수에 바리스타 직원을 요청하는 부분을 추가하여, 1건의 주문 건을 완료한 후에, 다음 주문을 받아 음료를 제조하는 상황을 표현했다. (바리스타가 1명 뿐이라 1건씩 처리)

 

 

2.3 Simulator 클래스

class Simulator(object):
    def __init__(self, name):
        self.name = name 
        self.env = simpy.Environment()
        
        #카운터 직원 2명 
        self.counter = simpy.Resource(self.env, capacity=2)
        #바리스타 직원 1명
        self.barista = simpy.Resource(self.env, capacity=1)
        self.customer = Customer(self.env, 10)


    def run(self):
        print(self.name)
        self.env.run()

이번에는 아예 Simulator 클래스도 따로 구분하여 작성했다.
env 객체를 생성하고, 각 Activity에 대한 자원(여기서는 직원)을 생성하는 부분, Customer 클래스 객체를 생성하는 부분으로 구성되어 있으며, 실제 시뮬레이션을 수행하는 run() 함수도 정의했다. 

 

 

2.4 Simulator 실행하는 부분 

simulator = Simulator('Starbucks_Example-02')
simulator.run()

앞에 Simulator 클래스를 작성해두었기 때문에, 실행하는 부분의 코드가 간단해졌다. 

 

 

2.5 전체 코드 및 수행 결과 

import simpy 

class Customer(object):
    def __init__(self, env, number):
        self.env = env
        self.number = number 
        #분포에 따라 customer 도착
        self.action = env.process(Activity(self.env).customer_generate(self.number))


class Activity(object):
    def __init__(self, env):
        self.env = env


    def customer_generate(self, number):
        for i in range(number):
            name = 'Customer-%s'%i      
            print(name, '%8.3f' %self.env.now,'카페도착')

            #도착한 고객은 주문하러 이동 
            self.env.process(self.order_coffee(name, simulator.counter))
            
            interval_time = 10
            yield self.env.timeout(interval_time)


    def order_coffee(self, name, counter):
        #카운터 직원 요청 
        with counter.request() as req:
            yield req

            #직원에게 30초동안 주문 
            ordering_duration = 30
            yield self.env.timeout(ordering_duration)
            print(name, '%8.3f'%self.env.now, '주문완료')
            
        #주문한 고객은 커피 수령을 위해 대기
        yield self.env.process(self.wait_for_coffee(name, simulator.barista))


    def wait_for_coffee(self, name, barista):
        #바리스타 직원 요청 
        with barista.request() as req:
            yield req

            #30초 커피 제조 
            waiting_duration = 30
            yield(self.env.timeout(waiting_duration))
            print(name, '%8.3f' %self.env.now,'커피수령')


class Simulator(object):
    def __init__(self, name):
        self.name = name 
        self.env = simpy.Environment()
        #카운터 직원 2명 
        self.counter = simpy.Resource(self.env, capacity=2)
        #바리스타 직원 1명
        self.barista = simpy.Resource(self.env, capacity=1)
        self.customer = Customer(self.env, 10)


    def run(self):
        print(self.name)
        self.env.run()


simulator = Simulator('Starbucks-scenario-01')
simulator.run()
반응형

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

 

 

3. Starbucks-02 시뮬레이션 예제 수행 결과 정리 

 

 

고객 ID별로 각 단계를 언제 수행했는지, 다음 단계까지 얼마나 소요되었는지 표로 정리하였다. 주문 완료 단계 테이블의 (s1), (s2)는 주문 받는 직원의 ID를 의미하고, 커피 수령 단계의 (b1)은 음료 제조 직원의 ID를 의미한다. 

 

바리스타 직원이 음료 제조에 걸리는 시간은 30초인데, Customer-1 고객부터 50초 이상씩 소요되는 것을 확인할 수 이다. 이는 바리스타 b1 직원이 Customer-0 고객의 커피를 준비하고 있기 때문이며, 바리스타 직원은 Customer-0 고객의 음료 제조가 끝난 시점 (60.0) 부터 Customer-1 고객의 커피를 준비하게 된다.  

* b1 직원이 얼마나 힘들까....😢

 

첫 번째 고객 Customer-0은 커피를 받기까지 총 60초가 걸렸는데, 마지막 고객 Customer-9는 커피를 받기까지 총 140초가 걸렸다. 첫 번째 고객보다 2배 넘게 기다린 셈이다.... 

 

 


 

 

이번 Starbucks_Example-02 예제를 구현하며, 생소하던 클래스 개념을 다시 정리해 볼 수 있었다. 다음에는 더 확장된 모델 예제를 구현해보고 포스팅할 것이다. 

* 코드 작성은 좀 어렵지만, 시뮬레이션이 돌아가면서 원하는 로그가 결과 값으로 찍힐 때 쾌감이 엄청난 것 같다! 블로그 글도 술술 적히는 것 같고.. 아무튼 좋다 💃💃💃

 

 

 

반응형

댓글