Source code for manpy.simulation.core.CoreObject

# ===========================================================================
# 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/>.
# ===========================================================================


from manpy.simulation.core.Globals import G
from manpy.simulation.ManPyObject import ManPyObject


[docs] class CoreObject(ManPyObject): """ Created on 12 Jul 2012 @author: George Class that acts as an abstract. It should have no instances. All the core-objects should inherit from it :param id: Internal Id :param name: Name of the CoreObject """ class_name = "manpy.CoreObject" def __init__(self, id, name, cost=0, **kw): ManPyObject.__init__(self, id, name) self.objName = name # lists that hold the previous and next objects in the flow self.next = [] # list with the next objects in the flow self.previous = [] # list with the previous objects in the flow self.nextIds = [] # list with the ids of the next objects in the flow self.previousIds = [] # list with the ids of the previous objects in the flow self.isNext = True self.isPrevious = True # lists to hold statistics of multiple runs self.Failure = [] self.Working = [] self.Blockage = [] self.Waiting = [] self.OffShift = [] self.WaitingForOperator = [] self.WaitingForLoadOperator = [] self.Loading = [] self.SettingUp = [] self.OnBreak = [] # flag that locks the entry of an object so that it cannot receive entities self.isLocked = False # list that holds the objectInterruptions that have this element as victim self.objectInterruptions = [] # list that holds the objectProperties that have this elements as victim self.objectProperties = [] # default attributes set so that the CoreObject has them self.isPreemptive = False self.resetOnPreemption = False self.interruptCause = None self.gatherWipStat = False # flag used to signal that the station waits for removeEntity event self.waitEntityRemoval = False # attributes/indices used for printing the route, # hold the cols corresponding to the object (entities route and operators route) self.station_col_inds = [] self.op_col_indx = None # if there is input in a dictionary parse from it G.ObjList.append(self) # add object to ObjList # list of expected signals of a station # (values can be used as flags to inform on which signals is the station currently yielding) self.expectedSignals = { "isRequested": 0, "canDispose": 0, "interruptionStart": 0, "interruptionEnd": 0, "loadOperatorAvailable": 0, "initialWIP": 0, "brokerIsSet": 0, "preemptQueue": 0, "entityRemoved": 0, "entityCreated": 0, "moveEnd": 0, "processOperatorUnavailable": 0, "objectPropertyEnd" : 0 } # flag notifying the the station can deliver entities that ended their processing while interrupted self.canDeliverOnInterruption = False # keep wip stats for every replication self.WipStat = [] # set the cost of going through the object self.cost = cost
[docs] def initialize(self): self.env = G.env self.Up = True # Boolean that shows if the object is in failure ("Down") or not ("up") self.onShift = True self.onBreak = False self.currentEntity = None # ============================== total times =============================================== self.totalOperationTime = 0 # dummy variable to hold totalWorkin/SetupTime during an interruption (yield ...(self.operation('setup')) self.totalBlockageTime = 0 # holds the total blockage time self.totalFailureTime = 0 # holds the total failure time self.totalWaitingTime = 0 # holds the total waiting time self.totalWorkingTime = 0 # holds the total working time self.totalBreakTime = 0 # holds the total break time self.totalOffShiftTime = 0 # holds the total off-shift time self.completedJobs = 0 # holds the number of completed jobs # ============================== Entity related attributes ================================= self.timeLastEntityEnded = ( 0 # holds the last time that an entity ended processing in the object ) self.nameLastEntityEnded = ( "" # holds the name of the last entity that ended processing in the object ) self.timeLastEntityEntered = ( 0 # holds the last time that an entity entered in the object ) self.nameLastEntityEntered = ( "" # holds the name of the last entity that entered in the object ) # ============================== shift related times ===================================== self.timeLastShiftStarted = ( 0 # holds the time that the last shift of the object started ) self.timeLastShiftEnded = ( 0 # holds the time that the last shift of the object ended ) self.offShiftTimeTryingToReleaseCurrentEntity = ( 0 # holds the time that the object was off-shift while trying ) # to release the current entity # ============================== failure related times ===================================== self.timeLastFailure = ( 0 # holds the time that the last failure of the object started ) self.timeLastFailureEnded = ( 0 # holds the time that the last failure of the object ended ) # processing the current entity self.downTimeInTryingToReleaseCurrentEntity = ( 0 # holds the time that the object was down while trying ) # to release the current entity . This might be due to failure, off-shift, etc self.timeLastEntityLeft = ( 0 # holds the last time that an entity left the object ) self.processingTimeOfCurrentEntity = ( 0 # holds the total processing time that the current entity required ) # ============================== waiting flag ============================================== self.waitToDispose = False # shows if the object waits to dispose an entity self.isWorkingOnTheLast = False # shows if the object is performing the last processing before scheduled interruption # ============================== the below are currently used in Jobshop ======================= self.giver = ( None # the CoreObject that the activeObject will take an Entity from ) if len(self.previous) > 0: self.giver = self.previous[0] self.receiver = ( None # the CoreObject that the activeObject will give an Entity to ) if len(self.next) > 0: self.receiver = self.next[0] # ============================== variable that is used for the loading of objects ============= self.exitAssignedToReceiver = None # by default the objects are not blocked # when the entities have to be loaded to operated objects # then the giverObjects have to be blocked for the time # that the object is being loaded # ============================== variable that is used signalling of objects ================== self.entryAssignedToGiver = None # by default the objects are not blocked # when the entities have to be received by objects # then the objects have to be blocked after the first signal they receive # in order to avoid signalling the same object # while it has not received the entity it has been originally signalled for # ============================== lists to hold statistics of multiple runs ===================== self.totalTimeWaitingForOperator = 0 self.operatorWaitTimeCurrentEntity = 0 self.totalTimeInCurrentEntity = 0 self.operatorWaitTimeCurrentEntity = 0 self.totalProcessingTimeInCurrentEntity = 0 # self.failureTimeInCurrentEntity=0 self.setupTimeCurrentEntity = 0 # the time that the object started/ended its wait for the operator self.timeWaitForOperatorStarted = 0 self.timeWaitForOperatorEnded = 0 # the time that the object started/ended its wait for the operator self.timeWaitForLoadOperatorStarted = 0 self.timeWaitForLoadOperatorEnded = 0 self.totalTimeWaitingForLoadOperator = 0 # the time that the operator started/ended loading the object self.timeLoadStarted = 0 self.timeLoadEnded = 0 self.totalLoadTime = 0 # the time that the operator started/ended setting-up the object self.timeSetupStarted = 0 self.timeSetupEnded = 0 self.totalSetupTime = 0 # Current entity load/setup/loadOperatorwait/operatorWait related times self.operatorWaitTimeCurrentEntity = ( 0 # holds the time that the object was waiting for the operator ) self.loadOperatorWaitTimeCurrentEntity = ( 0 # holds the time that the object waits for operator to load the it ) self.loadTimeCurrentEntity = 0 # holds the time to load the current entity self.setupTimeCurrentEntity = ( 0 # holds the time to setup the object before processing the current entity ) self.shouldPreempt = ( False # flag that shows that the object should preempt or not ) self.isProcessingInitialWIP = ( False # flag that is used only when a object has initial wip ) self.lastGiver = None # variable that holds the last giver of the object, used by object in case of preemption # initialize the wipStatList - # TODO, think what to do in multiple runs # TODO, this should be also updated in Globals.setWIP (in case we have initial wip) import numpy as np self.wipStatList = np.array([[0, 0]]) self.isRequested = self.env.event() self.canDispose = self.env.event() self.interruptionEnd = self.env.event() self.interruptionStart = self.env.event() self.interruptedBy = None self.entityRemoved = self.env.event() self.initialWIP = self.env.event() # flag used to signal that the station waits for removeEntity event self.waitEntityRemoval = False # attributes/indices used for printing the route, hold the cols corresponding to the object (entities route and operators route) self.station_col_inds = [] self.op_col_indx = None # flag that shows if the object is processing state at any given time self.isProcessing = False # variable that shows what kind of operation is the station performing at the moment """ it can be Processing or Setup XXX: others not yet implemented """ self.currentlyPerforming = None # flag that shows if the object is blocked state at any given time self.isBlocked = False self.timeLastBlockageStarted = None # list of expected signals of a station (values can be used as flags to inform on which signals is the station currently yielding) self.expectedSignals = { "isRequested": 0, "canDispose": 0, "interruptionStart": 0, "interruptionEnd": 0, "loadOperatorAvailable": 0, "initialWIP": 0, "brokerIsSet": 0, "preemptQueue": 0, "entityRemoved": 0, "entityCreated": 0, "moveEnd": 0, "processOperatorUnavailable": 0, "objectPropertyEnd" : 0 } # lists that keep the start/endShiftTimes of the victim self.endShiftTimes = [] self.startShiftTimes = []
[docs] def run(self): """ the main process of the core object, this is dummy, every object must have its own implementation :return: None """ raise NotImplementedError("Subclass must define 'run' method")
[docs] def defineRouting(self, predecessorList=[], successorList=[]): """ sets the routing in and out elements for the Object :param predecessorList: List containing the predecessor Objects :param successorList: List containing the successor Objects :return: None """ self.next = successorList self.previous = predecessorList
[docs] def defineNext(self, successorList=[]): """ sets the next element for the object and automatically registers itself as previous object of all objects in successorList. :param successorList: :return: None """ for s in successorList: if s.isNext: # checks if s can be a next object. e.g., exit cannot be a next object # __get_routing_target() is used to handle CoreObjects and ProductionLineModules differently if s.getRoutingTarget() not in self.next: self.next.extend(s.getRoutingTarget()) s.appendPrevious(self)
[docs] def definePrevious(self, predecessorList=[]): """Sets self.previous""" self.previous = predecessorList
[docs] def appendPrevious(self, previous): """Append previous to self.previous""" # first checks if previous can be a previous object # e.g., source cannot be a previous object if previous.isPrevious and previous not in self.previous: self.previous.append(previous) else: print(f"Registering {previous.name} as previous in {self.name} failed.")
[docs] def getRoutingTarget(self): """Returns the object. This method is used for dynamic routing in order to handle CoreObjects and ProductionLineModules differently""" return [self]
[docs] def printRouting(self): # print(f"{self.name}", end="") # # print("") # # for s in self.next: # s.printRouting() pass
[docs] def initialSignalReceiver(self): """ checks if there is anything set as WIP at the begging of the simulation and sends an event to initialize the simulation """ if self.haveToDispose(): self.signalReceiver()
[docs] def initialAllocationRequest(self): # TODO if the station is operated, and the operators have skills defined then the SkilledOperatorRouter should be signalled # XXX: there may be a case where one object is not assigned an operator, in that case we do not want to invoke the allocation routine if self.checkForDedicatedOperators(): allocationNeeded = False from .Globals import G for obj in G.MachineList: if obj.operatorPool != "None": if obj.operatorPool.operators: allocationNeeded = False break else: allocationNeeded = True if allocationNeeded: self.requestAllocation()
[docs] def removeEntity(self, entity=None, resetFlags=True, addBlockage=True): """ removes an Entity from the Object the Entity to be removed is passed as argument by getEntity of the receiver :param entity: :param resetFlags: :param addBlockage: :return: """ if addBlockage and self.isBlocked: # add the blocking time self.addBlockage() # reset flags if resetFlags: self.isBlocked = False self.isProcessing = False # add to cost of entity entity.cost += self.cost activeObjectQueue = self.Res.users activeObjectQueue.remove(entity) # remove the Entity from the queue if self.receiver: self.receiver.appendEntity(entity) self.downTimeInTryingToReleaseCurrentEntity = 0 self.offShiftTimeTryingToReleaseCurrentEntity = 0 self.timeLastEntityLeft = self.env.now self.outputTrace(entity.name, entity.id, "Left " + str(self.id)) # append the time to schedule so that it can be read in the result # remember that every entity has it's schedule which is supposed to be updated every time # he entity enters a new object if entity.schedule: entity.schedule[-1]["exitTime"] = self.env.now # update wipStatList if self.gatherWipStat: import numpy wip = 0 for holdEntity in activeObjectQueue: wip += holdEntity.numberOfUnits self.wipStatList = numpy.concatenate( (self.wipStatList, [[self.env.now, wip]]) ) if self.expectedSignals["entityRemoved"]: self.printTrace(self.id, signal="(removedEntity)") self.sendSignal(receiver=self, signal=self.entityRemoved) return entity
[docs] def appendEntity(self, entity=None): """ appends entity to the receiver object. to be called by the removeEntity of the giver this method is created to be overridden by the Assembly class in its getEntity where Frames are loaded """ activeObjectQueue = self.Res.users activeObjectQueue.append(entity)
[docs] def identifyEntityToGet(self): """ called be getEntity it identifies the Entity to be obtained so that getEntity gives it to removeEntity as argument """ giverObjectQueue = self.getGiverObjectQueue() return giverObjectQueue[0]
[docs] def addBlockage(self): """ adds the blockage time to totalBlockageTime each time an Entity is removed """ if self.timeLastBlockageStarted: self.totalBlockageTime += self.env.now - self.timeLastBlockageStarted
[docs] def getEntity(self): """ gets an entity from the giver """ # get active object and its queue, as well as the active (to be) entity # (after the sorting of the entities in the queue of the giver object) # activeObject=self.getActiveObject() activeObjectQueue = self.Res.users # get giver object, its queue, and sort the entities according to this object priorities giverObject = self.giver giverObject.sortEntities() # sort the Entities of the giver # according to the scheduling rule if applied giverObject.sortEntitiesForReceiver(self) giverObjectQueue = giverObject.Res.users # if the giverObject is blocked then unBlock it if giverObject.exitIsAssignedTo(): giverObject.unAssignExit() # if the activeObject entry is blocked then unBlock it if self.entryIsAssignedTo(): self.unAssignEntry() activeEntity = self.identifyEntityToGet() activeEntity.currentStation = self # update the receiver of the giverObject giverObject.receiver = self # remove entity from the giver activeEntity = giverObject.removeEntity(entity=self.identifyEntityToGet()) # variable that holds the last giver; used in case of preemption self.lastGiver = self.giver # #get the entity from the previous object and put it in front of the activeQ # activeObjectQueue.append(activeEntity) # append the time to schedule so that it can be read in the result # remember that every entity has it's schedule which is supposed to be updated every time # the entity enters a new object activeEntity.schedule.append({"station": self, "entranceTime": self.env.now}) # update variables activeEntity.currentStation = self self.timeLastEntityEntered = self.env.now self.nameLastEntityEntered = ( activeEntity.name ) # this holds the name of the last entity that got into object # update the next list of the object self.updateNext(activeEntity) self.outputTrace(activeEntity.name, activeEntity.id, "Entered " + str(self.id)) self.printTrace(activeEntity.name, enter=self.id) # # if there are entities with requiredParts then check whether the requirements are fulfilled for them to proceed # # ass soon as a "buffer" receives an entity it controls if the entity is requested elsewhere, # # then it checks if there other requested entities by the same requesting entity. # # Finally, it is controlled whether all the requested parts have concluded # # their sequences for the requesting entity # from Globals import G # # for all the entities in the EntityList # for entity in G.EntityList: # requiredParts=entity.getRequiredParts() # if requiredParts: # # if the activeEntity is in the requierdParts of the entity # if activeEntity in requiredParts: # # if the entity that requires the activeEntity can proceed then signal the currentStation of the entity # if entity.checkIfRequiredPartsReady() and entity.currentStation.expectedSignals['canDispose']: # entity.mayProceed=True # self.sendSignal(receiver=entity.currentStation, signal=entity.currentStation.canDispose) # update wipStatList if self.gatherWipStat: import numpy wip = 0 for holdEntity in activeObjectQueue: wip += holdEntity.numberOfUnits self.wipStatList = numpy.concatenate( (self.wipStatList, [[self.env.now, wip]]) ) return activeEntity
[docs] def updateNext(self, entity=None): """ updates the next list of the object """ pass
[docs] def preemptReceiver(self): """ check whether there is a critical entity to be disposed and if preemption is required """ activeObjectQueue = self.Res.users # find a critical order if any critical = False for entity in activeObjectQueue: if entity.isCritical: activeEntity = entity critical = True break if critical: # pick a receiver receiver = None if any( object for object in self.next if object.isPreemptive and object.checkIfActive() ): receiver = next( object for object in self.next if object.isPreemptive and object.checkIfActive() ) # if there is any receiver that can be preempted check if it is operated if receiver: receiverOperated = False # local variable to inform if the receiver is operated for Loading try: from .MachineJobShop import MachineJobShop from .MachineManagedJob import MachineManagedJob # TODO: implement preemption for simple machines if ( receiver.operatorPool and isinstance(receiver, MachineJobShop) or isinstance(receiver, MachineManagedJob) ): # and the operationType list contains Load, the receiver is operated if (receiver.operatorPool != "None") and any( type == "Load" for type in receiver.multOperationTypeList ): receiverOperated = True except: pass # if the obtained Entity is critical and the receiver is preemptive and not operated # in the case that the receiver is operated the preemption is performed by the operators # if the receiver is not Up then no preemption will be performed if not receiverOperated and len(receiver.Res.users) > 0: # if the receiver does not hold an Entity that is also critical if not receiver.Res.users[0].isCritical: receiver.shouldPreempt = True self.printTrace(self.id, preempt=receiver.id) receiver.preempt() receiver.timeLastEntityEnded = ( self.env.now ) # required to count blockage correctly in the preemptied station # sort so that the critical entity is placed in front activeObjectQueue.sort( key=lambda x: x == activeEntity, reverse=True ) # if there is a critical entity and the possible receivers are operated then signal the Router elif receiverOperated: self.signalRouter(receiver) activeObjectQueue.sort( key=lambda x: x == activeEntity, reverse=True ) # update wipStatList # update wipStatList if self.gatherWipStat: import numpy wip = 0 for holdEntity in activeObjectQueue: wip += holdEntity.numberOfUnits self.wipStatList = numpy.concatenate( (self.wipStatList, [[self.env.now, wip]]) )
[docs] @staticmethod def findReceiversFor(activeObject): """ find possible receivers """ receivers = [] for object in [ x for x in activeObject.next if x.canAccept(activeObject) and not x.isRequested.triggered and x.expectedSignals["isRequested"] ]: receivers.append(object) return receivers
[docs] def signalReceiver(self, transmitter=None): """signal the successor that the object can dispose an entity""" possibleReceivers = ( [transmitter] if transmitter else self.findReceiversFor(self) ) if possibleReceivers: receiver = self.selectReceiver(possibleReceivers) receiversGiver = self # perform the checks that canAcceptAndIsRequested used to perform and update activeCallersList or assignExit and operatorPool while not receiver.canAcceptAndIsRequested(receiversGiver): possibleReceivers.remove(receiver) if not possibleReceivers: receiversGiver = None receiver = None # if no receiver can accept then try to preempt a receive if the stations holds a critical order self.preemptReceiver() return False receiver = self.selectReceiver(possibleReceivers) receiversGiver = self # sorting the entities of the object for the receiver self.sortEntitiesForReceiver(receiver) # signalling the Router if the receiver is operated and not assigned an operator if self.signalRouter(receiver): return False self.receiver = receiver self.receiver.giver = self self.printTrace(self.id, signalReceiver=self.receiver.id) # assign the entry of the receiver self.receiver.assignEntryTo() # assign the exit of the current object to the receiver self.assignExitTo(self.receiver) if self.receiver.expectedSignals["isRequested"]: self.sendSignal( receiver=self.receiver, signal=self.receiver.isRequested ) return True # if no receiver can accept then try to preempt a receive if the stations holds a critical order self.preemptReceiver() return False
[docs] @staticmethod def selectReceiver(possibleReceivers=[]): """select a receiver Object""" candidates = possibleReceivers # dummy variables that help prioritize the objects requesting to give objects to the object (activeObject) maxTimeWaiting = 0 # dummy variable counting the time a successor is waiting receiver = None from .Globals import G for object in candidates: timeWaiting = ( G.env.now - object.timeLastEntityLeft ) # the time it has been waiting is updated and stored in dummy variable timeWaiting if ( timeWaiting > maxTimeWaiting or maxTimeWaiting == 0 ): # if the timeWaiting is the maximum among the ones of the successors maxTimeWaiting = timeWaiting receiver = ( object # set the receiver as the longest waiting possible receiver ) return receiver
[docs] def sortEntitiesForReceiver(self, receiver=None): """sort the entities of the queue for the receiver""" pass
[docs] @staticmethod def findGiversFor(activeObject): """find possible givers""" givers = [] for object in [ x for x in activeObject.previous if (not x is activeObject) and not x.canDispose.triggered and ( x.expectedSignals["canDispose"] or (x.canDeliverOnInterruption and x.timeLastShiftEnded == x.env.now) ) ]: # extra check.If shift ended right now and the object # can unload we relax the canDispose flag if object.haveToDispose(activeObject): givers.append(object) return givers
[docs] def signalGiver(self): """signal the giver that the entity is removed from its internalQueue""" possibleGivers = self.findGiversFor(self) if possibleGivers: giver = self.selectGiver(possibleGivers) giversReceiver = self # perform the checks that canAcceptAndIsRequested used to perform and update activeCallersList or assignExit and operatorPool while not self.canAcceptAndIsRequested(giver): possibleGivers.remove(giver) if not possibleGivers: return False giver = self.selectGiver(possibleGivers) giversReceiver = self self.giver = giver self.giver.receiver = self if self.giver.expectedSignals["canDispose"] or ( self.giver.canDeliverOnInterruption and self.giver.timeLastShiftEnded == self.env.now ): # extra check.If shift ended right now and the object # can unload we relax the canDispose flag self.sendSignal(receiver=self.giver, signal=self.giver.canDispose) self.printTrace(self.id, signalGiver=self.giver.id) return True return False
[docs] @staticmethod def selectGiver(possibleGivers=[]): """select a giver Object""" candidates = possibleGivers # dummy variables that help prioritize the objects requesting to give objects to the object (activeObject) maxTimeWaiting = 0 # dummy variable counting the time a predecessor is blocked giver = None from .Globals import G # loop through the possible givers to see which have to dispose and which is the one blocked for longer for object in candidates: # calculate how much the giver is waiting timeWaiting = G.env.now - object.timeLastEntityEnded if timeWaiting >= maxTimeWaiting: giver = object # the object to deliver the Entity to the activeObject is set to the ith member of the previous list maxTimeWaiting = timeWaiting return giver
[docs] def postProcessing(self, MaxSimtime=None): """actions to be taken after the simulation ends""" if MaxSimtime == None: from .Globals import G MaxSimtime = G.maxSimTime activeObject = self.getActiveObject() activeObjectQueue = self.getActiveObjectQueue() # update wipStatList if self.gatherWipStat: import numpy wip = 0 for holdEntity in activeObjectQueue: wip += holdEntity.numberOfUnits self.wipStatList = numpy.concatenate( (self.wipStatList, [[self.env.now, wip]]) ) # calculate the offShift time for current entity offShiftTimeInCurrentEntity = 0 if self.interruptedBy: if self.onShift == False: # and self.interruptedBy=='ShiftScheduler': offShiftTimeInCurrentEntity = ( self.env.now - activeObject.timeLastShiftEnded ) if self.isBlocked: self.addBlockage() # if object is currently processing an entity we should count this working time if self.isProcessing: """XXX currentlyPerforming can be Setup or Processing """ if self.currentlyPerforming: if self.currentlyPerforming == "Setup": activeObject.totalSetupTime += ( self.env.now - self.timeLastOperationStarted ) else: activeObject.totalWorkingTime += ( self.env.now - self.timeLastOperationStarted ) else: activeObject.totalWorkingTime += ( self.env.now - self.timeLastProcessingStarted ) # activeObject.totalTimeWaitingForOperator+=activeObject.operatorWaitTimeCurrentEntity # if object is down we have to add this failure time to its total failure time if self.Up == False: if self.onShift: activeObject.totalFailureTime += ( self.env.now - activeObject.timeLastFailure ) # if object is off shift add only the fail time before the shift ended if not self.onShift and self.timeLastFailure < self.timeLastShiftEnded: self.totalFailureTime += self.timeLastShiftEnded - self.timeLastFailure # if the Operator is on break we have to add this break time to its total break time if self.onBreak: if self.onShift: self.totalBreakTime += self.env.now - self.timeLastBreakStarted # if object is off shift add only the break time before the shift ended if not self.onShift and self.timeLastBreakStarted < self.timeLastShiftEnded: self.totalBreakTime += ( self.timeLastShiftEnded - self.timeLastBreakStarted ) # if the object is off shift,add this to the off-shift time if activeObject.onShift == False: # if we ran the simulation for infinite time we have to identify the last event now = self.env.now if now == float("inf"): now = 0 lastExits = [] for object in G.ExitList: lastExits.append(object.timeLastEntityEntered) if lastExits: now = max(lastExits) self.totalOffShiftTime += now - self.timeLastShiftEnded # object was idle when it was not in any other state activeObject.totalWaitingTime = ( MaxSimtime - activeObject.totalWorkingTime - activeObject.totalBlockageTime - activeObject.totalFailureTime - activeObject.totalLoadTime - activeObject.totalSetupTime - self.totalOffShiftTime - self.totalBreakTime ) if ( activeObject.totalBlockageTime < 0 and activeObject.totalBlockageTime > -0.00001 ): # to avoid some effects of getting negative cause of rounding precision self.totalBlockageTime = 0 if ( activeObject.totalWaitingTime < 0 and activeObject.totalWaitingTime > -0.00001 ): # to avoid some effects of getting negative cause of rounding precision self.totalWaitingTime = 0 activeObject.Failure.append(100 * self.totalFailureTime / MaxSimtime) activeObject.Blockage.append(100 * self.totalBlockageTime / MaxSimtime) activeObject.Waiting.append(100 * self.totalWaitingTime / MaxSimtime) activeObject.Working.append(100 * self.totalWorkingTime / MaxSimtime) activeObject.WaitingForOperator.append( 100 * self.totalTimeWaitingForOperator / MaxSimtime ) activeObject.WaitingForLoadOperator.append( 100 * self.totalTimeWaitingForLoadOperator / MaxSimtime ) activeObject.Loading.append(100 * self.totalLoadTime / MaxSimtime) activeObject.SettingUp.append(100 * self.totalSetupTime / MaxSimtime) activeObject.OffShift.append(100 * self.totalOffShiftTime / MaxSimtime) activeObject.OnBreak.append(100 * self.totalBreakTime / MaxSimtime) activeObject.WipStat.append(self.wipStatList.tolist())
[docs] def outputResultsJSON(self): """outputs results to JSON File""" pass
[docs] def haveToDispose(self, callerObject=None): """checks if the Object can dispose an entity to the following object""" activeObjectQueue = self.Res.users return len(activeObjectQueue) > 0
[docs] def canAcceptAndIsRequested(self, callerObject=None): """checks if the Object can accept an entity and there is an entity in some possible giver waiting for it""" pass
# =======================================================================
[docs] def canAccept(self, callerObject=None): """checks if the Object can accept an entity""" pass
[docs] def isInRouteOf(self, callerObject=None): """method used to check whether the station is a successor of the caller""" thecaller = callerObject # if the caller is not defined then return True. We are only interested in checking whether # the station can accept whatever entity from whichever giver if not thecaller: return True # check it the caller object is predecessor to the activeObject if thecaller in self.previous: return True return False
[docs] def sortEntities(self): """sorts the Entities in the activeQ of the objects""" pass
[docs] def getActiveObject(self): """get the active object. This always returns self""" return self
[docs] def getActiveObjectQueue(self): """get the activeQ of the active object.""" return self.Res.users
[docs] def getGiverObject(self): """get the giver object in a getEntity transaction.""" return self.giver
[docs] def getGiverObjectQueue(self): """get the giver object queue in a getEntity transaction.""" return self.giver.Res.users
[docs] def getReceiverObject(self): """get the receiver object in a removeEntity transaction.""" return self.receiver
[docs] def getReceiverObjectQueue(self): """get the receiver object queue in a removeEntity transaction.""" return self.receiver.Res.users
[docs] def calculateProcessingTime(self): """calculates the processing time""" # this is only for processing of the initial wip if self.isProcessingInitialWIP: activeEntity = self.getActiveObjectQueue()[0] if activeEntity.remainingProcessingTime: remainingProcessingTime = activeEntity.remainingProcessingTime from ..RandomNumberGenerator import RandomNumberGenerator initialWIPrng = RandomNumberGenerator(self, remainingProcessingTime) return initialWIPrng.generateNumber() return ( self.rng.generateNumber() ) # this is if we have a default processing time for all the entities
[docs] def calculateTime(self, type="Processing"): """calculates time (running through a dictionary) according to the type of processing given as argument""" return { "Load": self.loadRng.generateNumber, "Setup": self.stpRng.generateNumber, "Processing": self.calculateProcessingTime, }[type]()
[docs] def exitIsAssignedTo(self): """checks if the object is blocked""" return self.exitAssignedToReceiver
[docs] def assignExitTo(self, callerObject=None): """assign Exit of the object""" self.exitAssignedToReceiver = callerObject
[docs] def unAssignExit(self): """unblock the object""" self.exitAssignedToReceiver = None
[docs] def entryIsAssignedTo(self): """checks if the object is blocked""" return self.entryAssignedToGiver
[docs] def assignEntryTo(self): """assign Exit of the object""" self.entryAssignedToGiver = self.giver
[docs] def unAssignEntry(self): """unblock the object""" self.entryAssignedToGiver = None
[docs] def interruptionActions(self): """ actions to be carried whenever the object is interrupted (failure, break, preemption, etc) """ pass
[docs] def postInterruptionActions(self): """ actions to be carried whenever the object recovers control after an interruption (failure, break, preemption, etc) """ pass
[docs] def preempt(self): """method to execute preemption""" # ToDO make a generic method pass
[docs] def checkIfActive(self): """checks if the object is in an active position""" return self.Up and self.onShift and (not self.onBreak)
[docs] def activeQueueIsEmpty(self): """filter that returns True if the activeObject Queue is empty false if object holds entities in its queue""" return len(self.Res.users) == 0
[docs] def endOperationActions(self): """actions to be carried out when the processing of an Entity ends""" pass
[docs] def isInActiveQueue(self, entity=None): """check if an entity is in the internal Queue of the object""" activeObjectQueue = self.Res.users return any(x == entity for x in activeObjectQueue)