""" cellphone_OO.py

Simulate the operation of a BCC cellphone system.

Calls arrive at random to a cellphone hub with a fixed number of
channels. Service times are assumed exponential. The objective
is to determine the statistics of busy periods in the operation of a
BCC cellphone system.

The required measurements are
(1) the total busy time (all channels full) in each 1-hour period and
(2) the total number of busy times in a 1-hour period.

The simulation is continuous but the observing Process, a
Statistician, breaks the time into 1-hour observation periods
separated by 15-minute gaps to reduce autocorrelation. The total busy
time and number of busy times in each interval is printed.

   """
from SimPy.Simulation import *
import random as ran


## Model components ------------------------

class CallSource(Process):
 """ generates a sequence of calls """    
      
 def execute(self, maxN, lam,cell):
    for i in range(maxN):
         j = Call("Call%03d"%(i,),sim=self.sim)
         self.sim.activate(j,j.execute(cell))
         yield hold,self,ran.expovariate(lam)


class Call(Process):
    """ Calls arrive at random at the cellphone hub"""
    def execute(self,cell):
        self.trace("arrived")
        if cell.Nfree == 0: self.trace("blocked and left")
        else:
             self.trace("got a channel")
             cell.Nfree -=  1
             if cell.Nfree == 0:
                 self.trace("start busy period======")
                 cell.busyStartTime = self.sim.now()
                 cell.totalBusyVisits += 1
             yield hold,self,ran.expovariate(mu)
             self.trace("finished")
             if cell.Nfree == 0:
                 self.trace("end   busy period++++++")
                 cell.busyEndTime = self.sim.now()
                 busy = self.sim.now() - cell.busyStartTime
                 self.trace("         busy  = %9.4f"%(busy,))
                 cell.totalBusyTime +=busy
             cell.Nfree += 1

    def trace(self,message):
         if TRACING:
             print "%7.4f %13s %s "%(self.sim.now(), message, self.name)
  
class Cell:
    """ Holds global measurements"""
    Nfree   = 0
    totalBusyTime   = 0.0
    totalBusyVisits = 0
    result=()
    
class Statistician(Process):
     """ observes the system at intervals """
     
     def execute(self,Nperiods,obsPeriod,obsGap,cell):
         cell.busyEndTime = self.sim.now() # simulation start time
         if STRACING: print "Busy time Number"
         for i in range(Nperiods):
             yield hold,self,obsGap
             cell.totalBusyTime = 0.0
             cell.totalBusyVisits = 0
             if cell.Nfree == 0: cell.busyStartTime = self.sim.now()
             yield hold,self,obsPeriod
             if cell.Nfree == 0: cell.totalBusyTime += self.sim.now()-cell.busyStartTime
             if STRACING:
                 print "%7.3f %5d"%(cell.totalBusyTime,cell.totalBusyVisits)
             self.sim.m.tally(cell.totalBusyTime)
             self.sim.bn.tally(cell.totalBusyVisits)
         self.sim.stopSimulation()
         cell.result= (self.sim.m.mean(),self.sim.m.var(),self.sim.bn.mean(),self.sim.bn.var())

## Experiment data -------------------------

NChannels =  4         # number of channels in the cell
maxN    = 10000
ranSeed = 3333333
lam = 1.0              # per minute
mu = 0.6667            # per minute
Nperiods  =  10
obsPeriod = 60.0       # minutes
obsGap    = 15.0       # gap between observation periods

TRACING  = False
STRACING = True

## Model -----------------------------------
class CellphoneModel(Simulation):
    def run(self):
        self.initialize()
        self.m = Monitor(sim=self)
        self.bn =Monitor(sim=self)
        ran.seed(ranSeed)
        self.cell = Cell()           # the cellphone tower
        self.cell.Nfree   = NChannels
        s = Statistician('Statistician',sim=self)
        self.activate(s,s.execute(Nperiods,obsPeriod,obsGap,self.cell))
        g = CallSource('CallSource',sim=self)
        self.activate(g,g.execute(maxN, lam,self.cell))
        self.simulate(until=10000.0)
        
## Experiment ------------------------------
modl=CellphoneModel()
modl.run()

## Output ----------------------------------
print 'cellphone'
# input data:
print "lambda    mu      s  Nperiods obsPeriod  obsGap"
FMT= "%7.4f %6.4f %4d   %4d      %6.2f   %6.2f"
print FMT%(lam,mu,NChannels,Nperiods,obsPeriod,obsGap)

sr = modl.cell.result
print "Busy Time:   mean = %6.3f var= %6.3f"%sr[0:2]
print "Busy Number: mean = %6.3f var= %6.3f"%sr[2:4]
