def _makeDispatcher(self, connection):
     self.debugMsg("_makeDispatcher()")
     self.dispatcher = ActorDispatcher(
         connection=connection,
         name=self._dictName,  # name of keyword dictionary
     )
     # initialize a command queue
     self.cmdQueue = DispatcherCmdQueue(self.dispatcher)
 def _makeDispatcher(self, connection):
     self.debugMsg("_makeDispatcher()")
     self.dispatcher = ActorDispatcher(
         connection = connection,
         name = self._dictName, # name of keyword dictionary
     )
     # initialize a command queue
     self.cmdQueue = DispatcherCmdQueue(self.dispatcher)
class DispatcherWrapper(BaseWrapper):
    """!A wrapper for an opscore.ActorDispatcher talking to an wrapped actor

    This wrapper is responsible for starting and stopping everything:
    - It builds an actor dispatcher when the actor wrapper is ready
    - It stops the actor wrapper and actor dispatcher on close()

    Public attributes include:
    - actorWrapper: the actor wrapper (twistedActor.ActorWrapper)
    - dispatcher: the actor dispatcher (twistedActor.ActorDispatcher); None until ready
    - readyDeferred: called when the dispatcher is ready
      (for tracking closure use the Deferred returned by the close method, or stateCallback).
    """
    def __init__(self,
        actorWrapper,
        dictName,
        name = "",
        readCallback = None,
        stateCallback = None,
        debug = False,
    ):
        """!Construct a DispatcherWrapper that manages everything

        @param[in] actorWrapper  actor wrapper (twistedActor.ActorWrapper); must be starting up or ready
        @param[in] dictName  name of actor keyword dictionary
        @param[in] name  a name to use for messages
        @param[in] readCallback  function to call when the actor dispatcher has data to read
        @param[in] stateCallback  function to call when connection state of of any socket changes;
            receives one argument: this actor wrapper
        @param[in] debug  print debug messages to stdout?
        """
        BaseWrapper.__init__(self,
            name = name,
            stateCallback = stateCallback,
            callNow = False,
            debug = debug,
        )
        self.actorWrapper = actorWrapper
        self._dictName = dictName
        self._readCallback = readCallback
        self.dispatcher = None # the ActorDispatcher, once it's built
        self.actorWrapper.addCallback(self._actorWrapperStateChanged)
        self._actorWrapperStateChanged()

    def _makeDispatcher(self, connection):
        self.debugMsg("_makeDispatcher()")
        self.dispatcher = ActorDispatcher(
            connection = connection,
            name = self._dictName, # name of keyword dictionary
        )
        # initialize a command queue
        self.cmdQueue = DispatcherCmdQueue(self.dispatcher)

    @property
    def actor(self):
        """!Return the actor (in this case, the mirror controller)
        """
        return self.actorWrapper.actor

    @property
    def userPort(self):
        """!Return the actor port, if known, else None
        """
        return self.actorWrapper.userPort

    @property
    def isReady(self):
        """!Return True if the actor has connected to the fake hardware controller
        """
        return self.actorWrapper.isReady and self.dispatcher is not None and self.dispatcher.connection.isConnected

    @property
    def isDone(self):
        """!Return True if the actor and fake hardware controller are fully disconnected
        """
        return self.actorWrapper.isDone and self.dispatcher is not None and self.dispatcher.connection.isDisconnected

    @property
    def isFailing(self):
        """!Return True if there is a failure
        """
        return self.actorWrapper.didFail or (self.dispatcher is not None and self.dispatcher.connection.didFail)

    def queueCmd(self, cmdStr, timeLim=0, timeLimKeyVar=None, timeLimKeyInd=0, keyVars=None, callFunc=None, callCodes=":"):
        """!add command to queue, dispatch when ready

        @warning error handling is determined by callCodes; for details, see the documentation
            for the returned deferred below

        @param[in] cmdStr  a command string
        @param[in] timeLim  maximum time before command expires, in sec; 0 for no limit
        @param[in] timeLimKeyVar  a KeyVar specifying a delta-time by which the command must finish
        @param[in] timeLimKeyInd  the index of the time limit value in timeLimKeyVar; defaults to 0;
            ignored if timeLimKeyVar is None.
        @param[in] keyVars  a sequence of 0 or more keyword variables to monitor for this command.
            Any data for those variables that arrives IN RESPONSE TO THIS COMMAND is saved
            and can be retrieved using cmdVar.getKeyVarData or cmdVar.getLastKeyVarData.
        @param[in] callFunc  receives one arguement the CmdVar
        @param[in] callCodes  a string of message codes that will result in calling callFunc;
            common values include ":"=success, "F"=failure and ">"=queued
            see opscore.actor.keyvar for all call codes.

        Common use cases:
        - To call callFunc only when the command succeeds, use callCodes=":" (the default);
            if the command fails callFunc is not called.
        - To call callFunc every time the cmdVar calls back, while running successfully,
            specify callCodes=""DIW:>". Again, if the command fails callFunc is not called.
        - To test a command you expect to fail, specify callCodes=":F!"
            and have callFunc assert cmdVar.didFail

        @return two items:
        - deferred: a Twisted Deferred:
            - errorback is called if:
                - the cmdVar fails (unless callCodes includes "F")
                - callFunc raises an exception
            - otherwise callback is called when the command is done and callFunc did not raise an exception
        - cmdVar: the CmdVar for the command
        """
        cmdVar = CmdVar(
            actor = self._dictName,
            cmdStr = cmdStr,
            timeLim = timeLim,
            timeLimKeyVar = timeLimKeyVar,
            timeLimKeyInd = timeLimKeyInd,
            keyVars = keyVars,
        )
        cmdWrapper = CmdWrapper(cmdVar=cmdVar, callFunc=callFunc, callCodes=callCodes)
        self.cmdQueue.addCmd(cmdWrapper)
        return (cmdWrapper.deferred, cmdVar)

    def _actorWrapperStateChanged(self, dumArg=None):
        """!Called when the device wrapper changes state
        """
        if self.actorWrapper.isReady and not self.dispatcher:
            connection = TCPConnection(
                host = 'localhost',
                port = self.actorWrapper.userPort,
                readLines = True,
                name = "mirrorCtrlConn",
            )
            self._makeDispatcher(connection)
            connection.addStateCallback(self._stateChanged)
            if self._readCallback:
                connection.addReadCallback(self._readCallback)
            connection.connect()
        self._stateChanged()

    def _basicClose(self):
        """!Close dispatcher and actor
        """
        if self.dispatcher:
            self.dispatcher.disconnect()
        self.actorWrapper.close()
class DispatcherWrapper(BaseWrapper):
    """!A wrapper for an opscore.ActorDispatcher talking to an wrapped actor

    This wrapper is responsible for starting and stopping everything:
    - It builds an actor dispatcher when the actor wrapper is ready
    - It stops the actor wrapper and actor dispatcher on close()

    Public attributes include:
    - actorWrapper: the actor wrapper (twistedActor.ActorWrapper)
    - dispatcher: the actor dispatcher (twistedActor.ActorDispatcher); None until ready
    - readyDeferred: called when the dispatcher is ready
      (for tracking closure use the Deferred returned by the close method, or stateCallback).
    """
    def __init__(
        self,
        actorWrapper,
        dictName,
        name="",
        readCallback=None,
        stateCallback=None,
        debug=False,
    ):
        """!Construct a DispatcherWrapper that manages everything

        @param[in] actorWrapper  actor wrapper (twistedActor.ActorWrapper); must be starting up or ready
        @param[in] dictName  name of actor keyword dictionary
        @param[in] name  a name to use for messages
        @param[in] readCallback  function to call when the actor dispatcher has data to read
        @param[in] stateCallback  function to call when connection state of of any socket changes;
            receives one argument: this actor wrapper
        @param[in] debug  print debug messages to stdout?
        """
        BaseWrapper.__init__(
            self,
            name=name,
            stateCallback=stateCallback,
            callNow=False,
            debug=debug,
        )
        self.actorWrapper = actorWrapper
        self._dictName = dictName
        self._readCallback = readCallback
        self.dispatcher = None  # the ActorDispatcher, once it's built
        self.actorWrapper.addCallback(self._actorWrapperStateChanged)
        self._actorWrapperStateChanged()

    def _makeDispatcher(self, connection):
        self.debugMsg("_makeDispatcher()")
        self.dispatcher = ActorDispatcher(
            connection=connection,
            name=self._dictName,  # name of keyword dictionary
        )
        # initialize a command queue
        self.cmdQueue = DispatcherCmdQueue(self.dispatcher)

    @property
    def actor(self):
        """!Return the actor (in this case, the mirror controller)
        """
        return self.actorWrapper.actor

    @property
    def userPort(self):
        """!Return the actor port, if known, else None
        """
        return self.actorWrapper.userPort

    @property
    def isReady(self):
        """!Return True if the actor has connected to the fake hardware controller
        """
        return self.actorWrapper.isReady and self.dispatcher is not None and self.dispatcher.connection.isConnected

    @property
    def isDone(self):
        """!Return True if the actor and fake hardware controller are fully disconnected
        """
        return self.actorWrapper.isDone and self.dispatcher is not None and self.dispatcher.connection.isDisconnected

    @property
    def isFailing(self):
        """!Return True if there is a failure
        """
        return self.actorWrapper.didFail or (
            self.dispatcher is not None and self.dispatcher.connection.didFail)

    def queueCmd(self,
                 cmdStr,
                 timeLim=0,
                 timeLimKeyVar=None,
                 timeLimKeyInd=0,
                 keyVars=None,
                 callFunc=None,
                 callCodes=":"):
        """!add command to queue, dispatch when ready

        @warning error handling is determined by callCodes; for details, see the documentation
            for the returned deferred below

        @param[in] cmdStr  a command string
        @param[in] timeLim  maximum time before command expires, in sec; 0 for no limit
        @param[in] timeLimKeyVar  a KeyVar specifying a delta-time by which the command must finish
        @param[in] timeLimKeyInd  the index of the time limit value in timeLimKeyVar; defaults to 0;
            ignored if timeLimKeyVar is None.
        @param[in] keyVars  a sequence of 0 or more keyword variables to monitor for this command.
            Any data for those variables that arrives IN RESPONSE TO THIS COMMAND is saved
            and can be retrieved using cmdVar.getKeyVarData or cmdVar.getLastKeyVarData.
        @param[in] callFunc  receives one arguement the CmdVar
        @param[in] callCodes  a string of message codes that will result in calling callFunc;
            common values include ":"=success, "F"=failure and ">"=queued
            see opscore.actor.keyvar for all call codes.

        Common use cases:
        - To call callFunc only when the command succeeds, use callCodes=":" (the default);
            if the command fails callFunc is not called.
        - To call callFunc every time the cmdVar calls back, while running successfully,
            specify callCodes=""DIW:>". Again, if the command fails callFunc is not called.
        - To test a command you expect to fail, specify callCodes=":F!"
            and have callFunc assert cmdVar.didFail

        @return two items:
        - deferred: a Twisted Deferred:
            - errorback is called if:
                - the cmdVar fails (unless callCodes includes "F")
                - callFunc raises an exception
            - otherwise callback is called when the command is done and callFunc did not raise an exception
        - cmdVar: the CmdVar for the command
        """
        cmdVar = CmdVar(
            actor=self._dictName,
            cmdStr=cmdStr,
            timeLim=timeLim,
            timeLimKeyVar=timeLimKeyVar,
            timeLimKeyInd=timeLimKeyInd,
            keyVars=keyVars,
        )
        cmdWrapper = CmdWrapper(cmdVar=cmdVar,
                                callFunc=callFunc,
                                callCodes=callCodes)
        self.cmdQueue.addCmd(cmdWrapper)
        return (cmdWrapper.deferred, cmdVar)

    def _actorWrapperStateChanged(self, dumArg=None):
        """!Called when the device wrapper changes state
        """
        if self.actorWrapper.isReady and not self.dispatcher:
            connection = TCPConnection(
                host='localhost',
                port=self.actorWrapper.userPort,
                readLines=True,
                name="mirrorCtrlConn",
            )
            self._makeDispatcher(connection)
            connection.addStateCallback(self._stateChanged)
            if self._readCallback:
                connection.addReadCallback(self._readCallback)
            connection.connect()
        self._stateChanged()

    def _basicClose(self):
        """!Close dispatcher and actor
        """
        if self.dispatcher:
            self.dispatcher.disconnect()
        self.actorWrapper.close()