import random
import DESlib as des
#
# Global variables used during simulation
#
L = 0               # Accumulated number of customers in system, use to calculate E(L)
nOfCustomers = 0    # Number of of customers in system at current time
lostCustomers = 0   # Accumulated number of lost customers
c = 0               # number of customers beeing served

def onArrival():
#
# When a customer arrives, nothing will happen if the system is full, i.e., nOfCustomers == N,
# this will be a lost customer
#
# If there are free servers, i.e., c < C, the arriving customer is getting served
#
# Else nothing will hapen
#
# Independent of the system status, a new customer is put in the pending event set
#
    global L, nOfCustomers, lostCustomers, c
    L += nOfCustomers * des.getElapsedTime()    
    if nOfCustomers == N:
        lostCustomers += 1
    else:
        nOfCustomers += 1
        if c < C:
            
            c += 1 # Number of customers beeing served           
            des.eventNotice (random.expovariate(mu) + des.getClock(), "onServiceCompleted")        
    des.eventNotice(random.expovariate(lmbda) + des.getClock(), "onArrival") # next customer
    

def onServiceCompleted():
#
# When a service is completed, the number of customers in the system is reduced by one
# The actual server goes idle (for some seconds), but starts another service if there is a customer not beeing served
#
# Requirements for starting another service are:
# - There are customers in the system, i.e., nOfCustomers > 0
# - There are more customers not being served than there are occupied servers, i.e., nOfCustomers > c 
#
    global L, nOfCustomers, lostCustomers, c
    L +=  nOfCustomers * des.getElapsedTime() 
    nOfCustomers -= 1
    c -= 1
    if nOfCustomers > 0: # There are customers in the system
        if nOfCustomers > c: # There are customers not beeing served, proceed with the next customer in the queue
            des.eventNotice( random.expovariate(mu) + des.getClock(), "onServiceCompleted")
            c += 1   
    
def getL(): # Average number of customers in the system
    return L / des.getClock()

#
# Main program
#
lmbda = 60/15 # customers per hour
mu = 60/25    # service rate
N = 5 # max number in system
C = 3 # number of servers
import __main__ as onEventLib  # callbacks are defined in the main program
des.initPES(onEventLib)
des.eventNotice(random.expovariate(lmbda) , "onArrival") # Draw the first customer
maxTime = 100000 # Increase to get higher precision
while des.getClock() < maxTime:
    des.execute( des.getNxtEvent() )
print("Mean number of customers in system: " , L / des.getClock())
print("Lost customer", lostCustomers/des.getClock())
