#!/usr/bin/env python

# Mach2.py

# SimPy example:  Variation of Mach1.py.  Two machines, but sometimes
# break down.  Up time is exponentially distributed with mean 1.0, and
# repair time is exponentially distributed with mean 0.5.  In this
# example, there is only one repairperson, so the two machines cannot be
# repaired simultaneously if they are down at the same time.

# In addition to finding the long-run proportion of up time as in
# Mach1.py, let's also find the long-run proportion of the time that a
# given machine does not have immediate access to the repairperson when
# the machine breaks down (note:  collection of both of these statistics
# could be facilitated by using SimPy's monitor capability, though we
# haven't done so here).  Output values should be about 0.6 and 0.5.

# A SimPy construct used here but not in Mach1.py is Resource, which
# sets up queuing.

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

class Machine(Process):
   TotalUpTime = 0.0  # total up time for all machines
   NRep = 0  # number of times the machines have broken down
   NImmedRep = 0  # number of breakdowns in which the machine 
                  # started repair service right away
   UpRate = 1/1.0  # breakdown rate
   RepairRate = 1/0.5  # repair rate
   # the following two variables are not actually used, but are useful
   # for debugging purposes
   NextID = 0  # next available ID number for Machine objects
   NUp = 0  # number of machines currently up
   def __init__(self):
      Process.__init__(self)  # required in any Process subclass
      self.StartUpTime = 0.0  # time the current up period stated
      self.ID = Machine.NextID   # ID for this Machine object
      Machine.NextID += 1
      Machine.NUp += 1
   def Run(self):
      # start simulation of this machine, beginning in the up mode
      while 1:
         # record current time, now(), so can see how long machine is up
         self.StartUpTime = now()  
         # hold for exponentially distributed up time
         yield hold,self,Rnd.expovariate(Machine.UpRate)
         # update uptime total
         Machine.TotalUpTime += now() - self.StartUpTime
         # update number of breakdowns
         Machine.NRep += 1
         # to check whether we get repair service immediately, look at
         # n, a member variable of Resource class, which states how many
         # items in this resource (we only have one here) are currently
         # available 
         if RepairPerson.n == 1:
            Machine.NImmedRep += 1
         # need to request, and possibly queue for, the repairperson
         yield request,self,RepairPerson
         # OK, we've obtained access to the repairperson; now
         # hold for exponentially distributed repair time
         yield hold,self,Rnd.expovariate(Machine.RepairRate)
         # release the repairperson
         yield release,self,RepairPerson

# "main" program starts here

Rnd = Random(12345)

# create the repairperson 
RepairPerson = Resource(1)

initialize()  # required SimPy statement

# set up the two machine processes
for I in range(2):
   M = Machine()
   activate(M,M.Run(),delay=0.0)

MaxSimtime = 10000.0
simulate(until=MaxSimtime)

# print results
print "proportion of up time:" 
print Machine.TotalUpTime/(2*MaxSimtime)
print "proportion of times repair was immediate:"
print float(Machine.NImmedRep)/Machine.NRep

