def _start_pagent(self):
        """
        Construct and start the port agent.
        """

        # Create port agent object.
        this_pid = os.getpid()
        self._pagent = EthernetDeviceLogger.launch_process(
            self.device_address, self.device_port, WORK_DIR, DELIM, this_pid
        )

        # Get the pid and port agent server port number.
        pid = self._pagent.get_pid()
        while not pid:
            gevent.sleep(0.1)
            pid = self._pagent.get_pid()
        port = self._pagent.get_port()
        while not port:
            gevent.sleep(0.1)
            port = self._pagent.get_port()

        # Configure driver to use port agent port number.
        DVR_CONFIG["comms_config"] = {"addr": "localhost", "port": port}

        # Report.
        log.info("Started port agent pid %d listening at port %d.", pid, port)
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """
        
        # Create port agent object.
        this_pid = os.getpid()
        self._pagent = EthernetDeviceLogger.launch_process(DEV_ADDR, DEV_PORT,
                        WORK_DIR, DELIM, this_pid)

        pid = self._pagent.get_pid()
        while not pid:
            gevent.sleep(.1)
            pid = self._pagent.get_pid()
        port = self._pagent.get_port()
        while not port:
            gevent.sleep(.1)
            port = self._pagent.get_port()
        
        COMMS_CONFIG['port'] = port

        mi_logger.info('Started port agent pid %d listening at port %d', pid, port)
    def start_pagent(self):
        """
        Construct and start the port agent.
        @retval port Port that was used for connection to agent
        """
        # Create port agent object.
        this_pid = os.getpid()
        self._pagent = EthernetDeviceLogger.launch_process(self.device_addr,
                                                           self.device_port,
                                                           self.work_dir,
                                                           self.delim,
                                                           this_pid)

        pid = self._pagent.get_pid()
        while not pid:
            gevent.sleep(.1)
            pid = self._pagent.get_pid()
        port = self._pagent.get_port()
        while not port:
            gevent.sleep(.1)
            port = self._pagent.get_port()

        mi_logger.info('Started port agent pid %d listening at port %d', pid, port)
        return port
class InstrumentProtocol(object):
    """The base class for an instrument protocol
    
    The classes derived from this class will carry out the specific
    interactions between a specific device and the instrument driver. At this
    layer of interaction, there are no conflicts or transactions as that is
    handled at the layer above this. Think of this as encapsulating the
    transport layer of the communications.
    """
    
    implements(IInstrumentConnection)
    
    def __init__(self, evt_callback=None):
        """Set instrument connect at creation
        
        @param connection An InstrumetnConnection object
        """
        self._logger = None
        self._logger_client = None
        self._logger_popen = None
        self._fsm = None
        
        self.send_event = evt_callback
        """The driver callback where we an publish events. Should be a link
        to a function. Currently a dict with keys in EventKey enum."""
        
    ########################################################################
    # Protocol connection interface.
    ########################################################################

    """
    @todo Move this into the driver state machine?
    """
    
    def initialize(self, *args, **kwargs):
        """
        """
        mi_logger.info('Initializing device comms.')        
        self._logger = None
        self._logger_client = None
    
    def configure(self, config, *args, **kwargs):
        """
        """
        mi_logger.info('Configuring for device comms.')        

        method = config['method']
                
        if method == 'ethernet':
            device_addr = config['device_addr']
            device_port = config['device_port']
            server_addr = config['server_addr']
            server_port = config['server_port']
            self._logger = EthernetDeviceLogger(device_addr, device_port,
                                            server_port)
            self._logger_client = LoggerClient(server_addr, server_port)

        elif method == 'serial':
            # The config dict does not have a valid connection method.
            raise InstrumentConnectionException()
                
        else:
            # The config dict does not have a valid connection method.
            raise InstrumentConnectionException()
    
    def connect(self, *args, **kwargs):
        """Connect via the instrument connection object
        
        @param args connection arguments
        @throws InstrumentConnectionException
        """
        mi_logger.info('Connecting to device.')
        
        logger_pid = self._logger.get_pid()
        mi_logger.info('Found logger pid: %s.', str(logger_pid))
        if not logger_pid:
            self._logger_popen = self._logger.launch_process()
            time.sleep(0.2)
            try:
                retval = os.wait()
                mi_logger.debug('os.wait returned %s' % str(retval))
            except Exception as e:
                mi_logger.debug('os.wait() threw %s: %s' %
                               (e.__class__.__name__, str(e)))
            mi_logger.debug('popen wait returned %s', str(self._logger_popen.wait()))
            time.sleep(1)         
            self.attach()
        else:
            # There was a pidfile for the device.
            raise InstrumentConnectionException()

        return logger_pid
        
    def disconnect(self, *args, **kwargs):
        """Disconnect via the instrument connection object
        
        @throws InstrumentConnectionException
        """
        mi_logger.info('Disconnecting from device.')
        self.detach()
        self._logger.stop()
    
    def attach(self, *args, **kwargs):
        """
        """
        mi_logger.info('Attaching to device.')        
        self._logger_client.init_comms(self._got_data)
    
    def detach(self, *args, **kwargs):
        """
        """
        mi_logger.info('Detaching from device.')
        self._logger_client.stop_comms()
        
    def reset(self, *args, **kwargs):
        """Reset via the instrument connection object"""
        # Call logger reset here.
        pass
        
    ########################################################################
    # Protocol command interface.
    ########################################################################
        
    def get(self, *args, **kwargs):
        """Get some parameters
        
        @param params A list of parameters to fetch. These must be in the
        fetchable parameter list
        @retval results A dict of the parameters that were queried
        @throws InstrumentProtocolException Confusion dealing with the
        physical device
        @throws InstrumentStateException Unable to handle current or future
        state properly
        @throws InstrumentTimeoutException Timeout
        """
        pass
    
    def set(self, *args, **kwargs):
        """Get some parameters
        
        @throws InstrumentProtocolException Confusion dealing with the
        physical device
        @throws InstrumentStateException Unable to handle current or future
        state properly
        @throws InstrumentTimeoutException Timeout
        """
        pass

    def execute(self, *args, **kwargs):
        """Execute a command
        
        @param command A single command as a list with the command ID followed
        by the parameters for that command
        @throws InstrumentProtocolException Confusion dealing with the
        physical device
        @throws InstrumentStateException Unable to handle current or future
        state properly
        @throws InstrumentTimeoutException Timeout
        """
        pass
    
    def execute_direct(self, *args, **kwargs):
        """
        """
        pass
            
    ########################################################################
    # TBD.
    ########################################################################
    
    
    def get_capabilities(self):
        """
        """
        pass

    ########################################################################
    # Helper methods
    ########################################################################
    def _got_data(self, data):
       """
       Called by the logger whenever there is data available
       """
       pass

    def announce_to_driver(self, type, error_code=None, msg=None):
        """
        Announce an event to the driver via the callback
        
        @param type The DriverAnnouncement enum type of the event
        @param args Any arguments involved
        @param msg A message to be included
        @todo Clean this up, promote to InstrumentProtocol?
        """
        assert type != None
        event = {EventKey:type}
        
        if error_code:
            event.update({EventKey.ERROR_CODE:error_code})
        if msg:
            event.update({EventKey.MESSAGE:msg})
            
        self.send_event(event)