Example #1
0
class Throttle(object):
    def __init__(self, name=None, comPkg=None):
        gLog.print("Throttle {0}: initializing".format(name))

        assert isinstance(name, str)

        assert isinstance(comPkg, CommunicationsPackage)
        inQu = comPkg.inQu
        inQuNum = comPkg.inQuNum
        outQu = comPkg.outQu

        assert isinstance(inQu, multiprocessing.queues.Queue)
        assert inQuNum >= 0
        assert isinstance(outQu, multiprocessing.queues.Queue)

        self.name = name
        self.virtSlot = None
        self.direction = kForward
        self.lights = kOff
        self.horn = kOff
        self.bell = kOff
        self.mute = kOff
        self.F5 = 0  # Flip this to close next turnout
        self.F6 = 0  # Flip this ot throw next turnout
        self.msgHandler = MsgHandler(name=self.name, comPkg=comPkg)

    def addInterest(self, interest):
        gLog.print("Throttle {0}: adding interest {1}".format(self.name, interest))
        assert interest in ControllerInMsgs
        self.msgHandler.addInterest(interest)

    def removeInterest(self, interest):
        gLog.print("Throttle {0}: removing interest {1}".format(self.name, interest))
        assert interest in ControllerInMsgs
        self.msgHandler.removeInterest(interest)

    def waitFor(self, msg):
        gLog.print("waitFor: msg = {0}".format(msg))
        assert isinstance(msg, tuple)
        return self.msgHandler.waitFor(msg)

    def close(self):
        gLog.print("Throttle {0}: closing".format(self.name))
        self.msgHandler.close()

    def getBlocking(self):
        msg = self.msgHandler.getBlocking()
        # gLog.print("Throttle {0}: getBlocking msg {1}".format(self.name, msg))
        return msg

    def getNonblocking(self):
        try:
            msg = self.msgHandler.getNonblocking()
            # gLog.print("Throttle {0}: getNonlocking msg {1}".format(self.name, msg))
            return msg
        except multiprocessing.queues.Empty:
            gLog.print("Throttle {0}: getNonlocking empty qu exception".format(self.name))
            raise multiprocessing.queues.Empty

    def put(self, msg):
        self.msgHandler.put(msg)

    def atSpeedGoTo(self, speed, destination, sensorsToExclude):
        self.addInterest(PutTrainPositionMsg)
        self.put(GetTrainPositionMsg(slot=self.virtSlot))
        done = False
        while not done:
            msg = self.waitFor(PutTrainPositionMsg(slot=self.virtSlot, sensors=[]))
            if msg.slot == self.virtSlot:
                self.removeInterest(PutTrainPositionMsg)
                done = True
        fromSensor = msg.sensors[0]
        preSensor = msg.sensors[1]
        path = self.getPath(kBreadthFirst, preSensor, fromSensor, destination, sensorsToExclude)
        self.setSpeed(speed)
        self.followSensorPath(path)

    def waitForTrainToReach(self, sensor):
        # Pre  train is moving
        # Post returns when train reaches the indicated sensor
        method = 1
        if method == 1:
            # Use train position to know when train has reached sensor
            self.addInterest(PutTrainPositionMsg)
            while True:
                msg = self.waitFor(PutTrainPositionMsg(slot=self.virtSlot, sensors=[]))
                if msg.sensors[1] == sensor:
                    break
            self.removeInterest(PutTrainPositionMsg)
        else:
            # Use sensor firing, which has the following defect:
            #   One doesn't know which train fired the sensor
            self.addInterest(PutSensorStateMsg)
            self.waitFor(PutSensorStateMsg(id=sensor, state=kSensorOpen))
            self.removeInterest(PutSensorStateMsg)

    def followCommandPath(self, path):
        """
        A command path is a list of tuples (sensor#, command)
        When the train reaches the sensor, the throttle executes the command.
        """
        previousSensor = 0
        for point in path:
            sensor = point[0]
            command = point[1]
            if sensor != previousSensor:
                previousSensor = sensor
                self.waitForTrainToReach(sensor)
            self.doCommand(command)

    def atSensorDoCommand(self, sensor, command):
        commandPath = [[sensor, command]]
        self.followCommandPath(commandPath)

    def atSensorDoCommands(self, sensor, commands):
        commandPath = []
        for command in commands:
            commandPath.append([sensor, command])
        self.followCommandPath(commandPath)

    def followSwitchPath(self, path):
        """
        A switch path is a list of triples (sensor#, switch#, direction)
        When the train reaches the sensor, the throttle moves the switch.
        """
        previousSensor = 0
        for point in path:
            sensor = point[0]
            switch = point[1]
            direction = point[2]
            if sensor != previousSensor:
                previousSensor = sensor
                self.waitForTrainToReach(sensor)
            self.moveSwitch(switch, direction)

    def followSensorPath(self, path):
        """
        A sensor path is a list or tuple of sensor numbers defining the path which a train
        desires to follow. As the train approaches section (i, i+1),  the throttle makes section (i+1, i+2)
        usable. To get things started, the throttle makes the section (0, 1) usable,
        where i indicates sensor i.
        """
        self.makeSectionUsable(path[0], path[1])
        for i in range(0, len(path) - 2):
            self.waitForTrainToReach(path[i])
            self.makeSectionUsable(path[i + 1], path[i + 2])

    ################################################################################

    def readLayout(self, fileName):
        gLog.print("Throttle {0}: sending DoReadLayoutMsg using file {1}".format(self.name, fileName))
        assert isinstance(fileName, str)
        self.addInterest(PutReadLayoutResponseMsg)
        self.put(DoReadLayoutMsg(fileName=fileName))
        msg = self.waitFor(PutReadLayoutResponseMsg(responseFlag=0, code=0))
        self.removeInterest(PutReadLayoutResponseMsg)
        sleep(3)
        return msg

    def initTrain(self, address, position):
        gLog.print("Throttle {0}: sending DoLocoInitMsg".format(self.name))
        assert 0 <= address <= 9999
        assert isinstance(position, list) or isinstance(position, tuple)
        self.put(DoLocoInitMsg(address=address, sensors=position))
        responseMsg = None
        if position:
            self.addInterest(PutInitOutcomeMsg)
            responseMsg = self.waitFor(PutInitOutcomeMsg(physAdd=address, physSlot=0, virtAdd=0, virtSlot=0))
            self.removeInterest(PutInitOutcomeMsg)
            if responseMsg.physSlot > 120:
                self.virtSlot = None
            else:
                self.virtSlot = responseMsg.virtSlot
        return responseMsg

    def getPath(self, pathKind, preSensor, fromSensor, toSensor, sensorsToExclude):
        gLog.print("Throttle {0}: sending GetPathMsg".format(self.name))
        self.addInterest(PutPathMsg)
        self.put(
            GetPathMsg(
                slot=self.virtSlot,
                pathKind=pathKind,
                preSensor=preSensor,
                fromSensor=fromSensor,
                toSensor=toSensor,
                sensorsToExclude=sensorsToExclude,
            )
        )
        msg = self.waitFor(PutPathMsg(sensors=[]))
        self.removeInterest(PutPathMsg)
        return msg.sensors

    def setVirtSlot(self, virtSlot):
        self.virtSlot = virtSlot

    def doFunction(self, func, *args):
        func(self, *args)

    def doCommand(self, command):
        if len(command) == 1:
            command[0](self)
        elif len(command) == 2:
            command[0](self, command[1])
        elif len(command) == 3:

            command[0](self, command[1], command[2])
        elif len(command) == 4:
            command[0](self, command[1], command[2], command[3])
        else:
            print("Command too long: {0}".format(command))

    def doCommands(self, commands):
        for command in commands:
            self.doCommand(command)

    def sendDirf(self):
        assert self.virtSlot is not None
        self.put(
            LocoDirfMsg(
                slot=self.virtSlot, direction=self.direction, lights=self.lights, horn=self.horn, bell=self.bell
            )
        )

    def sendSnd(self):
        assert self.virtSlot is not None
        self.put(LocoSndMsg(slot=self.virtSlot, mute=self.mute, F5=self.F5, F6=self.F6))

    def setSpeed(self, speed):
        assert self.virtSlot is not None
        assert speed >= 0
        self.put(LocoSpdMsg(slot=self.virtSlot, speed=speed))

    def setDirection(self, direction):
        assert direction == kForward or direction == kBackward
        self.direction = direction
        self.sendDirf()

    def setLights(self, onOff):
        assert onOff == kOn or onOff == kOff
        self.lights = onOff
        self.sendDirf()

    def setHorn(self, onOff):
        assert onOff == kOn or onOff == kOff
        self.horn = onOff
        self.sendDirf()

    def tootHorn(self):
        for i in range(3):
            self.setHorn(kOn)
            sleep(1)
            self.setHorn(kOff)
            if i != 2:
                sleep(0.2)

    def setBell(self, onOff):
        assert onOff == kOn or onOff == kOff
        self.bell = onOff
        self.sendDirf()

    def setMute(self, onOff):
        assert onOff == kOn or onOff == kOff
        self.mute = onOff
        self.sendSnd()

    def closeNextSwitch(self):
        if self.F5 == 0:
            self.F5 = 1
        else:
            self.F5 = 0
        self.sendSnd()

    def throwNextSwitch(self):
        if self.F6 == 0:
            self.F6 = 1
        else:
            self.F6 = 0
        self.sendSnd()

    def moveSwitch(self, sId, direction):
        assert sId > 0
        assert direction == kForward or direction == kBackward
        self.put(SwReqMsg(switch=sId, direction=direction))

    def pause(self, secs):
        sleep(secs)

    def makeSectionUsable(self, sensor1, sensor2):
        assert sensor1 > 0
        assert sensor2 > 0
        self.put(DoMakeSectionUsableMsg(sensor1=sensor1, sensor2=sensor2))