Example #1
0
class RaceManager:
    
    testSpeedRatio = 1
    
    def __init__(self):
        self.fleets = []
        self.fleetsById = {}
        self.changed = Signal()
        self.finishes = []
        self.finishesById = {}
        # we store these on the race manager so that they get pickled
        self.nextFleetId = 1
        self.nextFinishId = 1
        
    #
    # this method controls how the RaceManager is pickled. We want to avoid pickling the Signal object
    # stored on the changed attribute
    #
    def __getstate__(self):
        attributes = self.__dict__.copy()
        del attributes["changed"]
        
        return attributes
    
    #
    # this method controls how the RaceManager is unpickled. We need to set the changed attribute
    # as it is not part of the pickle
    #
    def __setstate__(self,d):
        self.__dict__ = d
        self.changed = Signal()
         

    def incrementNextFleetId(self):
        self.nextFleetId = self.nextFleetId + 1

    def incrementNextFinishId(self):
        self.nextFinishId = self.nextFinishId + 1


        

    def adjustedSeconds(self,unadjustedSeconds):
        return unadjustedSeconds * RaceManager.testSpeedRatio
    
    def unadjustedSecond(self,adjustedSeconds):
        return adjustedSeconds / RaceManager.testSpeedRatio
    
    #
    # Create a fleet, add to our fleets and return the fleet. If the name is not specified,
    # we create a name as 'Fleet N' where N is the number of fleets.
    #
    def createFleet(self, name=None):
        aFleet = Fleet(name=name,fleetId=self.nextFleetId)
        self.incrementNextFleetId()
        self.addFleet(aFleet)
        return aFleet
    
    def fleetWithId(self,fleetId):
        if fleetId in self.fleetsById:
            return self.fleetsById[fleetId]
        

    def addFleet(self, aFleet):
        self.fleets.append(aFleet)
        self.fleetsById[aFleet.fleetId] = aFleet
        self.changed.fire("fleetAdded",aFleet)
        

    def removeFleet(self, aFleet):
        if aFleet in self.fleets:
            positionInList = self.fleets.index(aFleet)
            self.fleets.remove(aFleet)
            del self.fleetsById[aFleet.fleetId]
            self.changed.fire("fleetRemoved",aFleet)
            
        else:
            raise RaceException("Fleet not found",aFleet)
            

    def numberFleets(self):
        return len(self.fleets)
    
    def hasFleets(self):
        return self.numberFleets() > 0

    #
    # Start our race sequence in ten seconds with a five minute warning before the first
    # fleet, i.e. 10 minutes to the first fleet start. This is F flag start
    #
    def startRaceSequenceWithWarning(self):
        logging.info("Start sequence with warning (F flag start)")
        fleetNumber = 0
        
        now = datetime.now()
        sequenceStart = now + timedelta(seconds=10)
        for fleet in self.fleets:
            fleetNumber = fleetNumber + 1
            
            startTime = sequenceStart + timedelta(
                seconds = (WARNING_SECONDS/RaceManager.testSpeedRatio + 
                        (START_SECONDS * fleetNumber)/RaceManager.testSpeedRatio))

            self.updateFleetStartTime(fleet,startTime)
        self.changed.fire("sequenceStartedWithWarning")


    #
    # Start our race sequence without a warning (i.e. class start)
    #
    def startRaceSequenceWithoutWarning(self):
        logging.info("Start sequence without warning (class flag start)")
        fleetNumber = 0
        now = datetime.now()
        
        sequenceStart = now + timedelta(seconds=10)
        
        for fleet in self.fleets:
            fleetNumber = fleetNumber + 1
            
            startTime = sequenceStart + timedelta(
                seconds = (START_SECONDS * fleetNumber)/RaceManager.testSpeedRatio)

            self.updateFleetStartTime(fleet,startTime)
        self.changed.fire("sequenceStartedWithoutWarning")
    #
    # Update the startTime for a fleet. Do this through the race manager
    # so that the race manager can signal the event change
    #
    def updateFleetStartTime(self, aFleet, startTime):
        aFleet.startTime = startTime
        # signal that the fleet start time has changed
        self.changed.fire("fleetChanged",aFleet)
        
            

    #
    # Find the last fleet started. This is a reverse search
    # of the fleets list for a started fleet.
    # Returns None if not found
    #
    def lastFleetStarted(self):
        for fleet in reversed(self.fleets):
            if fleet.isStarted():
                return fleet
        return None
    
    #
    # Fine the next fleet to start. If we don't have a fleet starting,
    # return None.
    #
    def nextFleetToStart(self):
        for fleet in self.fleets:
            if fleet.isStarting() or fleet.isWaitingToStart():
                return fleet
        return None


    def hasStartedFleet(self):
        return not self.lastFleetStarted() is None
    
    
    def hasSequenceStarted(self):
        if self.nextFleetToStart():
            return True
        else:
            return False
    
    
    #
    # Reset start sequence - set all fleets to no start time, and fire a signal
    #
    def resetStartSequence(self):
        for fleet in self.fleets:
            fleet.startTime = None
        self.removeAllFinishes()
        self.changed.fire("startSequenceReset")

    def lastFleet(self):
        return self.fleets[-1]

    #
    # Perform a general recall. This is always for the fleet that
    # has most recently started
    #
    def generalRecall(self):
        logging.info("General recall")
        fleetToRecall = self.lastFleetStarted()
        
        
        

        # if this is the last (or only) fleet, set its start time to be six
        # minutes from now
        if fleetToRecall == self.fleets[-1]:
            logging.info("General recall last fleet")
            self.updateFleetStartTime(fleetToRecall,datetime.now()
                                 + timedelta(seconds=(START_SECONDS+LAST_START_GENERAL_RECALL_DELAY)/RaceManager.testSpeedRatio))

        # otherwise kick the fleet to be the back of the queue,
        # with a start time five minutes after the last fleet
        else:
            
            self.removeFleet(fleetToRecall)
            lastFleet = self.fleets[-1]
            self.updateFleetStartTime(fleetToRecall,
                    lastFleet.startTime + timedelta(seconds=START_SECONDS/RaceManager.testSpeedRatio))
            self.addFleet(fleetToRecall)
            logging.log(logging.INFO, "General recall not last fleet. Moving to back of queue. Delta to start time now %d seconds",
                        fleetToRecall.adjustedDeltaSecondsToStartTime())
            
        self.changed.fire("generalRecall", fleetToRecall)

        
    #
    # Create a finish and add it to the race manager's list of finishes. 
    # This method returns a finish object. By default, the finish object will
    # have a finish time of now and no fleet.
    #
    def createFinish(self, fleet=None, finishTime=None):
        
        # if no finish time is supplied, set the finish time to be now
        if not finishTime:
            finishTime = datetime.now()
        # create the finish object
        
        # if we only have one fleet, this will be the fleet for the finish
        if self.numberFleets() == 1:
            fleet = self.fleets[0]
        
        aFinish = Finish(fleet=fleet,finishTime=finishTime,finishId=self.nextFinishId)
        self.incrementNextFinishId()
        
        self.addFinish(aFinish)
        
        return aFinish
        
    
    def addFinish(self,finish):
        # add it to our list of finish objects
        self.finishes.append(finish)
        self.finishesById[finish.finishId] = finish
        # fire a change signal
        self.changed.fire("finishAdded",finish)
        
    def removeFinish(self,finish):
        self.finishes.remove(finish)
        del self.finishesById[finish.finishId]
        self.changed.fire("finishRemoved",finish)
        
    def updateFinish(self,finish):
        self.changed.fire("finishChanged",finish)
        
    def removeAllFinishes(self):
        for finish in list(self.finishes):
            self.removeFinish(finish)
        
    def finishWithId(self,finishId):
        if finishId in self.finishesById:
            return self.finishesById[finishId]