#!/usr/bin/env python

# MdMd1Alt.py

# Example of SimPy for discrete-time situations.

# Same model as in MdMd1.py:

# "M/M/1 queue," but in discrete time.  Time is slotted.  At each time
# slot, if the server is busy at the beginning of a time slot, it
# finishes that job at the end of that slot with probability p.  At the
# start of each slot, a new arrival occurs with probability q.   A job
# which arrives in a given slot and finds an idle server will start
# service right away.

# However, a different approach:

# Here, instead of having Server as a Process class, we have it as a
# Resource.  We have a new Process class, Job, and each time a job
# arrives, a Job object is created which then uses the Server Resource.

from __future__ import generators  # delete if use Python >= 2.3 
from SimPy.Simulation import *
from random import Random,expovariate,uniform

import sys

class Arrivals(Process):
   def __init__(self):
      Process.__init__(self) 
   def Run(self):
      while 1:
         if Globs.Rnd.random() < Globs.Q:
            # set up object for new arrival
            self.J = Job(now())
            # need to record it somewhere, or the object will be
            # garbage-collected when we next assign self.J
            Globs.Jbs.append(self.J)
            activate(self.J,self.J.Run(),delay=0.0)
         yield hold,self,1.0

class Job(Process):
   JbMon = Monitor()
   def __init__(self,Start):
      Process.__init__(self) 
      self.StartTime = Start
   def Run(self):
      # try to get served right away, otherwise queue
      yield request,self,Globs.Server
      # OK, the server is ours, start service
      while 1:
         yield hold,self,1.0
         if Globs.Rnd.random() < Globs.P:
            break
      # job done, do bookkeeping
      Job.JbMon.observe(now() - self.StartTime)
      yield release,self,Globs.Server
      del self

class Globs:
   Rnd = Random(12345)
   P = float(sys.argv[1])
   Q = float(sys.argv[2])
   Server = Resource(1)
   Arrs = Arrivals()
   Jbs = []  # list of current jobs

# "main" program starts here

def main():
   initialize()  
   activate(Globs.Arrs,Globs.Arrs.Run(),delay=0.0)
   MaxSimtime = 10000.0
   simulate(until=MaxSimtime)
   print Job.JbMon.count(), "jobs"
   print "mean residence time", Job.JbMon.mean()

if __name__ == '__main__':
   main()
