# ===========================================================================
# Copyright 2013 University of Limerick
#
# This file is part of DREAM.
#
# DREAM is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DREAM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with DREAM. If not, see <http://www.gnu.org/licenses/>.
# ===========================================================================
"""
Created on 6 Feb 2013
@author: George
"""
# from SimPy.Simulation import now, Process, Resource, infinity, waituntil, waitevent
import simpy
from manpy.simulation.core.CoreObject import CoreObject
# ===========================================================================
# The exit object
# ===========================================================================
[docs]
class Exit(CoreObject):
"""
models the exit of the model
"""
family = "Exit"
def __init__(self, id, name, cost=0, cancelCondition={}, **kw):
self.type = "Exit" # XXX needed ?
self.isNext = True
self.isPrevious = False
# lists to hold statistics of multiple runs
self.Exits = []
self.UnitExits = []
self.Lifespan = []
self.TaktTime = []
# if input is given in a dictionary
CoreObject.__init__(self, id, name, cost)
from .Globals import G
G.ExitList.append(self)
self.cancelCondition = cancelCondition
[docs]
def initialize(self):
# using the Process __init__ and not the CoreObject __init__
CoreObject.initialize(self)
# initialize the internal Queue (type Resource) of the Exit
self.Res = simpy.Resource(self.env, capacity=float("inf"))
# The number of resource that exited through this exit.
# XXX bug: cannot output as json when nothing has exited.
self.numOfExits = 0
self.totalNumberOfUnitsExited = 0
self.totalLifespan = 0
self.entities = []
self.totalTaktTime = 0 # the total time between to consecutive exits
self.intervalThroughPutList = []
self.expectedSignals["isRequested"] = 1
[docs]
def run(self):
while 1:
# wait until the Queue can accept an entity and one predecessor requests it
self.expectedSignals["isRequested"] = 1
yield self.isRequested
self.isRequested = self.env.event()
# TODO: insert extra controls to check whether the self.giver attribute is correctly updated
self.entities.append(self.getEntity())
self.signalGiver()
[docs]
def defineRouting(self, predecessorList=[]):
"""sets the routing in element for the Exit"""
self.previous = predecessorList # no successorList for the Exit
[docs]
def defineNext(self, successorList=[]):
"""Safeguarding for mistakenly trying to set the next element on an Exit object"""
print("Trying to set successors for Exits does not have any effects.")
[docs]
def canAccept(self, callerObject=None):
"""checks if the Exit can accept an entity"""
return True # the exit always can accept an entity
[docs]
def canAcceptAndIsRequested(self, callerObject=None):
"""checks if the Exit can accept an entity and there is an entity waiting for it"""
# get the active object and its internal queue
activeObject = self.getActiveObject()
activeObjectQueue = self.getActiveObjectQueue()
giverObject = callerObject
assert giverObject, "there must be a caller for canAcceptAndIsRequested"
return giverObject.haveToDispose(self)
[docs]
def getEntity(self):
"""gets an entity from the predecessor"""
activeEntity = CoreObject.getEntity(self) # run the default method
# add to cost of entity
activeEntity.cost += self.cost
# if the entity is in the G.pendingEntities list then remove it from there
from .Globals import G
# G.pendingEntities[:]=(entity for entity in G.pendingEntities if not entity is activeEntity)
if G.RouterList:
if activeEntity in G.pendingEntities:
G.pendingEntities.remove(activeEntity)
# if activeEntity in G.EntityList:
# G.EntityList.remove(activeEntity)
# self.clear(activeEntity)
self.totalLifespan += (
self.env.now - activeEntity.startTime
) # Add the entity's lifespan to the total one.
self.numOfExits += 1 # increase the exits by one
self.totalNumberOfUnitsExited += (
activeEntity.numberOfUnits
) # add the number of units that xited
self.totalTaktTime += (
self.env.now - self.timeLastEntityLeft
) # add the takt time
self.timeLastEntityLeft = (
self.env.now
) # update the time that the last entity left from the Exit
activeObjectQueue = self.getActiveObjectQueue()
del self.Res.users[:]
# if there is a cancelCondition the exit may end the simulation
if self.cancelCondition:
if (
self.cancelCondition.get("reason", None) == "throughput"
and int(self.cancelCondition.get("number", -1)) == self.numOfExits
):
self.endSimulation()
if (
self.cancelCondition.get("reason", None) == "empty"
and self.checkIfSystemEmpty()
):
self.endSimulation()
return activeEntity
[docs]
@staticmethod
def clear(entity):
from .Globals import G
def deleteEntityfromlist(entity, list):
if entity in list:
list.remove(entity)
lists = (G.EntityList, G.PartList, G.pendingEntities, G.WipList)
# lists=(G.EntityList, G.PartList, G.BatchList, G.SubBatchList,
# G.JobList, G.OrderList, G.OrderComponentList, G.MouldList,
# G.pendingEntities, G.WipList)
for list in lists:
deleteEntityfromlist(entity, list)
[docs]
def haveToDispose(self, callerObject=None):
"""haveToDispose of an exit must always return False"""
return False
[docs]
def postProcessing(self, MaxSimtime=None):
"""actions to be taken after the simulation ends"""
from .Globals import G
if MaxSimtime == None:
MaxSimtime = G.maxSimTime
# hold the numberOfExits of each replication
self.Exits.append(self.numOfExits)
self.UnitExits.append(self.totalNumberOfUnitsExited)
try: # throw exception in case the numOfExits is zero
self.Lifespan.append(((self.totalLifespan) / self.numOfExits) / G.Base)
except ZeroDivisionError: # the lifespan in this case is zero
self.Lifespan.append(0)
try: # throw exception in case of zero division
self.TaktTime.append(((self.totalTaktTime) / self.numOfExits) / G.Base)
except ZeroDivisionError: # the average time between exits is zero if no Entity exited
self.TaktTime.append(0)
[docs]
def outputResultsJSON(self):
"""outputs results to JSON File"""
from .Globals import G
json = {
"_class": "manpy.%s" % self.__class__.__name__,
"id": self.id,
"family": self.family,
"results": {},
}
json["results"]["throughput"] = self.Exits
json["results"]["lifespan"] = self.Lifespan
json["results"]["takt_time"] = self.TaktTime
if (
self.Exits != self.UnitExits
): # output this only if there was variability in units
json["results"]["unitsThroughput"] = self.UnitExits
G.outputJSON["elementList"].append(json)