# simulates one cell in a cellular phone network; a major highway runs
# through it, so handoff calls come in at random times but stay active
# for a constant time, the time it takes to drive through the cell (in
# this simpler model, we assume that a handoff call lasts the entire
# time the car is in the cell); calls also originate at random times
# from local people, who stay in the cell; for them, calls last a random
# time; a call is dropped, not queued, if a channel is not available
# модель имитирует одну ячейку в сети сотовой связи - соту; 
# через эту соту проходит крупная автомагистраль, поэтому звонки поступают в произвольное время,
# но остаются активными на постоянное время- необходимое для прохождения через ячейку
# (в этой упрощенной модели мы предполагаем, что такой вызов длится всё
# время нахождения автомобиля в соте); вызовы также исходят в случайное время
# от местных жителей, которые остаются в зоне соты; для них звонки длятся случайное
# время; вызов отбрасывается, а не ставится в очередь, если канал недоступен.

# использование:
# python Cell.py HRate DrvTime LRate LDurRate NChn NRsrvd MaxSimTime

# where:
# HRate = rate of arrivals of handoff calls (reciprocal of mean time between arrivals)
# DrvTime = drive-through time for the cell
# LRate = rate of creations of local calls (reciprocal of mean time between creations)
# LDurRate = reciprocal of mean duration of local calls
# NChn = number of channels
# NRsrvd = number of channels reserved for handoff calls
# MaxSimtime = amount of time to simulate

import sys,random
import simpy

class Globals:
    Rnd = random.Random(2347)
    NArrv = {'handoff':0,'local':0} # numbers of calls arrived
    NRej = {'handoff':0,'local':0} # numbers of calls rejected

class Cell:
    NChn = None
    NRsrvd = None
    FreeChannels = None
    NNoLocalsAllowedPeriods = 0
    TimeNoLocalsAllowed = 0.0
    LatestStartNoLocalsAllowed = None
    
    def GrabAChannel(envnow):
       Cell.FreeChannels -= 1 # grab the channel
       if Cell.FreeChannels == Cell.NRsrvd:
          Cell.LatestStartNoLocalsAllowed = envnow

    GrabAChannel = staticmethod(GrabAChannel)

    def ReleaseAChannel(envnow):
       Cell.FreeChannels += 1 # release the channel
       if Cell.FreeChannels == Cell.NRsrvd+1:
          Cell.NNoLocalsAllowedPeriods += 1
          Cell.TimeNoLocalsAllowed += envnow - Cell.LatestStartNoLocalsAllowed
          Cell.LatestStartNoLocalsAllowed = None

    ReleaseAChannel = staticmethod(ReleaseAChannel)

def ArrivalProc(env,Type,Arri,Dura):
   while True:
      TimeToNextArrival = Globals.Rnd.expovariate(Arri)
      yield env.timeout(TimeToNextArrival)
      Globals.NArrv[Type] += 1
      proc = env.process(CalleR(env,Type,Dura))                        

def CalleR(env,Typ,Dur): # simulates one call
   if (Typ == 'handoff' and Cell.FreeChannels == 0) or \
      (Typ == 'local' and Cell.FreeChannels <= Cell.NRsrvd):
      Globals.NRej[Typ] += 1
      return
   else: 
      Cell.GrabAChannel(env.now)
      if Typ == 'handoff':
         CallTime = Dur
      else: # ’local’
         CallTime = Globals.Rnd.expovariate(Dur)
      yield env.timeout(CallTime) # the call runs its course
      Cell.ReleaseAChannel(env.now)
      print('Free Channels =%d in time-%d' % (Cell.FreeChannels,env.now))
                         
def main():
    HRate = 1 #float(sys.argv[1])
    DrvTime = 22 #float(sys.argv[2])
    LRate = 1 #float(sys.argv[3])
    LDurRate = 33 #float(sys.argv[4])
    Cell.NChn = 22 #int(sys.argv[5])
    Cell.FreeChannels = Cell.NChn
    Cell.NRsrvd = 2 #int(sys.argv[6])
    MaxSimtime = 100 #float(sys.argv[7])
    print ('SwitchedRate=%d,SwDuration=%d,LocalRate=%d,LocalDuration=%d,SimTime=%d' %(HRate,DrvTime,LRate,LDurRate,MaxSimtime))
    
    env = simpy.Environment() #initialize
    procDrv = env.process(ArrivalProc(env,'handoff',HRate,DrvTime))
    procLoc = env.process(ArrivalProc(env,'local',LRate,LDurRate))
    env.run(until=MaxSimtime)  #simulate

    print ('percentage of rejected handoff calls:', Globals.NRej['handoff']/float(Globals.NArrv['handoff']))
    print ('percentage of rejected local calls:', Globals.NRej['local']/float(Globals.NArrv['local']))
    print ('mean banned period for locals:', Cell.TimeNoLocalsAllowed/float(Cell.NNoLocalsAllowedPeriods))

if __name__ == '__main__': main()
