#!/usr/bin/env python

# Mach3.py

# SimPy example:  Variation of Mach1.py, Mach2.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, and she is not summoned
# until both machines are down.

# 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
   NextID = 0  # next available ID number for Machine objects
   NUp = 0  # number of machines currently up
   def __init__(self):
      Process.__init__(self)  
      self.StartUpTime = 0.0  # time the current up period started
      self.ID = Machine.NextID   # ID for this Machine object
      Machine.NextID += 1
      Machine.NUp += 1
   def Run(self):
      print "starting machine", self.ID
      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(UpRate)
         # update up time total
         Machine.TotalUpTime += now() - self.StartUpTime
         # update number of up machines
         Machine.NUp -= 1
         # if only one machine down, then wait for the other to go down
         if Machine.NUp == 1:
            yield passivate,self
         # here is the case in which we are the second machine down; but
         # we must distinguish between two cases:  (a) the other machine
         # was waiting for this machine to go down, or (b) the other
         # machine is in the process of being repaired (so it shouldn't
         # be reactiviated)
         elif RepairPerson.n == 1:
            reactivate(M[1-self.ID])
         # now go to repair
         yield request,self,RepairPerson
         yield hold,self,Rnd.expovariate(RepairRate)
         Machine.NUp += 1
         yield release,self,RepairPerson

# "main" program starts here

UpRate = 1/1.0
RepairRate = 1/0.5
Rnd = Random(12345)
RepairPerson = Resource(1)
initialize()  

M = []
for I in range(2):
   M.append(Machine())
   activate(M[I],M[I].Run(),delay=0.0)

MaxSimtime = 10000.0
simulate(until=MaxSimtime)

# print results
print "proportion of up time was:" 
print Machine.TotalUpTime/(2*MaxSimtime)

