コード例 #1
0
    def _construct_fsm(self):
        """
        """
        log.debug("constructing fsm")

        self._fsm = InstrumentFSM(PlatformDriverState,
                                  PlatformDriverEvent,
                                  PlatformDriverEvent.ENTER,
                                  PlatformDriverEvent.EXIT)

        for state in PlatformDriverState.list():
            self._fsm.add_handler(state, PlatformDriverEvent.ENTER, self._common_state_enter)
            self._fsm.add_handler(state, PlatformDriverEvent.EXIT, self._common_state_exit)

        # UNCONFIGURED state event handlers:
        self._fsm.add_handler(PlatformDriverState.UNCONFIGURED, PlatformDriverEvent.CONFIGURE, self._handler_unconfigured_configure)

        # DISCONNECTED state event handlers:
        self._fsm.add_handler(PlatformDriverState.DISCONNECTED, PlatformDriverEvent.CONNECT, self._handler_disconnected_connect)

        # CONNECTED state event handlers:
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.DISCONNECT, self._handler_connected_disconnect)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.CONNECTION_LOST, self._handler_connected_disconnect)

        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.PING, self._handler_connected_ping)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_METADATA, self._handler_connected_get_metadata)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_ATTRIBUTE_VALUES, self._handler_connected_get_attribute_values)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.SET_ATTRIBUTE_VALUES, self._handler_connected_set_attribute_values)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.CONNECT_INSTRUMENT, self._handler_connected_connect_instrument)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.DISCONNECT_INSTRUMENT, self._handler_disconnected_connect_instrument)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_CONNECTED_INSTRUMENTS, self._handler_connected_get_connected_instruments)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.TURN_ON_PORT, self._handler_connected_turn_on_port)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.TURN_OFF_PORT, self._handler_connected_turn_off_port)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_CHECKSUM, self._handler_connected_get_checksum)
コード例 #2
0
    def __init__(self, event_callback):
        """
        Constructor for singly connected instrument drivers.
        @param event_callback Callback to the driver process to send asynchronous
        driver events back to the agent.
        """
        InstrumentDriver.__init__(self, event_callback)
        
        # The only and only instrument connection.
        # Exists in the connected state.
        self._connection = None

        # The one and only instrument protocol.
        self._protocol = None
        
        # Build connection state machine.
        self._connection_fsm = InstrumentFSM(DriverConnectionState,
                                                DriverEvent,
                                                DriverEvent.ENTER,
                                                DriverEvent.EXIT)
        
        # Add handlers for all events.
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.ENTER, self._handler_unconfigured_enter)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.EXIT, self._handler_unconfigured_exit)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.INITIALIZE, self._handler_unconfigured_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.CONFIGURE, self._handler_unconfigured_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.ENTER, self._handler_disconnected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.EXIT, self._handler_disconnected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.INITIALIZE, self._handler_disconnected_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.CONFIGURE, self._handler_disconnected_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.CONNECT, self._handler_disconnected_connect)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.ENTER, self._handler_connected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.EXIT, self._handler_connected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.DISCONNECT, self._handler_connected_disconnect)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.CONNECTION_LOST, self._handler_connected_connection_lost)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.DISCOVER, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.GET, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.SET, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.ACQUIRE_SAMPLE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.START_AUTOSAMPLE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.STOP_AUTOSAMPLE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.TEST, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.CALIBRATE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.EXECUTE_DIRECT, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.START_DIRECT, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.STOP_DIRECT, self._handler_connected_protocol_event)
            
        # Start state machine.
        self._connection_fsm.start(DriverConnectionState.UNCONFIGURED)
コード例 #3
0
ファイル: agent.py プロジェクト: pombredanne/coi-services
    def _construct_fsm(self):
        """
        Construct the state machine and register default handlers.
        Override in subclass to add handlers for resouce-dependent behaviors
        and state transitions.
        """
                
        # Instrument agent state machine.
        self._fsm = InstrumentFSM(ResourceAgentState, ResourceAgentEvent,
                            ResourceAgentEvent.ENTER, ResourceAgentEvent.EXIT)
        
        self._fsm.add_handler(ResourceAgentState.UNINITIALIZED, ResourceAgentEvent.ENTER, self._handler_uninitialized_enter)
        self._fsm.add_handler(ResourceAgentState.UNINITIALIZED, ResourceAgentEvent.EXIT, self._handler_uninitialized_exit)

        self._fsm.add_handler(ResourceAgentState.POWERED_DOWN, ResourceAgentEvent.ENTER, self._handler_powered_down_enter)
        self._fsm.add_handler(ResourceAgentState.POWERED_DOWN, ResourceAgentEvent.EXIT, self._handler_powered_down_exit)

        self._fsm.add_handler(ResourceAgentState.INACTIVE, ResourceAgentEvent.ENTER, self._handler_inactive_enter)
        self._fsm.add_handler(ResourceAgentState.INACTIVE, ResourceAgentEvent.EXIT, self._handler_inactive_exit)

        self._fsm.add_handler(ResourceAgentState.IDLE, ResourceAgentEvent.ENTER, self._handler_idle_enter)
        self._fsm.add_handler(ResourceAgentState.IDLE, ResourceAgentEvent.EXIT, self._handler_idle_exit)

        self._fsm.add_handler(ResourceAgentState.STOPPED, ResourceAgentEvent.ENTER, self._handler_stopped_enter)
        self._fsm.add_handler(ResourceAgentState.STOPPED, ResourceAgentEvent.EXIT, self._handler_stopped_exit)

        self._fsm.add_handler(ResourceAgentState.COMMAND, ResourceAgentEvent.ENTER, self._handler_command_enter)
        self._fsm.add_handler(ResourceAgentState.COMMAND, ResourceAgentEvent.EXIT, self._handler_command_exit)
        
        self._fsm.add_handler(ResourceAgentState.STREAMING, ResourceAgentEvent.ENTER, self._handler_streaming_enter)
        self._fsm.add_handler(ResourceAgentState.STREAMING, ResourceAgentEvent.EXIT, self._handler_streaming_exit)
        
        self._fsm.add_handler(ResourceAgentState.TEST, ResourceAgentEvent.ENTER, self._handler_test_enter)
        self._fsm.add_handler(ResourceAgentState.TEST, ResourceAgentEvent.EXIT, self._handler_test_exit)
        
        self._fsm.add_handler(ResourceAgentState.CALIBRATE, ResourceAgentEvent.ENTER, self._handler_calibrate_enter)
        self._fsm.add_handler(ResourceAgentState.CALIBRATE, ResourceAgentEvent.EXIT, self._handler_calibrate_exit)
        
        self._fsm.add_handler(ResourceAgentState.DIRECT_ACCESS, ResourceAgentEvent.ENTER, self._handler_direct_access_enter)
        self._fsm.add_handler(ResourceAgentState.DIRECT_ACCESS, ResourceAgentEvent.EXIT, self._handler_direct_access_exit)
        
        self._fsm.add_handler(ResourceAgentState.BUSY, ResourceAgentEvent.ENTER, self._handler_busy_enter)
        self._fsm.add_handler(ResourceAgentState.BUSY, ResourceAgentEvent.EXIT, self._handler_busy_exit)
コード例 #4
0
    def __init__(self, event_callback):
        """
        Constructor for singly connected instrument drivers.
        @param event_callback Callback to the driver process to send asynchronous
        driver events back to the agent.
        """
        InstrumentDriver.__init__(self, event_callback)

        # The only and only instrument connection.
        # Exists in the connected state.
        self._connection = None

        # The one and only instrument protocol.
        self._protocol = None

        # Build connection state machine.
        self._connection_fsm = InstrumentFSM(DriverConnectionState,
                                             DriverEvent, DriverEvent.ENTER,
                                             DriverEvent.EXIT)

        # Add handlers for all events.
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.ENTER,
                                         self._handler_unconfigured_enter)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.EXIT,
                                         self._handler_unconfigured_exit)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.INITIALIZE,
                                         self._handler_unconfigured_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.CONFIGURE,
                                         self._handler_unconfigured_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.ENTER,
                                         self._handler_disconnected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.EXIT,
                                         self._handler_disconnected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.INITIALIZE,
                                         self._handler_disconnected_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.CONFIGURE,
                                         self._handler_disconnected_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.CONNECT,
                                         self._handler_disconnected_connect)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED,
                                         DriverEvent.ENTER,
                                         self._handler_connected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED,
                                         DriverEvent.EXIT,
                                         self._handler_connected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED,
                                         DriverEvent.DISCONNECT,
                                         self._handler_connected_disconnect)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.CONNECTION_LOST,
            self._handler_connected_connection_lost)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.DISCOVER,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.GET,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.SET,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.ACQUIRE_SAMPLE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.START_AUTOSAMPLE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.STOP_AUTOSAMPLE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.TEST,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.CALIBRATE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.EXECUTE_DIRECT,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.START_DIRECT,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.STOP_DIRECT,
            self._handler_connected_protocol_event)

        # Start state machine.
        self._connection_fsm.start(DriverConnectionState.UNCONFIGURED)
コード例 #5
0
class SingleConnectionInstrumentDriver(InstrumentDriver):
    """
    Base class for instrument drivers with a single device connection.
    Provides connenction state logic for single connection drivers. This is
    the base class for the majority of driver implementation classes.
    """
    def __init__(self, event_callback):
        """
        Constructor for singly connected instrument drivers.
        @param event_callback Callback to the driver process to send asynchronous
        driver events back to the agent.
        """
        InstrumentDriver.__init__(self, event_callback)

        # The only and only instrument connection.
        # Exists in the connected state.
        self._connection = None

        # The one and only instrument protocol.
        self._protocol = None

        # Build connection state machine.
        self._connection_fsm = InstrumentFSM(DriverConnectionState,
                                             DriverEvent, DriverEvent.ENTER,
                                             DriverEvent.EXIT)

        # Add handlers for all events.
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.ENTER,
                                         self._handler_unconfigured_enter)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.EXIT,
                                         self._handler_unconfigured_exit)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.INITIALIZE,
                                         self._handler_unconfigured_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED,
                                         DriverEvent.CONFIGURE,
                                         self._handler_unconfigured_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.ENTER,
                                         self._handler_disconnected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.EXIT,
                                         self._handler_disconnected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.INITIALIZE,
                                         self._handler_disconnected_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.CONFIGURE,
                                         self._handler_disconnected_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED,
                                         DriverEvent.CONNECT,
                                         self._handler_disconnected_connect)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED,
                                         DriverEvent.ENTER,
                                         self._handler_connected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED,
                                         DriverEvent.EXIT,
                                         self._handler_connected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED,
                                         DriverEvent.DISCONNECT,
                                         self._handler_connected_disconnect)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.CONNECTION_LOST,
            self._handler_connected_connection_lost)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.DISCOVER,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.GET,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.SET,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.ACQUIRE_SAMPLE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.START_AUTOSAMPLE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.STOP_AUTOSAMPLE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.TEST,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.CALIBRATE,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.EXECUTE_DIRECT,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.START_DIRECT,
            self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(
            DriverConnectionState.CONNECTED, DriverEvent.STOP_DIRECT,
            self._handler_connected_protocol_event)

        # Start state machine.
        self._connection_fsm.start(DriverConnectionState.UNCONFIGURED)

    #############################################################
    # Device connection interface.
    #############################################################

    def initialize(self, *args, **kwargs):
        """
        Initialize driver connection, bringing communications parameters
        into unconfigured state (no connection object).
        @raises InstrumentStateException if command not allowed in current state        
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.INITIALIZE, *args,
                                             **kwargs)

    def configure(self, *args, **kwargs):
        """
        Configure the driver for communications with the device via
        port agent / logger (valid but unconnected connection object).
        @param arg[0] comms config dict.
        @raises InstrumentStateException if command not allowed in current state        
        @throws InstrumentParameterException if missing comms or invalid config dict.
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.CONFIGURE, *args,
                                             **kwargs)

    def connect(self, *args, **kwargs):
        """
        Establish communications with the device via port agent / logger
        (connected connection object).
        @raises InstrumentStateException if command not allowed in current state
        @throws InstrumentConnectionException if the connection failed.
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.CONNECT, *args,
                                             **kwargs)

    def disconnect(self, *args, **kwargs):
        """
        Disconnect from device via port agent / logger.
        @raises InstrumentStateException if command not allowed in current state
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.DISCONNECT, *args,
                                             **kwargs)

    #############################################################
    # Commande and control interface.
    #############################################################

    def discover(self, *args, **kwargs):
        """
        Determine initial state upon establishing communications.
        @param timeout=timeout Optional command timeout.        
        @retval Current device state.
        @raises InstrumentTimeoutException if could not wake device.
        @raises InstrumentStateException if command not allowed in current state or if
        device state not recognized.
        @raises NotImplementedException if not implemented by subclass.
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.DISCOVER,
                                             DriverEvent.DISCOVER, *args,
                                             **kwargs)

    def get(self, *args, **kwargs):
        """
        Retrieve device parameters.
        @param args[0] DriverParameter.ALL or a list of parameters to retrive.
        @retval parameter : value dict.
        @raises InstrumentParameterException if missing or invalid get parameters.
        @raises InstrumentStateException if command not allowed in current state
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.GET, DriverEvent.GET,
                                             *args, **kwargs)

    def set(self, *args, **kwargs):
        """
        Set device parameters.
        @param args[0] parameter : value dict of parameters to set.
        @param timeout=timeout Optional command timeout.
        @raises InstrumentParameterException if missing or invalid set parameters.
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if set command not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.SET, DriverEvent.SET,
                                             *args, **kwargs)

    def execute_acquire_sample(self, *args, **kwargs):
        """
        Poll for a sample.
        @param timeout=timeout Optional command timeout.        
        @ retval Device sample dict.
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if acquire command not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.ACQUIRE_SAMPLE,
                                             DriverEvent.ACQUIRE_SAMPLE, *args,
                                             **kwargs)

    def execute_start_autosample(self, *args, **kwargs):
        """
        Switch to autosample mode.
        @param timeout=timeout Optional command timeout.        
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.START_AUTOSAMPLE,
                                             DriverEvent.START_AUTOSAMPLE,
                                             *args, **kwargs)

    def execute_stop_autosample(self, *args, **kwargs):
        """
        Leave autosample mode.
        @param timeout=timeout Optional command timeout.        
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if stop command not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
         """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.STOP_AUTOSAMPLE,
                                             DriverEvent.STOP_AUTOSAMPLE,
                                             *args, **kwargs)

    def execute_test(self, *args, **kwargs):
        """
        Execute device tests.
        @param timeout=timeout Optional command timeout (for wakeup only --
        device specific timeouts for internal test commands).
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if test commands not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.TEST,
                                             DriverEvent.TEST, *args, **kwargs)

    def execute_calibrate(self, *args, **kwargs):
        """
        Execute device calibration.
        @param timeout=timeout Optional command timeout (for wakeup only --
        device specific timeouts for internal calibration commands).
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if test commands not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.CALIBRATE,
                                             DriverEvent.CALIBRATE, *args,
                                             **kwargs)

    ########################################################################
    # Resource query interface.
    ########################################################################

    def get_current_state(self):
        """
        Return current device state. For single connection devices, return
        a single connection state if not connected, and protocol state if connected.
        """
        connection_state = self._connection_fsm.get_current_state()
        if connection_state == DriverConnectionState.CONNECTED:
            return self._protocol.get_current_state()
        else:
            return connection_state

    ########################################################################
    # Unconfigured handlers.
    ########################################################################

    def _handler_unconfigured_enter(self, *args, **kwargs):
        """
        Enter unconfigured state.
        """
        # Send state change event to agent.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_unconfigured_exit(self, *args, **kwargs):
        """
        Exit unconfigured state.
        """
        pass

    def _handler_unconfigured_initialize(self, *args, **kwargs):
        """
        Initialize handler. We are already in unconfigured state, do nothing.
        @retval (next_state, result) tuple, (None, None).
        """
        next_state = None
        result = None

        return (next_state, result)

    def _handler_unconfigured_configure(self, *args, **kwargs):
        """
        Configure driver for device comms.
        @param args[0] Communiations config dictionary.
        @retval (next_state, result) tuple, (DriverConnectionState.DISCONNECTED,
        None) if successful, (None, None) otherwise.
        @raises InstrumentParameterException if missing or invalid param dict.        
        """
        next_state = None
        result = None

        # Get the required param dict.
        try:
            config = args[0]

        except IndexError:
            raise InstrumentParameterException(
                'Missing comms config parameter.')

        # Verify dict and construct connection client.
        try:
            addr = config['addr']
            port = config['port']

            if isinstance(addr, str) and isinstance(port,
                                                    int) and len(addr) > 0:
                self._connection = LoggerClient(addr, port)
                next_state = DriverConnectionState.DISCONNECTED

            else:
                raise InstrumentParameterException(
                    'Invalid comms config dict.')

        except (TypeError, KeyError):
            raise InstrumentParameterException('Invalid comms config dict.')

        return (next_state, result)

    ########################################################################
    # Disconnected handlers.
    ########################################################################

    def _handler_disconnected_enter(self, *args, **kwargs):
        """
        Enter disconnected state.
        """
        # Send state change event to agent.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_disconnected_exit(self, *args, **kwargs):
        """
        Exit disconnected state.
        """
        pass

    def _handler_disconnected_initialize(self, *args, **kwargs):
        """
        Initialize device communications. Causes the connection parameters to
        be reset.
        @retval (next_state, result) tuple, (DriverConnectionState.UNCONFIGURED,
        None).
        """
        next_state = None
        result = None

        self._connection = None
        next_state = DriverConnectionState.UNCONFIGURED

        return (next_state, result)

    def _handler_disconnected_configure(self, *args, **kwargs):
        """
        Configure driver for device comms.
        @param args[0] Communiations config dictionary.
        @retval (next_state, result) tuple, (None, None).
        @raises InstrumentParameterException if missing or invalid param dict.
        """
        next_state = None
        result = None

        # Get required config param dict.
        try:
            config = args[0]

        except IndexError:
            raise InstrumentParameterException(
                'Missing comms config parameter.')

        # Verify configuration dict, and update connection if possible.
        try:
            addr = config['addr']
            port = config['port']

            if isinstance(addr, str) and isinstance(port,
                                                    int) and len(addr) > 0:
                self._connection = LoggerClient(addr, port)

            else:
                raise InstrumentParameterException(
                    'Invalid comms config dict.')

        except (TypeError, KeyError):
            raise InstrumentParameterException('Invalid comms config dict.')

        return (next_state, result)

    def _handler_disconnected_connect(self, *args, **kwargs):
        """
        Establish communications with the device via port agent / logger and
        construct and intialize a protocol FSM for device interaction.
        @retval (next_state, result) tuple, (DriverConnectionState.CONNECTED,
        None) if successful.
        @raises InstrumentConnectionException if the attempt to connect failed.
        """
        next_state = None
        result = None

        self._build_protocol()
        self._connection.init_comms(self._protocol.got_data)
        self._protocol._connection = self._connection
        next_state = DriverConnectionState.CONNECTED

        return (next_state, result)

    ########################################################################
    # Connected handlers.
    ########################################################################

    def _handler_connected_enter(self, *args, **kwargs):
        """
        Enter connected state.
        """
        # Send state change event to agent.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_connected_exit(self, *args, **kwargs):
        """
        Exit connected state.
        """
        pass

    def _handler_connected_disconnect(self, *args, **kwargs):
        """
        Disconnect to the device via port agent / logger and destroy the
        protocol FSM.
        @retval (next_state, result) tuple, (DriverConnectionState.DISCONNECTED,
        None) if successful.
        """
        next_state = None
        result = None

        self._connection.stop_comms()
        self._protocol = None
        next_state = DriverConnectionState.DISCONNECTED

        return (next_state, result)

    def _handler_connected_connection_lost(self, *args, **kwargs):
        """
        The device connection was lost. Stop comms, destroy protocol FSM and
        revert to disconnected state.
        @retval (next_state, result) tuple, (DriverConnectionState.DISCONNECTED,
        None).
        """
        next_state = None
        result = None

        self._connection.stop_comms()
        self._protocol = None
        next_state = DriverConnectionState.DISCONNECTED

        return (next_state, result)

    def _handler_connected_protocol_event(self, event, *args, **kwargs):
        """
        Forward a driver command event to the protocol FSM.
        @param args positional arguments to pass on.
        @param kwargs keyword arguments to pass on.
        @retval (next_state, result) tuple, (None, protocol result).
        """
        next_state = None
        result = self._protocol._protocol_fsm.on_event(event, *args, **kwargs)

        return (next_state, result)

    ########################################################################
    # Helpers.
    ########################################################################

    def _build_protocol(self):
        """
        Construct device specific single connection protocol FSM.
        Overridden in device specific subclasses.
        """
        pass
コード例 #6
0
class SingleConnectionInstrumentDriver(InstrumentDriver):
    """
    Base class for instrument drivers with a single device connection.
    Provides connenction state logic for single connection drivers. This is
    the base class for the majority of driver implementation classes.
    """
    
    def __init__(self, event_callback):
        """
        Constructor for singly connected instrument drivers.
        @param event_callback Callback to the driver process to send asynchronous
        driver events back to the agent.
        """
        InstrumentDriver.__init__(self, event_callback)
        
        # The only and only instrument connection.
        # Exists in the connected state.
        self._connection = None

        # The one and only instrument protocol.
        self._protocol = None
        
        # Build connection state machine.
        self._connection_fsm = InstrumentFSM(DriverConnectionState,
                                                DriverEvent,
                                                DriverEvent.ENTER,
                                                DriverEvent.EXIT)
        
        # Add handlers for all events.
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.ENTER, self._handler_unconfigured_enter)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.EXIT, self._handler_unconfigured_exit)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.INITIALIZE, self._handler_unconfigured_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.UNCONFIGURED, DriverEvent.CONFIGURE, self._handler_unconfigured_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.ENTER, self._handler_disconnected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.EXIT, self._handler_disconnected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.INITIALIZE, self._handler_disconnected_initialize)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.CONFIGURE, self._handler_disconnected_configure)
        self._connection_fsm.add_handler(DriverConnectionState.DISCONNECTED, DriverEvent.CONNECT, self._handler_disconnected_connect)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.ENTER, self._handler_connected_enter)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.EXIT, self._handler_connected_exit)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.DISCONNECT, self._handler_connected_disconnect)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.CONNECTION_LOST, self._handler_connected_connection_lost)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.DISCOVER, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.GET, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.SET, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.ACQUIRE_SAMPLE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.START_AUTOSAMPLE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.STOP_AUTOSAMPLE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.TEST, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.CALIBRATE, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.EXECUTE_DIRECT, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.START_DIRECT, self._handler_connected_protocol_event)
        self._connection_fsm.add_handler(DriverConnectionState.CONNECTED, DriverEvent.STOP_DIRECT, self._handler_connected_protocol_event)
            
        # Start state machine.
        self._connection_fsm.start(DriverConnectionState.UNCONFIGURED)
                
    #############################################################
    # Device connection interface.
    #############################################################
    
    def initialize(self, *args, **kwargs):
        """
        Initialize driver connection, bringing communications parameters
        into unconfigured state (no connection object).
        @raises InstrumentStateException if command not allowed in current state        
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.INITIALIZE, *args, **kwargs)
        
    def configure(self, *args, **kwargs):
        """
        Configure the driver for communications with the device via
        port agent / logger (valid but unconnected connection object).
        @param arg[0] comms config dict.
        @raises InstrumentStateException if command not allowed in current state        
        @throws InstrumentParameterException if missing comms or invalid config dict.
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.CONFIGURE, *args, **kwargs)
        
    def connect(self, *args, **kwargs):
        """
        Establish communications with the device via port agent / logger
        (connected connection object).
        @raises InstrumentStateException if command not allowed in current state
        @throws InstrumentConnectionException if the connection failed.
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.CONNECT, *args, **kwargs)
    
    def disconnect(self, *args, **kwargs):
        """
        Disconnect from device via port agent / logger.
        @raises InstrumentStateException if command not allowed in current state
        """
        # Forward event and argument to the connection FSM.
        return self._connection_fsm.on_event(DriverEvent.DISCONNECT, *args, **kwargs)

    #############################################################
    # Commande and control interface.
    #############################################################

    def discover(self, *args, **kwargs):
        """
        Determine initial state upon establishing communications.
        @param timeout=timeout Optional command timeout.        
        @retval Current device state.
        @raises InstrumentTimeoutException if could not wake device.
        @raises InstrumentStateException if command not allowed in current state or if
        device state not recognized.
        @raises NotImplementedException if not implemented by subclass.
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.DISCOVER, DriverEvent.DISCOVER, *args, **kwargs)

    def get(self, *args, **kwargs):
        """
        Retrieve device parameters.
        @param args[0] DriverParameter.ALL or a list of parameters to retrive.
        @retval parameter : value dict.
        @raises InstrumentParameterException if missing or invalid get parameters.
        @raises InstrumentStateException if command not allowed in current state
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.GET, DriverEvent.GET, *args, **kwargs)

    def set(self, *args, **kwargs):
        """
        Set device parameters.
        @param args[0] parameter : value dict of parameters to set.
        @param timeout=timeout Optional command timeout.
        @raises InstrumentParameterException if missing or invalid set parameters.
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if set command not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.SET, DriverEvent.SET, *args, **kwargs)

    def execute_acquire_sample(self, *args, **kwargs):
        """
        Poll for a sample.
        @param timeout=timeout Optional command timeout.        
        @ retval Device sample dict.
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if acquire command not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.ACQUIRE_SAMPLE, DriverEvent.ACQUIRE_SAMPLE, *args, **kwargs)

    def execute_start_autosample(self, *args, **kwargs):
        """
        Switch to autosample mode.
        @param timeout=timeout Optional command timeout.        
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.START_AUTOSAMPLE, DriverEvent.START_AUTOSAMPLE, *args, **kwargs)

    def execute_stop_autosample(self, *args, **kwargs):
        """
        Leave autosample mode.
        @param timeout=timeout Optional command timeout.        
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if stop command not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
         """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.STOP_AUTOSAMPLE, DriverEvent.STOP_AUTOSAMPLE, *args, **kwargs)

    def execute_test(self, *args, **kwargs):
        """
        Execute device tests.
        @param timeout=timeout Optional command timeout (for wakeup only --
        device specific timeouts for internal test commands).
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if test commands not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.TEST, DriverEvent.TEST, *args, **kwargs)

    def execute_calibrate(self, *args, **kwargs):
        """
        Execute device calibration.
        @param timeout=timeout Optional command timeout (for wakeup only --
        device specific timeouts for internal calibration commands).
        @raises InstrumentTimeoutException if could not wake device or no response.
        @raises InstrumentProtocolException if test commands not recognized.
        @raises InstrumentStateException if command not allowed in current state.
        @raises NotImplementedException if not implemented by subclass.                        
        """
        # Forward event and argument to the protocol FSM.
        return self._connection_fsm.on_event(DriverEvent.CALIBRATE, DriverEvent.CALIBRATE, *args, **kwargs)


    ########################################################################
    # Resource query interface.
    ########################################################################

    def get_current_state(self):
        """
        Return current device state. For single connection devices, return
        a single connection state if not connected, and protocol state if connected.
        """
        connection_state = self._connection_fsm.get_current_state()
        if connection_state == DriverConnectionState.CONNECTED:
            return self._protocol.get_current_state()
        else:
            return connection_state

    ########################################################################
    # Unconfigured handlers.
    ########################################################################

    def _handler_unconfigured_enter(self, *args, **kwargs):
        """
        Enter unconfigured state.
        """
        # Send state change event to agent.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)
    
    def _handler_unconfigured_exit(self, *args, **kwargs):
        """
        Exit unconfigured state.
        """
        pass

    def _handler_unconfigured_initialize(self, *args, **kwargs):
        """
        Initialize handler. We are already in unconfigured state, do nothing.
        @retval (next_state, result) tuple, (None, None).
        """
        next_state = None
        result = None
        
        return (next_state, result)

    def _handler_unconfigured_configure(self, *args, **kwargs):
        """
        Configure driver for device comms.
        @param args[0] Communiations config dictionary.
        @retval (next_state, result) tuple, (DriverConnectionState.DISCONNECTED,
        None) if successful, (None, None) otherwise.
        @raises InstrumentParameterException if missing or invalid param dict.        
        """
        next_state = None
        result = None
        
        # Get the required param dict.
        try:
            config = args[0]
        
        except IndexError:
            raise InstrumentParameterException('Missing comms config parameter.')
        
        # Verify dict and construct connection client.
        try:
            addr = config['addr']
            port = config['port']
            
            if isinstance(addr, str) and isinstance(port, int) and len(addr)>0:                
                self._connection = LoggerClient(addr, port)
                next_state = DriverConnectionState.DISCONNECTED
                            
            else:
                raise InstrumentParameterException('Invalid comms config dict.')
                
        except (TypeError, KeyError):
            raise InstrumentParameterException('Invalid comms config dict.')        
        
        return (next_state, result)

    ########################################################################
    # Disconnected handlers.
    ########################################################################

    def _handler_disconnected_enter(self, *args, **kwargs):
        """
        Enter disconnected state.
        """
        # Send state change event to agent.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_disconnected_exit(self, *args, **kwargs):
        """
        Exit disconnected state.
        """
        pass

    def _handler_disconnected_initialize(self, *args, **kwargs):
        """
        Initialize device communications. Causes the connection parameters to
        be reset.
        @retval (next_state, result) tuple, (DriverConnectionState.UNCONFIGURED,
        None).
        """
        next_state = None
        result = None
        
        self._connection = None
        next_state = DriverConnectionState.UNCONFIGURED
        
        return (next_state, result)

    def _handler_disconnected_configure(self, *args, **kwargs):
        """
        Configure driver for device comms.
        @param args[0] Communiations config dictionary.
        @retval (next_state, result) tuple, (None, None).
        @raises InstrumentParameterException if missing or invalid param dict.
        """
        next_state = None
        result = None
        
        # Get required config param dict.
        try:
            config = args[0]
        
        except IndexError:
            raise InstrumentParameterException('Missing comms config parameter.')

        # Verify configuration dict, and update connection if possible.        
        try:
            addr = config['addr']
            port = config['port']
            
            if isinstance(addr, str) and isinstance(port, int) and len(addr)>0:                
                self._connection = LoggerClient(addr, port)
                            
            else:
                raise InstrumentParameterException('Invalid comms config dict.')
                
        except (TypeError, KeyError):
            raise InstrumentParameterException('Invalid comms config dict.')        
        
        return (next_state, result)

    def _handler_disconnected_connect(self, *args, **kwargs):
        """
        Establish communications with the device via port agent / logger and
        construct and intialize a protocol FSM for device interaction.
        @retval (next_state, result) tuple, (DriverConnectionState.CONNECTED,
        None) if successful.
        @raises InstrumentConnectionException if the attempt to connect failed.
        """
        next_state = None
        result = None
        
        self._build_protocol()
        self._connection.init_comms(self._protocol.got_data)
        self._protocol._connection = self._connection
        next_state = DriverConnectionState.CONNECTED
        
        return (next_state, result)

    ########################################################################
    # Connected handlers.
    ########################################################################

    def _handler_connected_enter(self, *args, **kwargs):
        """
        Enter connected state.
        """
        # Send state change event to agent.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_connected_exit(self, *args, **kwargs):
        """
        Exit connected state.
        """
        pass

    def _handler_connected_disconnect(self, *args, **kwargs):
        """
        Disconnect to the device via port agent / logger and destroy the
        protocol FSM.
        @retval (next_state, result) tuple, (DriverConnectionState.DISCONNECTED,
        None) if successful.
        """
        next_state = None
        result = None
        
        self._connection.stop_comms()
        self._protocol = None
        next_state = DriverConnectionState.DISCONNECTED
        
        return (next_state, result)

    def _handler_connected_connection_lost(self, *args, **kwargs):
        """
        The device connection was lost. Stop comms, destroy protocol FSM and
        revert to disconnected state.
        @retval (next_state, result) tuple, (DriverConnectionState.DISCONNECTED,
        None).
        """
        next_state = None
        result = None

        self._connection.stop_comms()
        self._protocol = None
        next_state = DriverConnectionState.DISCONNECTED
        
        return (next_state, result)

    def _handler_connected_protocol_event(self, event, *args, **kwargs):
        """
        Forward a driver command event to the protocol FSM.
        @param args positional arguments to pass on.
        @param kwargs keyword arguments to pass on.
        @retval (next_state, result) tuple, (None, protocol result).
        """
        next_state = None
        result = self._protocol._protocol_fsm.on_event(event, *args, **kwargs)
        
        return (next_state, result)


    ########################################################################
    # Helpers.
    ########################################################################

    def _build_protocol(self):
        """
        Construct device specific single connection protocol FSM.
        Overridden in device specific subclasses.
        """
        pass
コード例 #7
0
class PlatformDriver(object):
    """
    A platform driver handles a particular platform in a platform network.
    """

    def __init__(self, pnode, evt_recv):
        """
        Creates a PlatformDriver instance.

        @param pnode     Root PlatformNode defining the platform network rooted at
                         this platform.
        @param evt_recv  Listener of events generated by this driver
        """
        assert pnode, "pnode must be given"
        assert evt_recv, "evt_recv parameter must be given"

        self._pnode = pnode
        self._send_event = evt_recv

        self._platform_id = self._pnode.platform_id
        if self._pnode.parent:
            self._parent_platform_id = self._pnode.parent.platform_id
        else:
            self._parent_platform_id = None

        self._platform_attributes = \
            dict((a.attr_id, a.defn) for a in self._pnode.attrs.itervalues())

        if log.isEnabledFor(logging.DEBUG):
            log.debug("%r: PlatformDriver constructor called: pnode:\n%s\n"
                      "_platform_attributes=%s",
                      self._platform_id,
                      NetworkUtil._dump_pnode(self._pnode, include_subplatforms=False),
                      self._platform_attributes)

        self._driver_config = None

        # construct FSM and start it with initial state UNCONFIGURED:
        self._construct_fsm()
        self._fsm.start(PlatformDriverState.UNCONFIGURED)

    def _get_platform_attributes(self):
        """
        Gets a dict of the attribute definitions in this platform as given at
        construction time (from pnode parameter).
        """
        return self._platform_attributes

    def _validate_driver_configuration(self, driver_config):
        """
        Called by configure so a subclass can perform any needed additional
        validation of the provided configuration.
        Nothing is done in this base class. Note that basic validation is
        done by PlatformAgent prior to creating/configuring the driver.

        @param driver_config Driver configuration.

        @raise PlatformDriverException Error in driver configuration.
        """

    def configure(self, driver_config):
        """
        Configures this driver. It first calls _validate_driver_configuration.

        @param driver_config Driver configuration.
        """
        if log.isEnabledFor(logging.DEBUG):
            log.debug("%r: configure: %s" % (self._platform_id, str(driver_config)))

        self._validate_driver_configuration(driver_config)
        self._driver_config = driver_config

    def connect(self):
        """
        To be implemented by subclass.
        Establishes communication with the platform device.

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def disconnect(self):
        """
        To be implemented by subclass.
        Ends communication with the platform device.

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def ping(self):
        """
        To be implemented by subclass.
        Verifies communication with external platform returning "PONG" if
        this verification completes OK.

        @retval "PONG"
        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def get_metadata(self):
        """
        To be implemented by subclass.
        Returns the metadata associated to the platform.

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def get_attribute_values(self, attr_names, from_time):
        """
        To be implemented by subclass.
        Returns the values for specific attributes since a given time.

        @param attr_names [attrName, ...] desired attributes
        @param from_time time from which the values are requested.
                         Assummed to be in the format basically described by
                         pyon's get_ion_ts function, "a str representing an
                         integer number, the millis in UNIX epoch."

        @retval {attrName : [(attrValue, timestamp), ...], ...}
                dict indexed by attribute name with list of (value, timestamp)
                pairs. Timestamps in same format as from_time.
        """
        raise NotImplementedError()  #pragma: no cover

    def set_attribute_values(self, attrs):
        """
        To be implemented by subclass.
        Sets values for writable attributes in this platform.

        @param attrs 	[(attrName, attrValue), ...] 	List of attribute values

        @retval {platform_id: {attrName : [(attrValue, timestamp), ...], ...}}
                dict with a single entry for the requested platform ID and value
                as a list of (value,timestamp) pairs for each attribute indicated
                in the input. Returned timestamps indicate the time when the
                value was set. Each timestamp is "a str representing an
                integer number, the millis in UNIX epoch;" this is to be
                aligned with description of pyon's get_ion_ts function.
        """
        raise NotImplementedError()  #pragma: no cover

    def connect_instrument(self, port_id, instrument_id, attributes):
        """
        To be implemented by subclass.
        Connects an instrument to a port in this platform.

        @param port_id      Port ID
        @param instrument_id Instrument ID
        @param attributes   Attribute dictionary

        @retval The resulting configuration for the instrument.

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def disconnect_instrument(self, port_id, instrument_id):
        """
        To be implemented by subclass.
        Disconnects an instrument from a port in this platform.

        @param port_id      Port ID
        @param instrument_id Instrument ID

        @retval

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def get_connected_instruments(self, port_id):
        """
        To be implemented by subclass.
        Retrieves the IDs of the instruments connected to a port.

        @param port_id      Port ID

        @retval

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def turn_on_port(self, port_id):
        """
        To be implemented by subclass.
        Turns on a port in this platform.

        @param port_id      Port ID

        @retval The resulting on/off of the port.

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def turn_off_port(self, port_id):
        """
        To be implemented by subclass.
        Turns off a port in this platform.

        @param port_id      Port ID

        @retval The resulting on/off of the port.

        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def destroy(self):
        """
        Stops all activity done by the driver. Nothing done in this class.
        (previously it stopped resource monitoring).
        """

    def _notify_driver_event(self, driver_event):
        """
        Convenience method for subclasses to send a driver event to
        corresponding platform agent.

        @param driver_event a DriverEvent object.
        """
        log.debug("platform driver=%r: notify driver_event=%s",
            self._platform_id, driver_event)

        assert isinstance(driver_event, DriverEvent)

        self._send_event(driver_event)

    def get_checksum(self):
        """
        To be implemented by subclass.
        Returns the checksum for this platform.

        @return SHA1 hash value as string of hexadecimal digits.
        @raise PlatformConnectionException
        """
        raise NotImplementedError()  #pragma: no cover

    def get_driver_state(self):
        """
        Returns the current FSM state.
        """
        return self._fsm.get_current_state()

    ##############################################################
    # FSM event handlers.
    ##############################################################

    def _common_state_enter(self, *args, **kwargs):
        """
        Common work upon every state entry.
        Nothing done in this base class.
        @todo determine what should be done, in particular regarding eventual
        notification of the platform driver state transition.
        """
        state = self.get_driver_state()
        if log.isEnabledFor(logging.DEBUG):
            log.debug('%r: driver entering state: %s' % (self._platform_id, state))

        # TODO: publish the platform driver FSM state transition?
        # event_data = {
        #     'state': state
        # }
        # result = self._event_publisher.publish_event(
        #     event_type = ?????,
        #     origin     = ?????,
        #     **event_data)
        # if log.isEnabledFor(logging.DEBUG):
        #     log.debug('%r: PlatformDriver published state change: %s, '
        #               'time: %s result: %s',
        #               self._platform_id, state, get_ion_ts(), str(result))

    def _common_state_exit(self, *args, **kwargs):
        """
        Common work upon every state exit.
        Nothing done in this base class.
        """

    ##############################################################
    # UNCONFIGURED event handlers.
    ##############################################################

    def _handler_unconfigured_configure(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        driver_config = kwargs.get('driver_config', None)
        if driver_config is None:
            raise FSMError('configure: missing driver_config argument')

        try:
            result = self.configure(driver_config)
            next_state = PlatformDriverState.DISCONNECTED
        except PlatformDriverException as e:
            result = None
            next_state = None
            log.error("Error in platform driver configuration", e)

        return next_state, result

    ##############################################################
    # DISCONNECTED event handlers.
    ##############################################################

    def _handler_disconnected_connect(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        result = self.connect()
        next_state = PlatformDriverState.CONNECTED

        return next_state, result

    ##############################################################
    # CONNECTED event handlers.
    ##############################################################

    def _handler_connected_disconnect(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        result = self.disconnect(*args, **kwargs)
        next_state = PlatformDriverState.DISCONNECTED

        return next_state, result

    def _handler_connected_ping(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        result = None
        try:
            result = self.ping()
            next_state = None
        except PlatformConnectionException as e:
            next_state = PlatformDriverState.DISCONNECTED
            log.error("Ping failed", e)

        return next_state, result

    def _handler_connected_get_metadata(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        result = self.get_metadata()
        next_state = None

        return next_state, result

    def _handler_connected_get_attribute_values(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        attr_names = kwargs.get('attr_names', None)
        if attr_names is None:
            raise FSMError('get_attribute_values: missing attr_names argument')

        from_time = kwargs.get('from_time', None)
        if from_time is None:
            raise FSMError('get_attribute_values: missing from_time argument')

        result = self.get_attribute_values(attr_names, from_time)
        next_state = None

        return next_state, result

    def _handler_connected_set_attribute_values(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        attrs = kwargs.get('attrs', None)
        if attrs is None:
            raise FSMError('set_attribute_values: missing attrs argument')

        result = self.set_attribute_values(attrs)
        next_state = None

        return next_state, result

    def _handler_connected_connect_instrument(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('connect_instrument: missing port_id argument')

        instrument_id = kwargs.get('instrument_id', None)
        if instrument_id is None:
            raise FSMError('connect_instrument: missing instrument_id argument')

        attributes = kwargs.get('attributes', None)
        if attributes is None:
            raise FSMError('connect_instrument: missing attributes argument')

        result = self.connect_instrument(port_id, instrument_id, attributes)
        next_state = None

        return next_state, result

    def _handler_disconnected_connect_instrument(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('disconnect_instrument: missing port_id argument')

        instrument_id = kwargs.get('instrument_id', None)
        if instrument_id is None:
            raise FSMError('disconnect_instrument: missing instrument_id argument')

        result = self.disconnect_instrument(port_id, instrument_id)
        next_state = None

        return next_state, result

    def _handler_connected_get_connected_instruments(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('get_connected_instruments: missing port_id argument')

        result = self.get_connected_instruments(port_id)
        next_state = None

        return next_state, result

    def _handler_connected_turn_on_port(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('turn_on_port: missing port_id argument')

        result = self.turn_on_port(port_id)
        next_state = None

        return next_state, result

    def _handler_connected_turn_off_port(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('turn_off_port: missing port_id argument')

        result = self.turn_off_port(port_id)
        next_state = None

        return next_state, result

    def _handler_connected_get_checksum(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        result = self.get_checksum()
        next_state = None

        return next_state, result

    ##############################################################
    # Platform driver FSM setup
    ##############################################################

    def _construct_fsm(self):
        """
        """
        log.debug("constructing fsm")

        self._fsm = InstrumentFSM(PlatformDriverState,
                                  PlatformDriverEvent,
                                  PlatformDriverEvent.ENTER,
                                  PlatformDriverEvent.EXIT)

        for state in PlatformDriverState.list():
            self._fsm.add_handler(state, PlatformDriverEvent.ENTER, self._common_state_enter)
            self._fsm.add_handler(state, PlatformDriverEvent.EXIT, self._common_state_exit)

        # UNCONFIGURED state event handlers:
        self._fsm.add_handler(PlatformDriverState.UNCONFIGURED, PlatformDriverEvent.CONFIGURE, self._handler_unconfigured_configure)

        # DISCONNECTED state event handlers:
        self._fsm.add_handler(PlatformDriverState.DISCONNECTED, PlatformDriverEvent.CONNECT, self._handler_disconnected_connect)

        # CONNECTED state event handlers:
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.DISCONNECT, self._handler_connected_disconnect)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.CONNECTION_LOST, self._handler_connected_disconnect)

        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.PING, self._handler_connected_ping)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_METADATA, self._handler_connected_get_metadata)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_ATTRIBUTE_VALUES, self._handler_connected_get_attribute_values)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.SET_ATTRIBUTE_VALUES, self._handler_connected_set_attribute_values)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.CONNECT_INSTRUMENT, self._handler_connected_connect_instrument)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.DISCONNECT_INSTRUMENT, self._handler_disconnected_connect_instrument)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_CONNECTED_INSTRUMENTS, self._handler_connected_get_connected_instruments)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.TURN_ON_PORT, self._handler_connected_turn_on_port)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.TURN_OFF_PORT, self._handler_connected_turn_off_port)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, PlatformDriverEvent.GET_CHECKSUM, self._handler_connected_get_checksum)
コード例 #8
0
ファイル: sbe37_driver.py プロジェクト: dstuebe/coi-services
    def __init__(self, prompts, newline, driver_event):
        """
        SBE37Protocol constructor.
        @param prompts A BaseEnum class containing instrument prompts.
        @param newline The SBE37 newline.
        @param driver_event Driver process event callback.
        """
        # Construct protocol superclass.
        CommandResponseInstrumentProtocol.__init__(self, prompts, newline, driver_event)
        
        # Build SBE37 protocol state machine.
        self._protocol_fsm = InstrumentFSM(SBE37ProtocolState, SBE37ProtocolEvent,
                            SBE37ProtocolEvent.ENTER, SBE37ProtocolEvent.EXIT)

        # Add event handlers for protocol state machine.
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN, SBE37ProtocolEvent.ENTER, self._handler_unknown_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN, SBE37ProtocolEvent.EXIT, self._handler_unknown_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN, SBE37ProtocolEvent.DISCOVER, self._handler_unknown_discover)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.ENTER, self._handler_command_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.EXIT, self._handler_command_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.ACQUIRE_SAMPLE, self._handler_command_acquire_sample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.START_AUTOSAMPLE, self._handler_command_start_autosample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.GET, self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.SET, self._handler_command_set)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.TEST, self._handler_command_test)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.START_DIRECT, self._handler_command_start_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.ENTER, self._handler_autosample_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.EXIT, self._handler_autosample_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.GET, self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.STOP_AUTOSAMPLE, self._handler_autosample_stop_autosample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.ENTER, self._handler_test_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.EXIT, self._handler_test_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.RUN_TEST, self._handler_test_run_tests)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.GET, self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.ENTER, self._handler_direct_access_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.EXIT, self._handler_direct_access_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.EXECUTE_DIRECT, self._handler_direct_access_execute_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.STOP_DIRECT, self._handler_direct_access_stop_direct)

        # Construct the parameter dictionary containing device parameters,
        # current parameter values, and set formatting functions.
        self._build_param_dict()

        # Add build handlers for device commands.
        self._add_build_handler('ds', self._build_simple_command)
        self._add_build_handler('dc', self._build_simple_command)
        self._add_build_handler('ts', self._build_simple_command)
        self._add_build_handler('startnow', self._build_simple_command)
        self._add_build_handler('stop', self._build_simple_command)
        self._add_build_handler('tc', self._build_simple_command)
        self._add_build_handler('tt', self._build_simple_command)
        self._add_build_handler('tp', self._build_simple_command)
        self._add_build_handler('set', self._build_set_command)

        # Add response handlers for device commands.
        self._add_response_handler('ds', self._parse_dsdc_response)
        self._add_response_handler('dc', self._parse_dsdc_response)
        self._add_response_handler('ts', self._parse_ts_response)
        self._add_response_handler('set', self._parse_set_response)
        self._add_response_handler('tc', self._parse_test_response)
        self._add_response_handler('tt', self._parse_test_response)
        self._add_response_handler('tp', self._parse_test_response)

       # Add sample handlers.
        self._sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        self._sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        self._sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        self._sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'        
        self._sample_regex = re.compile(self._sample_pattern)

        # State state machine in UNKNOWN state. 
        self._protocol_fsm.start(SBE37ProtocolState.UNKNOWN)
        
        # commands sent sent to device to be filtered in responses for telnet DA
        self._sent_cmds = []
コード例 #9
0
ファイル: sbe37_driver.py プロジェクト: dstuebe/coi-services
class SBE37Protocol(CommandResponseInstrumentProtocol):
    """
    Instrument protocol class for SBE37 driver.
    Subclasses CommandResponseInstrumentProtocol
    """
    def __init__(self, prompts, newline, driver_event):
        """
        SBE37Protocol constructor.
        @param prompts A BaseEnum class containing instrument prompts.
        @param newline The SBE37 newline.
        @param driver_event Driver process event callback.
        """
        # Construct protocol superclass.
        CommandResponseInstrumentProtocol.__init__(self, prompts, newline, driver_event)
        
        # Build SBE37 protocol state machine.
        self._protocol_fsm = InstrumentFSM(SBE37ProtocolState, SBE37ProtocolEvent,
                            SBE37ProtocolEvent.ENTER, SBE37ProtocolEvent.EXIT)

        # Add event handlers for protocol state machine.
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN, SBE37ProtocolEvent.ENTER, self._handler_unknown_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN, SBE37ProtocolEvent.EXIT, self._handler_unknown_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN, SBE37ProtocolEvent.DISCOVER, self._handler_unknown_discover)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.ENTER, self._handler_command_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.EXIT, self._handler_command_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.ACQUIRE_SAMPLE, self._handler_command_acquire_sample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.START_AUTOSAMPLE, self._handler_command_start_autosample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.GET, self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.SET, self._handler_command_set)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.TEST, self._handler_command_test)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.START_DIRECT, self._handler_command_start_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.ENTER, self._handler_autosample_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.EXIT, self._handler_autosample_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.GET, self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.STOP_AUTOSAMPLE, self._handler_autosample_stop_autosample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.ENTER, self._handler_test_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.EXIT, self._handler_test_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.RUN_TEST, self._handler_test_run_tests)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST, SBE37ProtocolEvent.GET, self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.ENTER, self._handler_direct_access_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.EXIT, self._handler_direct_access_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.EXECUTE_DIRECT, self._handler_direct_access_execute_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS, SBE37ProtocolEvent.STOP_DIRECT, self._handler_direct_access_stop_direct)

        # Construct the parameter dictionary containing device parameters,
        # current parameter values, and set formatting functions.
        self._build_param_dict()

        # Add build handlers for device commands.
        self._add_build_handler('ds', self._build_simple_command)
        self._add_build_handler('dc', self._build_simple_command)
        self._add_build_handler('ts', self._build_simple_command)
        self._add_build_handler('startnow', self._build_simple_command)
        self._add_build_handler('stop', self._build_simple_command)
        self._add_build_handler('tc', self._build_simple_command)
        self._add_build_handler('tt', self._build_simple_command)
        self._add_build_handler('tp', self._build_simple_command)
        self._add_build_handler('set', self._build_set_command)

        # Add response handlers for device commands.
        self._add_response_handler('ds', self._parse_dsdc_response)
        self._add_response_handler('dc', self._parse_dsdc_response)
        self._add_response_handler('ts', self._parse_ts_response)
        self._add_response_handler('set', self._parse_set_response)
        self._add_response_handler('tc', self._parse_test_response)
        self._add_response_handler('tt', self._parse_test_response)
        self._add_response_handler('tp', self._parse_test_response)

       # Add sample handlers.
        self._sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        self._sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        self._sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        self._sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'        
        self._sample_regex = re.compile(self._sample_pattern)

        # State state machine in UNKNOWN state. 
        self._protocol_fsm.start(SBE37ProtocolState.UNKNOWN)
        
        # commands sent sent to device to be filtered in responses for telnet DA
        self._sent_cmds = []



    ########################################################################
    # Unknown handlers.
    ########################################################################

    def _handler_unknown_enter(self, *args, **kwargs):
        """
        Enter unknown state.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)
    
    def _handler_unknown_exit(self, *args, **kwargs):
        """
        Exit unknown state.
        """
        pass

    def _handler_unknown_discover(self, *args, **kwargs):
        """
        Discover current state; can be COMMAND or AUTOSAMPLE.
        @retval (next_state, result), (SBE37ProtocolState.COMMAND or
        SBE37State.AUTOSAMPLE, None) if successful.
        @throws InstrumentTimeoutException if the device cannot be woken.
        @throws InstrumentStateException if the device response does not correspond to
        an expected state.
        """
        next_state = None
        result = None
        
        # Wakeup the device with timeout if passed.
        timeout = kwargs.get('timeout', SBE37_TIMEOUT)
        prompt = self._wakeup(timeout)
        prompt = self._wakeup(timeout)
        
        # Set the state to change.
        # Raise if the prompt returned does not match command or autosample.
        if prompt == SBE37Prompt.COMMAND:
            next_state = SBE37ProtocolState.COMMAND
            result = SBE37ProtocolState.COMMAND
        elif prompt == SBE37Prompt.AUTOSAMPLE:
            next_state = SBE37ProtocolState.AUTOSAMPLE
            result = SBE37ProtocolState.AUTOSAMPLE
        else:
            raise InstrumentStateException('Unknown state.')
            
        return (next_state, result)

    ########################################################################
    # Command handlers.
    ########################################################################

    def _handler_command_enter(self, *args, **kwargs):
        """
        Enter command state.
        @throws InstrumentTimeoutException if the device cannot be woken.
        @throws InstrumentProtocolException if the update commands and not recognized.
        """
        # Command device to update parameters and send a config change event.
        self._update_params()

        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)
            
    def _handler_command_exit(self, *args, **kwargs):
        """
        Exit command state.
        """
        pass

    def _handler_command_set(self, *args, **kwargs):
        """
        Perform a set command.
        @param args[0] parameter : value dict.
        @retval (next_state, result) tuple, (None, None).
        @throws InstrumentParameterException if missing set parameters, if set parameters not ALL and
        not a dict, or if paramter can't be properly formatted.
        @throws InstrumentTimeoutException if device cannot be woken for set command.
        @throws InstrumentProtocolException if set command could not be built or misunderstood.
        """
        next_state = None
        result = None

        # Retrieve required parameter.
        # Raise if no parameter provided, or not a dict.
        try:
            params = args[0]
            
        except IndexError:
            raise InstrumentParameterException('Set command requires a parameter dict.')

        if not isinstance(params, dict):
            raise InstrumentParameterException('Set parameters not a dict.')
        
        # For each key, val in the dict, issue set command to device.
        # Raise if the command not understood.
        else:
            
            for (key, val) in params.iteritems():
                result = self._do_cmd_resp('set', key, val, **kwargs)
            self._update_params()
            
        return (next_state, result)

    def _handler_command_acquire_sample(self, *args, **kwargs):
        """
        Acquire sample from SBE37.
        @retval (next_state, result) tuple, (None, sample dict).        
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command could not be built or misunderstood.
        @throws SampleException if a sample could not be extracted from result.
        """
        next_state = None
        result = None

        result = self._do_cmd_resp('ts', *args, **kwargs)
        
        return (next_state, result)

    def _handler_command_start_autosample(self, *args, **kwargs):
        """
        Switch into autosample mode.
        @retval (next_state, result) tuple, (SBE37ProtocolState.AUTOSAMPLE,
        None) if successful.
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command could not be built or misunderstood.
        """
        next_state = None
        result = None

        # Assure the device is transmitting.
        if not self._param_dict.get(SBE37Parameter.TXREALTIME):
            self._do_cmd_resp('set', SBE37Parameter.TXREALTIME, True, **kwargs)
        
        # Issue start command and switch to autosample if successful.
        self._do_cmd_no_resp('startnow', *args, **kwargs)
                
        next_state = SBE37ProtocolState.AUTOSAMPLE        
        
        return (next_state, result)

    def _handler_command_test(self, *args, **kwargs):
        """
        Switch to test state to perform instrument tests.
        @retval (next_state, result) tuple, (SBE37ProtocolState.TEST, None).
        """
        next_state = None
        result = None

        next_state = SBE37ProtocolState.TEST
        
        return (next_state, result)

    def _handler_command_start_direct(self):
        """
        """
        next_state = None
        result = None

        next_state = SBE37ProtocolState.DIRECT_ACCESS
        
        return (next_state, result)

    ########################################################################
    # Autosample handlers.
    ########################################################################

    def _handler_autosample_enter(self, *args, **kwargs):
        """
        Enter autosample state.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.        
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)
    
    def _handler_autosample_exit(self, *args, **kwargs):
        """
        Exit autosample state.
        """
        pass

    def _handler_autosample_stop_autosample(self, *args, **kwargs):
        """
        Stop autosample and switch back to command mode.
        @retval (next_state, result) tuple, (SBE37ProtocolState.COMMAND,
        None) if successful.
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command misunderstood or
        incorrect prompt received.
        """
        next_state = None
        result = None

        # Wake up the device, continuing until autosample prompt seen.
        timeout = kwargs.get('timeout', SBE37_TIMEOUT)
        self._wakeup_until(timeout, SBE37Prompt.AUTOSAMPLE)

        # Issue the stop command.
        self._do_cmd_resp('stop', *args, **kwargs)        
        
        # Prompt device until command prompt is seen.
        self._wakeup_until(timeout, SBE37Prompt.COMMAND)
        
        next_state = SBE37ProtocolState.COMMAND

        return (next_state, result)
        
    ########################################################################
    # Common handlers.
    ########################################################################

    def _handler_command_autosample_test_get(self, *args, **kwargs):
        """
        Get device parameters from the parameter dict.
        @param args[0] list of parameters to retrieve, or DriverParameter.ALL.
        @throws InstrumentParameterException if missing or invalid parameter.
        """
        next_state = None
        result = None

        # Retrieve the required parameter, raise if not present.
        try:
            params = args[0]
           
        except IndexError:
            raise InstrumentParameterException('Get command requires a parameter list or tuple.')

        # If all params requested, retrieve config.
        if params == DriverParameter.ALL:
            result = self._param_dict.get_config()
                    
        # If not all params, confirm a list or tuple of params to retrieve.
        # Raise if not a list or tuple.
        # Retireve each key in the list, raise if any are invalid.
        else:
            if not isinstance(params, (list, tuple)):
                raise InstrumentParameterException('Get argument not a list or tuple.')
            result = {}
            for key in params:
                try:
                    val = self._param_dict.get(key)
                    result[key] = val

                except KeyError:
                    raise InstrumentParameterException(('%s is not a valid parameter.' % key))
            
        return (next_state, result)

    ########################################################################
    # Test handlers.
    ########################################################################

    def _handler_test_enter(self, *args, **kwargs):
        """
        Enter test state. Setup the secondary call to run the tests.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.        
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)
        
        # Forward the test event again to run the test handler and
        # switch back to command mode afterward.
        Timer(1, lambda: self._protocol_fsm.on_event(SBE37ProtocolEvent.RUN_TEST)).start()
    
    def _handler_test_exit(self, *args, **kwargs):
        """
        Exit test state.
        """
        pass

    def _handler_test_run_tests(self, *args, **kwargs):
        """
        Run test routines and validate results.
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command misunderstood or
        incorrect prompt received.
        """
        next_state = None
        result = None

        tc_pass = False
        tt_pass = False
        tp_pass = False
        tc_result = None
        tt_result = None
        tp_result = None

        test_result = {}

        try:
            tc_pass, tc_result = self._do_cmd_resp('tc', timeout=200)
            tt_pass, tt_result = self._do_cmd_resp('tt', timeout=200)
            tp_pass, tp_result = self._do_cmd_resp('tp', timeout=200)
        
        except Exception as e:
            test_result['exception'] = e
            test_result['message'] = 'Error running instrument tests.'
        
        finally:
            test_result['cond_test'] = 'Passed' if tc_pass else 'Failed'
            test_result['cond_data'] = tc_result
            test_result['temp_test'] = 'Passed' if tt_pass else 'Failed'
            test_result['temp_data'] = tt_result
            test_result['pres_test'] = 'Passed' if tp_pass else 'Failed'
            test_result['pres_data'] = tp_result
            test_result['success'] = 'Passed' if (tc_pass and tt_pass and tp_pass) else 'Failed'
            
        self._driver_event(DriverAsyncEvent.TEST_RESULT, test_result)
        next_state = SBE37ProtocolState.COMMAND
 
        return (next_state, result)

    ########################################################################
    # Direct access handlers.
    ########################################################################

    def _handler_direct_access_enter(self, *args, **kwargs):
        """
        Enter direct access state.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.                
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)
        
        self._sent_cmds = []
    
    def _handler_direct_access_exit(self, *args, **kwargs):
        """
        Exit direct access state.
        """
        pass

    def _handler_direct_access_execute_direct(self, data):
        """
        """
        next_state = None
        result = None

        self._do_cmd_direct(data)
                        
        # add sent command to list for 'echo' filtering in callback
        self._sent_cmds.append(data)        

        return (next_state, result)

    def _handler_direct_access_stop_direct(self):
        """
        @throw InstrumentProtocolException on invalid command
        """
        next_state = None
        result = None

        next_state = SBE37ProtocolState.COMMAND
            
        return (next_state, result)

    ########################################################################
    # Private helpers.
    ########################################################################
        
    def _send_wakeup(self):
        """
        Send a newline to attempt to wake the SBE37 device.
        """
        self._connection.send(SBE37_NEWLINE)
                
    def _update_params(self, *args, **kwargs):
        """
        Update the parameter dictionary. Wake the device then issue
        display status and display calibration commands. The parameter
        dict will match line output and udpate itself.
        @throws InstrumentTimeoutException if device cannot be timely woken.
        @throws InstrumentProtocolException if ds/dc misunderstood.
        """

        
        # Get old param dict config.
        old_config = self._param_dict.get_config()
        
        # Issue display commands and parse results.
        timeout = kwargs.get('timeout', SBE37_TIMEOUT)
        self._do_cmd_resp('ds',timeout=timeout)
        self._do_cmd_resp('dc',timeout=timeout)
        
        # Get new param dict config. If it differs from the old config,
        # tell driver superclass to publish a config change event.
        new_config = self._param_dict.get_config()
        if new_config != old_config:
            self._driver_event(DriverAsyncEvent.CONFIG_CHANGE)
        
    def _build_simple_command(self, cmd):
        """
        Build handler for basic SBE37 commands.
        @param cmd the simple sbe37 command to format.
        @retval The command to be sent to the device.
        """
        return cmd+SBE37_NEWLINE
    
    def _build_set_command(self, cmd, param, val):
        """
        Build handler for set commands. param=val followed by newline.
        String val constructed by param dict formatting function.
        @param param the parameter key to set.
        @param val the parameter value to set.
        @ retval The set command to be sent to the device.
        @throws InstrumentProtocolException if the parameter is not valid or
        if the formatting function could not accept the value passed.
        """
        try:
            str_val = self._param_dict.format(param, val)
            set_cmd = '%s=%s' % (param, str_val)
            set_cmd = set_cmd + SBE37_NEWLINE
            
        except KeyError:
            raise InstrumentParameterException('Unknown driver parameter %s' % param)
            
        return set_cmd

    def _parse_set_response(self, response, prompt):
        """
        Parse handler for set command.
        @param response command response string.
        @param prompt prompt following command response.        
        @throws InstrumentProtocolException if set command misunderstood.
        """
        if prompt != SBE37Prompt.COMMAND:
            raise InstrumentProtocolException('Set command not recognized: %s' % response)

    def _parse_dsdc_response(self, response, prompt):
        """
        Parse handler for dsdc commands.
        @param response command response string.
        @param prompt prompt following command response.        
        @throws InstrumentProtocolException if dsdc command misunderstood.
        """
        if prompt != SBE37Prompt.COMMAND:
            raise InstrumentProtocolException('dsdc command not recognized: %s.' % response)
            
        for line in response.split(SBE37_NEWLINE):
            self._param_dict.update(line)
        
    def _parse_ts_response(self, response, prompt):
        """
        Response handler for ts command.
        @param response command response string.
        @param prompt prompt following command response.
        @retval sample dictionary containig c, t, d values.
        @throws InstrumentProtocolException if ts command misunderstood.
        @throws InstrumentSampleException if response did not contain a sample
        """
        
        if prompt != SBE37Prompt.COMMAND:
            raise InstrumentProtocolException('ts command not recognized: %s', response)
        
        sample = None
        for line in response.split(SBE37_NEWLINE):
            sample = self._extract_sample(line, True)
            if sample:
                break
        
        if not sample:     
            raise SampleException('Response did not contain sample: %s' % repr(response))
            
        return sample
                
    def _parse_test_response(self, response, prompt):
        """
        Do minimal checking of test outputs.
        @param response command response string.
        @param promnpt prompt following command response.
        @retval tuple of pass/fail boolean followed by response
        """
        
        success = False
        lines = response.split()
        if len(lines)>2:
            data = lines[1:-1]
            bad_count = 0
            for item in data:
                try:
                    float(item)
                    
                except ValueError:
                    bad_count += 1
            
            if bad_count == 0:
                success = True
        
        return (success, response)        
                
    def got_data(self, data):
        """
        Callback for receiving new data from the device.
        """
        if self.get_current_state() == SBE37ProtocolState.DIRECT_ACCESS:
            # direct access mode
            if len(data) > 0:
                mi_logger.debug("SBE37Protocol._got_data(): <" + data + ">") 
                if self._driver_event:
                    self._driver_event(DriverAsyncEvent.DIRECT_ACCESS, data)
                    # TODO: what about logging this as an event?
            return
        
        if len(data)>0:
            # Call the superclass to update line and prompt buffers.
            CommandResponseInstrumentProtocol.got_data(self, data)
    
            # If in streaming mode, process the buffer for samples to publish.
            cur_state = self.get_current_state()
            if cur_state == SBE37ProtocolState.AUTOSAMPLE:
                if SBE37_NEWLINE in self._linebuf:
                    lines = self._linebuf.split(SBE37_NEWLINE)
                    self._linebuf = lines[-1]
                    for line in lines:
                        self._extract_sample(line)                    
                
    def _extract_sample(self, line, publish=True):
        """
        Extract sample from a response line if present and publish to agent.
        @param line string to match for sample.
        @param publsih boolean to publish sample (default True).
        @retval Sample dictionary if present or None.
        """
        sample = None
        match = self._sample_regex.match(line)
        if match:
            sample = {}
            sample['t'] = [float(match.group(1))]
            sample['c'] = [float(match.group(2))]
            sample['p'] = [float(match.group(3))]

            # Driver timestamp.
            sample['time'] = [time.time()]
            sample['stream_name'] = 'ctd_parsed'

            if self._driver_event:
                self._driver_event(DriverAsyncEvent.SAMPLE, sample)

        return sample            
        
    def _build_param_dict(self):
        """
        Populate the parameter dictionary with SBE37 parameters.
        For each parameter key, add match stirng, match lambda function,
        and value formatting function for set commands.
        """
        # Add parameter handlers to parameter dict.        
        self._param_dict.add(SBE37Parameter.OUTPUTSAL,
                             r'(do not )?output salinity with each sample',
                             lambda match : False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.OUTPUTSV,
                             r'(do not )?output sound velocity with each sample',
                             lambda match : False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.NAVG,
                             r'number of samples to average = (\d+)',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.SAMPLENUM,
                             r'samplenumber = (\d+), free = \d+',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.INTERVAL,
                             r'sample interval = (\d+) seconds',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.STORETIME,
                             r'(do not )?store time with each sample',
                             lambda match : False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.TXREALTIME,
                             r'(do not )?transmit real-time data',
                             lambda match : False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.SYNCMODE,
                             r'serial sync mode (enabled|disabled)',
                             lambda match : False if (match.group(1)=='disabled') else True,
                             self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.SYNCWAIT,
                             r'wait time after serial sync sampling = (\d+) seconds',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.TCALDATE,
                             r'temperature: +((\d+)-([a-zA-Z]+)-(\d+))',
                             lambda match : self._string_to_date(match.group(1), '%d-%b-%y'),
                             self._date_to_string)
        self._param_dict.add(SBE37Parameter.TA0,
                             r' +TA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.TA1,
                             r' +TA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.TA2,
                             r' +TA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.TA3,
                             r' +TA3 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CCALDATE,
                             r'conductivity: +((\d+)-([a-zA-Z]+)-(\d+))',
                             lambda match : self._string_to_date(match.group(1), '%d-%b-%y'),
                             self._date_to_string)
        self._param_dict.add(SBE37Parameter.CG,
                             r' +G = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CH,
                             r' +H = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CI,
                             r' +I = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CJ,
                             r' +J = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.WBOTC,
                             r' +WBOTC = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CTCOR,
                             r' +CTCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CPCOR,
                             r' +CPCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PCALDATE,
                             r'pressure .+ ((\d+)-([a-zA-Z]+)-(\d+))',
                             lambda match : self._string_to_date(match.group(1), '%d-%b-%y'),
                             self._date_to_string)
        self._param_dict.add(SBE37Parameter.PA0,
                             r' +PA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PA1,
                             r' +PA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PA2,
                             r' +PA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCA0,
                             r' +PTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCA1,
                             r' +PTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCA2,
                             r' +PTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCB0,
                             r' +PTCSB0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCB1,
                             r' +PTCSB1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCB2,
                             r' +PTCSB2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.POFFSET,
                             r' +POFFSET = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.RCALDATE,
                             r'rtc: +((\d+)-([a-zA-Z]+)-(\d+))',
                             lambda match : self._string_to_date(match.group(1), '%d-%b-%y'),
                             self._date_to_string)
        self._param_dict.add(SBE37Parameter.RTCA0,
                             r' +RTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.RTCA1,
                             r' +RTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.RTCA2,
                             r' +RTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
    

    ########################################################################
    # Static helpers to format set commands.
    ########################################################################

    @staticmethod
    def _true_false_to_string(v):
        """
        Write a boolean value to string formatted for sbe37 set operations.
        @param v a boolean value.
        @retval A yes/no string formatted for sbe37 set operations.
        @throws InstrumentParameterException if value not a bool.
        """
        
        if not isinstance(v,bool):
            raise InstrumentParameterException('Value %s is not a bool.' % str(v))
        if v:
            return 'y'
        else:
            return 'n'

    @staticmethod
    def _int_to_string(v):
        """
        Write an int value to string formatted for sbe37 set operations.
        @param v An int val.
        @retval an int string formatted for sbe37 set operations.
        @throws InstrumentParameterException if value not an int.
        """
        
        if not isinstance(v,int):
            raise InstrumentParameterException('Value %s is not an int.' % str(v))
        else:
            return '%i' % v

    @staticmethod
    def _float_to_string(v):
        """
        Write a float value to string formatted for sbe37 set operations.
        @param v A float val.
        @retval a float string formatted for sbe37 set operations.
        @throws InstrumentParameterException if value is not a float.
        """

        if not isinstance(v,float):
            raise InstrumentParameterException('Value %s is not a float.' % v)
        else:
            return '%e' % v

    @staticmethod
    def _date_to_string(v):
        """
        Write a date tuple to string formatted for sbe37 set operations.
        @param v a date tuple: (day,month,year).
        @retval A date string formatted for sbe37 set operations.
        @throws InstrumentParameterException if date tuple is not valid.
        """

        if not isinstance(v,(list,tuple)):
            raise InstrumentParameterException('Value %s is not a list, tuple.' % str(v))
        
        if not len(v)==3:
            raise InstrumentParameterException('Value %s is not length 3.' % str(v))
        
        months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep',
                  'Oct','Nov','Dec']
        day = v[0]
        month = v[1]
        year = v[2]
        
        if len(str(year)) > 2:
            year = int(str(year)[-2:])
        
        if not isinstance(day,int) or day < 1 or day > 31:
            raise InstrumentParameterException('Value %s is not a day of month.' % str(day))
        
        if not isinstance(month,int) or month < 1 or month > 12:
            raise InstrumentParameterException('Value %s is not a month.' % str(month))

        if not isinstance(year,int) or year < 0 or year > 99:
            raise InstrumentParameterException('Value %s is not a 0-99 year.' % str(year))
        
        return '%02i-%s-%02i' % (day,months[month-1],year)

    @staticmethod
    def _string_to_date(datestr,fmt):
        """
        Extract a date tuple from an sbe37 date string.
        @param str a string containing date information in sbe37 format.
        @retval a date tuple.
        @throws InstrumentParameterException if datestr cannot be formatted to
        a date.
        """
        if not isinstance(datestr,str):
            raise InstrumentParameterException('Value %s is not a string.' % str(datestr))
        try:
            date_time = time.strptime(datestr,fmt)
            date = (date_time[2],date_time[1],date_time[0])

        except ValueError:
            raise InstrumentParameterException('Value %s could not be formatted to a date.' % str(datestr))
                        
        return date
コード例 #10
0
ファイル: agent.py プロジェクト: pombredanne/coi-services
class ResourceAgent(BaseResourceAgent):
    """
    A resource agent is an ION process of type "agent" that exposes the standard
    resource agent service interface. This base class captures the mechanisms
    common to all resource agents and is subclassed with implementations
    specific for instrument agents, user agents, etc.
    """
    
    ##############################################################
    # Class variables.
    ##############################################################

    # ION process type.
    process_type = "agent"

    # Override in subclass to publish specific types of events.
    COMMAND_EVENT_TYPE = "ResourceCommandEvent"

    # Override in subclass to set specific origin type.
    ORIGIN_TYPE = "Resource"
    
    # Override in subclass to expose agent capabilities.
    CAPABILITIES = []
    
    ##############################################################
    # Constructor and ION init/deinit.
    ##############################################################    
    
    def __init__(self, *args, **kwargs):
        """
        Initialize superclass and id variables.
        """
        
        # Base class constructor.        
        super(ResourceAgent, self).__init__(*args, **kwargs)

        # The ID of the AgentInstance subtype resource object.
        self.agent_id = None

        # The ID of the AgentDefinition subtype resource object.
        self.agent_def_id = None

        # The ID of the target resource object, e.g. a device id.
        self.resource_id = None

        # UUID of the current mutex.
        self._mutex_id = None

        # Event publisher.
        self._event_publisher = None
                
        # An example agent parameter.
        self.aparam_example = None

        # Set intial state.        
        if 'initial_state' in kwargs:
            if ResourceAgentState.has(kwargs['initial_state']):
                self._initial_state = kwargs['initial_state']
        
        else:
            self._initial_state = ResourceAgentState.UNINITIALIZED

        # Construct the default state machine.
        self._construct_fsm()

    def _on_init(self):
        """
        ION on_init initializer called once the process exists.
        """
        log.debug("Resource Agent initializing. name=%s, resource_id=%s"
                  % (self._proc_name, self.resource_id))

        # Create event publisher.
        self._event_publisher = EventPublisher()

        # Start state machine.
        self._fsm.start(self._initial_state)

    def _on_quit(self):
        """
        ION on_quit called prior to terminating the process.
        """
        pass

    ##############################################################
    # Governance interface.
    ##############################################################    

    def negotiate(self, resource_id="", sap_in=None):
        """
        TBD.
        """
        pass

    ##############################################################
    # Capabilities interface.
    ##############################################################    

    def get_capabilities(self, resource_id="", current_state=True):
        """
        """
        
        agent_cmds = self._fsm.get_events(current_state)
        agent_cmds = self._filter_capabilities(agent_cmds)
        agent_params = self.get_agent_parameters()
        
        try:
            [res_cmds, res_params] = self._fsm.on_event(ResourceAgentEvent.GET_RESOURCE_CAPABILITIES, current_state)

        except FSMStateError:
            res_cmds = []
            res_params = []
                
        caps = []
        for item in agent_cmds:
            cap = IonObject('AgentCapability', name=item,
                            cap_type=CapabilityType.AGT_CMD)
            caps.append(cap)
        for item in agent_params:
            cap = IonObject('AgentCapability', name=item,
                            cap_type=CapabilityType.AGT_PAR)
            caps.append(cap)
        for item in res_cmds:
            cap = IonObject('AgentCapability', name=item,
                            cap_type=CapabilityType.RES_CMD)
            caps.append(cap)
        for item in res_params:
            cap = IonObject('AgentCapability', name=item,
                            cap_type=CapabilityType.RES_PAR)
            caps.append(cap)

        return caps
    
    def get_agent_parameters(self):
        """
        """
        params = [x.lstrip('aparam_') for x in vars(self).keys() if x.startswith('aparam_')]
        return params    
    
    def _filter_capabilities(self, events):
        """
        """
        return events
    
    ##############################################################
    # Agent interface.
    ##############################################################    

    def get_agent(self, resource_id='', params=[]):
        """
        """
        result = {}
        for x in params:
            try:
                key = 'aparam_' + x
                val = getattr(self, key)
                result[x] = val
            
            except TypeError:
                raise BadRequest('Bad agent parameter name: %s' % str(x))

            except AttributeError:
                raise BadRequest('Unknown agent parameter: %s' % x)
        
        return result
        
    def set_agent(self, resource_id='', params={}):
        """
        """
        for (x, val) in params.iteritems():
            try:
                key = 'aparam_' + x
                getattr(self, key)
                
                set_key = 'aparam_set_' + x
                try:
                    set_func = getattr(self, set_key)
                    
                except AttributeError:
                    set_func = None
                
                else:
                    if not callable(set_func):
                        set_func = None

                if set_func:
                    set_func(val)
                
                else:
                    setattr(self, key, val)
                    
            except TypeError:
                raise BadRequest('Bad agent parameter name: %s' % str(x))

            except AttributeError:
                raise BadRequest('Unknown agent parameter: %s' % x)
    
    def get_agent_state(self, resource_id=''):
        """
        Return resource agent current common fsm state.
        """
        return self._fsm.get_current_state()

    def execute_agent(self, resource_id="", command=None):
        """
        """
                
        if not command:
            iex = BadRequest('Execute argument "command" not set.')
            self._on_command_error('execute_agent', None, None, None, iex)
            raise iex
        
        # Grab command syntax.
        id = command.command_id
        cmd = command.command
        args = command.args or []
        kwargs = command.kwargs or {}

        if not command.command:
            iex = BadRequest('Command name not set.')
            self._on_command_error('execute_agent', cmd, args, kwargs, iex)
            raise iex

        # Construct a command result object.
        cmd_result = IonObject("AgentCommandResult",
                               command_id=id,
                               command=cmd,
                               ts_execute=get_ion_ts(),
                               status=0)

        try:
            result = self._fsm.on_event(cmd, *args, **kwargs)
            cmd_result.result = result
            self._on_command('execute_agent', cmd, args, kwargs, result)
        
        except FSMStateError as ex:
            iex = Conflict(*(ex.args))    
            self._on_command_error('execute_agent', cmd, args, kwargs, iex)
            raise iex

        except FSMCommandUnknownError as ex:
            iex = BadRequest(*(ex.args))    
            self._on_command_error('execute_agent', cmd, args, kwargs, iex)
            raise iex
                
        except IonException as iex:
            self._on_command_error('execute_agent', cmd, args, kwargs, iex)
            raise

        except Exception as ex:
            iex = ServerError(*(ex.args))
            self._on_command_error('execute_agent', cmd, args, kwargs, iex)
            raise iex
                
        return cmd_result

    def ping_agent(self, resource_id=""):
        """
        """
        result = 'ping from %s, time: %s' % (str(self), get_ion_ts())
        return result

    def aparam_set_example(self, val):
        """
        """
        if isinstance(val, str):
            self.aparam_example = val
        
        else:
            raise BadRequest('Invalid type to set agent parameter "example".')
        
    ##############################################################
    # Resource interface.
    ##############################################################    
    
    def get_resource(self, resource_id='', params=[]):
        """
        """
        
        try:
            result = self._fsm.on_event(ResourceAgentEvent.GET_RESOURCE, params)
            return result
        
        except FSMStateError as ex:
            iex = Conflict(*(ex.args))
            self._on_command_error('get_resource', None, [params], None, iex)
            raise iex
    
        except FSMCommandUnknownError as iex:
            iex = BadRequest(*(ex.args))
            self._on_command_error('get_resource', None, [params], None, iex)
            raise iex

        except IonException as iex:
            self._on_command_error('get_resource', None, [params], None, iex)
            raise

        except Exception as ex:
            iex = ServerError(*(ex.args))
            self._on_command_error('get_resource', None, [params], None, iex)
            raise iex
    
    def set_resource(self, resource_id='', params={}):
        """
        """

        try:
            result = self._fsm.on_event(ResourceAgentEvent.SET_RESOURCE, params)
            self._on_command('set_resource', None, [params], None, result)
            return result
        
        except FSMStateError as ex:
            iex = Conflict(*(ex.args))
            self._on_command_error('set_resource', None, [params], None, iex)
            raise iex
        
        except FSMCommandUnknownError as ex:
            iex = BadRequest(*(ex.args))
            self._on_command_error('set_resource', None, [params], None, iex)
            raise iex

        except IonException as iex:
            self._on_command_error('set_resource', None, [params], None, iex)
            raise iex
    
        except Exception as ex:
            iex = ServerError(*(ex.args))            
            self._on_command_error('set_resource', None, [params], None, iex)
            raise iex

    def get_resource_state(self, resource_id=''):
        """
        """
        try:
            return self._fsm.on_event(ResourceAgentEvent.GET_RESOURCE_STATE)
            
        except FSMStateError as ex:
            iex = Conflict(*(ex.args))    
            self._on_command_error('get_resource_state', None, None, None, iex)
            raise iex

        except FSMCommandUnknownError as ex:
            iex = BadRequest(*(ex.args))
            self._on_command_error('get_resource_state', None, None, None, iex)
            raise iex
        
        except IonException as iex:
            self._on_command_error('get_resource_state', None, None, None, iex)
            raise iex

        except Exception as ex:
            iex = ServerError(*(ex.args))
            self._on_command_error('get_resource_state', None, None, None, iex)
            raise iex
        
    def execute_resource(self, resource_id='', command=None):
        """
        """        

        if not command:
            iex = BadRequest('Execute argument "command" not set.')
            self._on_command_error('execute_resource', None, None, None, iex)
            raise iex

        # Grab command syntax.
        id = command.command_id
        cmd = command.command
        args = command.args or []
        kwargs = command.kwargs or {}

        if not command.command:
            iex = BadRequest('Command name not set.')
            self._on_command_error('execute_resource', cmd, args, kwargs, iex)
            raise iex

        # Construct a command result object.
        cmd_result = IonObject("AgentCommandResult",
                               command_id=id,
                               command=cmd,
                               ts_execute=get_ion_ts(),
                               status=0)

        try:
            result = self._fsm.on_event(
                ResourceAgentEvent.EXECUTE_RESOURCE, cmd, *args, **kwargs)
            cmd_result.result = result
            self._on_command('execute_resource', cmd, args, kwargs, result)
            
        except FSMStateError as ex:
            iex = Conflict(*(ex.args))    
            self._on_command_error('execute_resource', cmd, args, kwargs, iex)
            raise iex

        except FSMCommandUnknownError as ex:
            iex = BadRequest(*(ex.args))
            self._on_command_error('execute_resource', cmd, args, kwargs, iex)
            raise iex
        
        except IonException as iex:
            self._on_command_error('execute_resource', cmd, args, kwargs, iex)
            raise iex

        except Exception as ex:
            iex = ServerError(*(ex.args))
            self._on_command_error('execute_resource', cmd, args, kwargs, iex)
            raise iex
        
        return cmd_result        

    def ping_resource(self, resource_id=''):
        """
        """
        try:
            return self._fsm.on_event(ResourceAgentEvent.PING_RESOURCE)
            
        except FSMStateError as ex:
            iex = Conflict(*(ex.args))    
            self._on_command_error('ping_resource', None, None, None, iex)
            raise iex

        except FSMCommandUnknownError as ex:
            iex = BadRequest(*(ex.args))
            self._on_command_error('ping_resource', None, None, None, iex)
            raise iex
        
        except IonException as iex:
            self._on_command_error('ping_resource', None, None, None, iex)
            raise iex

        except Exception as ex:
            iex = ServerError(*(ex.args))
            self._on_command_error('ping_resource', None, None, None, iex)
            raise iex

    ##############################################################
    # UNINITIALIZED event handlers.
    ##############################################################    

    def _handler_uninitialized_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_uninitialized_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # POWERED_DOWN event handlers.
    ##############################################################    

    def _handler_powered_down_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_powered_down_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # INACTIVE event handlers.
    ##############################################################    

    def _handler_inactive_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_inactive_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # IDLE event handlers.
    ##############################################################    

    def _handler_idle_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_idle_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # STOPPED event handlers.
    ##############################################################    

    def _handler_stopped_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_stopped_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # COMMAND event handlers.
    ##############################################################    

    def _handler_command_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_command_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # STREAMING event handlers.
    ##############################################################    

    def _handler_streaming_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_streaming_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # TEST event handlers.
    ##############################################################    

    def _handler_test_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_test_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # CALIBRATE event handlers.
    ##############################################################    

    def _handler_calibrate_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_calibrate_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # DIRECT_ACCESS event handlers.
    ##############################################################    

    def _handler_direct_access_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_direct_access_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)

    ##############################################################
    # BUSY event handlers.
    ##############################################################

    def _handler_busy_enter(self, *args, **kwargs):
        """
        """
        self._common_state_enter(*args, **kwargs)
    
    def _handler_busy_exit(self, *args, **kwargs):
        """
        """
        self._common_state_exit(*args, **kwargs)
    
    ##############################################################
    # Helpers.
    ##############################################################    
    
    def _common_state_enter(self, *args, **kwargs):
        """
        Common work upon every state entry.
        """
        state = self._fsm.get_current_state()
        log.info('Resource agent %s publsihing state change: %s, time: %s', self.id, state, get_ion_ts())
        
        event_data = {
            'state' : state
        }
        self._event_publisher.publish_event(event_type='ResourceAgentStateEvent',
                              origin=self.resource_id, **event_data)

    def _common_state_exit(self, *args, **kwargs):
        """
        Common work upon every state exit.
        """
        pass
    
    def _on_command(self, cmd, execute_cmd, args, kwargs, result):
        log.info('Resource agent %s publishing command event: \
                 cmd=%s, execute_cmd=%s, args=%s kwargs=%s time=%s',
                 self.id, str(cmd), str(execute_cmd), str(args), str(kwargs),
                 get_ion_ts())
        event_data = {
            'command': cmd,
            'execute_command' : execute_cmd,
            'args' : args,
            'kwargs' : kwargs,
            'result' : result
        }
        self._event_publisher.publish_event(event_type='ResourceAgentCommandEvent',
                                            origin=self.resource_id,
                                            **event_data)
    
    def _on_command_error(self, cmd, execute_cmd, args, kwargs, ex):
        log.info('Resource agent %s publishing command error event: \
                 cmd=%s, execute_cmd=%s, args=%s, kwargs=%s, \
                 errtype=%s, errmsg=%s, errno=%i, time=%s',
                 self.id, str(cmd), str(execute_cmd), str(args), str(kwargs),
                 str(type(ex)), ex.message, ex.status_code, get_ion_ts())
        
        if hasattr(ex, 'status_code'):
            status_code = ex.status_code
        else:
            status_code = -1
            
        event_data = {
            'command': cmd,
            'execute_command' : execute_cmd,
            'args' : args,
            'kwargs' : kwargs,
            'error_type' : str(type(ex)),
            'error_msg' : ex.message,
            'error_code' : status_code
        }
            
        self._event_publisher.publish_event(event_type='ResourceAgentErrorEvent',
                                            origin=self.resource_id,
                                            **event_data)
     
    def _construct_fsm(self):
        """
        Construct the state machine and register default handlers.
        Override in subclass to add handlers for resouce-dependent behaviors
        and state transitions.
        """
                
        # Instrument agent state machine.
        self._fsm = InstrumentFSM(ResourceAgentState, ResourceAgentEvent,
                            ResourceAgentEvent.ENTER, ResourceAgentEvent.EXIT)
        
        self._fsm.add_handler(ResourceAgentState.UNINITIALIZED, ResourceAgentEvent.ENTER, self._handler_uninitialized_enter)
        self._fsm.add_handler(ResourceAgentState.UNINITIALIZED, ResourceAgentEvent.EXIT, self._handler_uninitialized_exit)

        self._fsm.add_handler(ResourceAgentState.POWERED_DOWN, ResourceAgentEvent.ENTER, self._handler_powered_down_enter)
        self._fsm.add_handler(ResourceAgentState.POWERED_DOWN, ResourceAgentEvent.EXIT, self._handler_powered_down_exit)

        self._fsm.add_handler(ResourceAgentState.INACTIVE, ResourceAgentEvent.ENTER, self._handler_inactive_enter)
        self._fsm.add_handler(ResourceAgentState.INACTIVE, ResourceAgentEvent.EXIT, self._handler_inactive_exit)

        self._fsm.add_handler(ResourceAgentState.IDLE, ResourceAgentEvent.ENTER, self._handler_idle_enter)
        self._fsm.add_handler(ResourceAgentState.IDLE, ResourceAgentEvent.EXIT, self._handler_idle_exit)

        self._fsm.add_handler(ResourceAgentState.STOPPED, ResourceAgentEvent.ENTER, self._handler_stopped_enter)
        self._fsm.add_handler(ResourceAgentState.STOPPED, ResourceAgentEvent.EXIT, self._handler_stopped_exit)

        self._fsm.add_handler(ResourceAgentState.COMMAND, ResourceAgentEvent.ENTER, self._handler_command_enter)
        self._fsm.add_handler(ResourceAgentState.COMMAND, ResourceAgentEvent.EXIT, self._handler_command_exit)
        
        self._fsm.add_handler(ResourceAgentState.STREAMING, ResourceAgentEvent.ENTER, self._handler_streaming_enter)
        self._fsm.add_handler(ResourceAgentState.STREAMING, ResourceAgentEvent.EXIT, self._handler_streaming_exit)
        
        self._fsm.add_handler(ResourceAgentState.TEST, ResourceAgentEvent.ENTER, self._handler_test_enter)
        self._fsm.add_handler(ResourceAgentState.TEST, ResourceAgentEvent.EXIT, self._handler_test_exit)
        
        self._fsm.add_handler(ResourceAgentState.CALIBRATE, ResourceAgentEvent.ENTER, self._handler_calibrate_enter)
        self._fsm.add_handler(ResourceAgentState.CALIBRATE, ResourceAgentEvent.EXIT, self._handler_calibrate_exit)
        
        self._fsm.add_handler(ResourceAgentState.DIRECT_ACCESS, ResourceAgentEvent.ENTER, self._handler_direct_access_enter)
        self._fsm.add_handler(ResourceAgentState.DIRECT_ACCESS, ResourceAgentEvent.EXIT, self._handler_direct_access_exit)
        
        self._fsm.add_handler(ResourceAgentState.BUSY, ResourceAgentEvent.ENTER, self._handler_busy_enter)
        self._fsm.add_handler(ResourceAgentState.BUSY, ResourceAgentEvent.EXIT, self._handler_busy_exit)
コード例 #11
0
    def __init__(self, prompts, newline, driver_event):
        """
        SBE37Protocol constructor.
        @param prompts A BaseEnum class containing instrument prompts.
        @param newline The SBE37 newline.
        @param driver_event Driver process event callback.
        """
        # Construct protocol superclass.
        CommandResponseInstrumentProtocol.__init__(self, prompts, newline,
                                                   driver_event)

        # Build SBE37 protocol state machine.
        self._protocol_fsm = InstrumentFSM(SBE37ProtocolState,
                                           SBE37ProtocolEvent,
                                           SBE37ProtocolEvent.ENTER,
                                           SBE37ProtocolEvent.EXIT)

        # Add event handlers for protocol state machine.
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_unknown_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_unknown_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN,
                                       SBE37ProtocolEvent.DISCOVER,
                                       self._handler_unknown_discover)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_command_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_command_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.ACQUIRE_SAMPLE,
                                       self._handler_command_acquire_sample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.START_AUTOSAMPLE,
                                       self._handler_command_start_autosample)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.GET,
            self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.SET,
                                       self._handler_command_set)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.TEST,
                                       self._handler_command_test)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.START_DIRECT,
                                       self._handler_command_start_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_autosample_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_autosample_exit)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.GET,
            self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.STOP_AUTOSAMPLE,
            self._handler_autosample_stop_autosample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_test_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_test_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST,
                                       SBE37ProtocolEvent.RUN_TEST,
                                       self._handler_test_run_tests)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.TEST, SBE37ProtocolEvent.GET,
            self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_direct_access_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_direct_access_exit)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.DIRECT_ACCESS,
            SBE37ProtocolEvent.EXECUTE_DIRECT,
            self._handler_direct_access_execute_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS,
                                       SBE37ProtocolEvent.STOP_DIRECT,
                                       self._handler_direct_access_stop_direct)

        # Construct the parameter dictionary containing device parameters,
        # current parameter values, and set formatting functions.
        self._build_param_dict()

        # Add build handlers for device commands.
        self._add_build_handler('ds', self._build_simple_command)
        self._add_build_handler('dc', self._build_simple_command)
        self._add_build_handler('ts', self._build_simple_command)
        self._add_build_handler('startnow', self._build_simple_command)
        self._add_build_handler('stop', self._build_simple_command)
        self._add_build_handler('tc', self._build_simple_command)
        self._add_build_handler('tt', self._build_simple_command)
        self._add_build_handler('tp', self._build_simple_command)
        self._add_build_handler('set', self._build_set_command)

        # Add response handlers for device commands.
        self._add_response_handler('ds', self._parse_dsdc_response)
        self._add_response_handler('dc', self._parse_dsdc_response)
        self._add_response_handler('ts', self._parse_ts_response)
        self._add_response_handler('set', self._parse_set_response)
        self._add_response_handler('tc', self._parse_test_response)
        self._add_response_handler('tt', self._parse_test_response)
        self._add_response_handler('tp', self._parse_test_response)

        # Add sample handlers.
        self._sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        self._sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        self._sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        self._sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'
        self._sample_regex = re.compile(self._sample_pattern)

        # State state machine in UNKNOWN state.
        self._protocol_fsm.start(SBE37ProtocolState.UNKNOWN)

        # commands sent sent to device to be filtered in responses for telnet DA
        self._sent_cmds = []
コード例 #12
0
class SBE37Protocol(CommandResponseInstrumentProtocol):
    """
    Instrument protocol class for SBE37 driver.
    Subclasses CommandResponseInstrumentProtocol
    """
    def __init__(self, prompts, newline, driver_event):
        """
        SBE37Protocol constructor.
        @param prompts A BaseEnum class containing instrument prompts.
        @param newline The SBE37 newline.
        @param driver_event Driver process event callback.
        """
        # Construct protocol superclass.
        CommandResponseInstrumentProtocol.__init__(self, prompts, newline,
                                                   driver_event)

        # Build SBE37 protocol state machine.
        self._protocol_fsm = InstrumentFSM(SBE37ProtocolState,
                                           SBE37ProtocolEvent,
                                           SBE37ProtocolEvent.ENTER,
                                           SBE37ProtocolEvent.EXIT)

        # Add event handlers for protocol state machine.
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_unknown_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_unknown_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.UNKNOWN,
                                       SBE37ProtocolEvent.DISCOVER,
                                       self._handler_unknown_discover)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_command_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_command_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.ACQUIRE_SAMPLE,
                                       self._handler_command_acquire_sample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.START_AUTOSAMPLE,
                                       self._handler_command_start_autosample)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.COMMAND, SBE37ProtocolEvent.GET,
            self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.SET,
                                       self._handler_command_set)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.TEST,
                                       self._handler_command_test)
        self._protocol_fsm.add_handler(SBE37ProtocolState.COMMAND,
                                       SBE37ProtocolEvent.START_DIRECT,
                                       self._handler_command_start_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_autosample_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.AUTOSAMPLE,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_autosample_exit)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.GET,
            self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.AUTOSAMPLE, SBE37ProtocolEvent.STOP_AUTOSAMPLE,
            self._handler_autosample_stop_autosample)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_test_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_test_exit)
        self._protocol_fsm.add_handler(SBE37ProtocolState.TEST,
                                       SBE37ProtocolEvent.RUN_TEST,
                                       self._handler_test_run_tests)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.TEST, SBE37ProtocolEvent.GET,
            self._handler_command_autosample_test_get)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS,
                                       SBE37ProtocolEvent.ENTER,
                                       self._handler_direct_access_enter)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS,
                                       SBE37ProtocolEvent.EXIT,
                                       self._handler_direct_access_exit)
        self._protocol_fsm.add_handler(
            SBE37ProtocolState.DIRECT_ACCESS,
            SBE37ProtocolEvent.EXECUTE_DIRECT,
            self._handler_direct_access_execute_direct)
        self._protocol_fsm.add_handler(SBE37ProtocolState.DIRECT_ACCESS,
                                       SBE37ProtocolEvent.STOP_DIRECT,
                                       self._handler_direct_access_stop_direct)

        # Construct the parameter dictionary containing device parameters,
        # current parameter values, and set formatting functions.
        self._build_param_dict()

        # Add build handlers for device commands.
        self._add_build_handler('ds', self._build_simple_command)
        self._add_build_handler('dc', self._build_simple_command)
        self._add_build_handler('ts', self._build_simple_command)
        self._add_build_handler('startnow', self._build_simple_command)
        self._add_build_handler('stop', self._build_simple_command)
        self._add_build_handler('tc', self._build_simple_command)
        self._add_build_handler('tt', self._build_simple_command)
        self._add_build_handler('tp', self._build_simple_command)
        self._add_build_handler('set', self._build_set_command)

        # Add response handlers for device commands.
        self._add_response_handler('ds', self._parse_dsdc_response)
        self._add_response_handler('dc', self._parse_dsdc_response)
        self._add_response_handler('ts', self._parse_ts_response)
        self._add_response_handler('set', self._parse_set_response)
        self._add_response_handler('tc', self._parse_test_response)
        self._add_response_handler('tt', self._parse_test_response)
        self._add_response_handler('tp', self._parse_test_response)

        # Add sample handlers.
        self._sample_pattern = r'^#? *(-?\d+\.\d+), *(-?\d+\.\d+), *(-?\d+\.\d+)'
        self._sample_pattern += r'(, *(-?\d+\.\d+))?(, *(-?\d+\.\d+))?'
        self._sample_pattern += r'(, *(\d+) +([a-zA-Z]+) +(\d+), *(\d+):(\d+):(\d+))?'
        self._sample_pattern += r'(, *(\d+)-(\d+)-(\d+), *(\d+):(\d+):(\d+))?'
        self._sample_regex = re.compile(self._sample_pattern)

        # State state machine in UNKNOWN state.
        self._protocol_fsm.start(SBE37ProtocolState.UNKNOWN)

        # commands sent sent to device to be filtered in responses for telnet DA
        self._sent_cmds = []

    ########################################################################
    # Unknown handlers.
    ########################################################################

    def _handler_unknown_enter(self, *args, **kwargs):
        """
        Enter unknown state.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_unknown_exit(self, *args, **kwargs):
        """
        Exit unknown state.
        """
        pass

    def _handler_unknown_discover(self, *args, **kwargs):
        """
        Discover current state; can be COMMAND or AUTOSAMPLE.
        @retval (next_state, result), (SBE37ProtocolState.COMMAND or
        SBE37State.AUTOSAMPLE, None) if successful.
        @throws InstrumentTimeoutException if the device cannot be woken.
        @throws InstrumentStateException if the device response does not correspond to
        an expected state.
        """
        next_state = None
        result = None

        # Wakeup the device with timeout if passed.
        timeout = kwargs.get('timeout', SBE37_TIMEOUT)
        prompt = self._wakeup(timeout)
        prompt = self._wakeup(timeout)

        # Set the state to change.
        # Raise if the prompt returned does not match command or autosample.
        if prompt == SBE37Prompt.COMMAND:
            next_state = SBE37ProtocolState.COMMAND
            result = SBE37ProtocolState.COMMAND
        elif prompt == SBE37Prompt.AUTOSAMPLE:
            next_state = SBE37ProtocolState.AUTOSAMPLE
            result = SBE37ProtocolState.AUTOSAMPLE
        else:
            raise InstrumentStateException('Unknown state.')

        return (next_state, result)

    ########################################################################
    # Command handlers.
    ########################################################################

    def _handler_command_enter(self, *args, **kwargs):
        """
        Enter command state.
        @throws InstrumentTimeoutException if the device cannot be woken.
        @throws InstrumentProtocolException if the update commands and not recognized.
        """
        # Command device to update parameters and send a config change event.
        self._update_params()

        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_command_exit(self, *args, **kwargs):
        """
        Exit command state.
        """
        pass

    def _handler_command_set(self, *args, **kwargs):
        """
        Perform a set command.
        @param args[0] parameter : value dict.
        @retval (next_state, result) tuple, (None, None).
        @throws InstrumentParameterException if missing set parameters, if set parameters not ALL and
        not a dict, or if paramter can't be properly formatted.
        @throws InstrumentTimeoutException if device cannot be woken for set command.
        @throws InstrumentProtocolException if set command could not be built or misunderstood.
        """
        next_state = None
        result = None

        # Retrieve required parameter.
        # Raise if no parameter provided, or not a dict.
        try:
            params = args[0]

        except IndexError:
            raise InstrumentParameterException(
                'Set command requires a parameter dict.')

        if not isinstance(params, dict):
            raise InstrumentParameterException('Set parameters not a dict.')

        # For each key, val in the dict, issue set command to device.
        # Raise if the command not understood.
        else:

            for (key, val) in params.iteritems():
                result = self._do_cmd_resp('set', key, val, **kwargs)
            self._update_params()

        return (next_state, result)

    def _handler_command_acquire_sample(self, *args, **kwargs):
        """
        Acquire sample from SBE37.
        @retval (next_state, result) tuple, (None, sample dict).        
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command could not be built or misunderstood.
        @throws SampleException if a sample could not be extracted from result.
        """
        next_state = None
        result = None

        result = self._do_cmd_resp('ts', *args, **kwargs)

        return (next_state, result)

    def _handler_command_start_autosample(self, *args, **kwargs):
        """
        Switch into autosample mode.
        @retval (next_state, result) tuple, (SBE37ProtocolState.AUTOSAMPLE,
        None) if successful.
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command could not be built or misunderstood.
        """
        next_state = None
        result = None

        # Assure the device is transmitting.
        if not self._param_dict.get(SBE37Parameter.TXREALTIME):
            self._do_cmd_resp('set', SBE37Parameter.TXREALTIME, True, **kwargs)

        # Issue start command and switch to autosample if successful.
        self._do_cmd_no_resp('startnow', *args, **kwargs)

        next_state = SBE37ProtocolState.AUTOSAMPLE

        return (next_state, result)

    def _handler_command_test(self, *args, **kwargs):
        """
        Switch to test state to perform instrument tests.
        @retval (next_state, result) tuple, (SBE37ProtocolState.TEST, None).
        """
        next_state = None
        result = None

        next_state = SBE37ProtocolState.TEST

        return (next_state, result)

    def _handler_command_start_direct(self):
        """
        """
        next_state = None
        result = None

        next_state = SBE37ProtocolState.DIRECT_ACCESS

        return (next_state, result)

    ########################################################################
    # Autosample handlers.
    ########################################################################

    def _handler_autosample_enter(self, *args, **kwargs):
        """
        Enter autosample state.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

    def _handler_autosample_exit(self, *args, **kwargs):
        """
        Exit autosample state.
        """
        pass

    def _handler_autosample_stop_autosample(self, *args, **kwargs):
        """
        Stop autosample and switch back to command mode.
        @retval (next_state, result) tuple, (SBE37ProtocolState.COMMAND,
        None) if successful.
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command misunderstood or
        incorrect prompt received.
        """
        next_state = None
        result = None

        # Wake up the device, continuing until autosample prompt seen.
        timeout = kwargs.get('timeout', SBE37_TIMEOUT)
        self._wakeup_until(timeout, SBE37Prompt.AUTOSAMPLE)

        # Issue the stop command.
        self._do_cmd_resp('stop', *args, **kwargs)

        # Prompt device until command prompt is seen.
        self._wakeup_until(timeout, SBE37Prompt.COMMAND)

        next_state = SBE37ProtocolState.COMMAND

        return (next_state, result)

    ########################################################################
    # Common handlers.
    ########################################################################

    def _handler_command_autosample_test_get(self, *args, **kwargs):
        """
        Get device parameters from the parameter dict.
        @param args[0] list of parameters to retrieve, or DriverParameter.ALL.
        @throws InstrumentParameterException if missing or invalid parameter.
        """
        next_state = None
        result = None

        # Retrieve the required parameter, raise if not present.
        try:
            params = args[0]

        except IndexError:
            raise InstrumentParameterException(
                'Get command requires a parameter list or tuple.')

        # If all params requested, retrieve config.
        if params == DriverParameter.ALL:
            result = self._param_dict.get_config()

        # If not all params, confirm a list or tuple of params to retrieve.
        # Raise if not a list or tuple.
        # Retireve each key in the list, raise if any are invalid.
        else:
            if not isinstance(params, (list, tuple)):
                raise InstrumentParameterException(
                    'Get argument not a list or tuple.')
            result = {}
            for key in params:
                try:
                    val = self._param_dict.get(key)
                    result[key] = val

                except KeyError:
                    raise InstrumentParameterException(
                        ('%s is not a valid parameter.' % key))

        return (next_state, result)

    ########################################################################
    # Test handlers.
    ########################################################################

    def _handler_test_enter(self, *args, **kwargs):
        """
        Enter test state. Setup the secondary call to run the tests.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

        # Forward the test event again to run the test handler and
        # switch back to command mode afterward.
        Timer(1, lambda: self._protocol_fsm.on_event(SBE37ProtocolEvent.
                                                     RUN_TEST)).start()

    def _handler_test_exit(self, *args, **kwargs):
        """
        Exit test state.
        """
        pass

    def _handler_test_run_tests(self, *args, **kwargs):
        """
        Run test routines and validate results.
        @throws InstrumentTimeoutException if device cannot be woken for command.
        @throws InstrumentProtocolException if command misunderstood or
        incorrect prompt received.
        """
        next_state = None
        result = None

        tc_pass = False
        tt_pass = False
        tp_pass = False
        tc_result = None
        tt_result = None
        tp_result = None

        test_result = {}

        try:
            tc_pass, tc_result = self._do_cmd_resp('tc', timeout=200)
            tt_pass, tt_result = self._do_cmd_resp('tt', timeout=200)
            tp_pass, tp_result = self._do_cmd_resp('tp', timeout=200)

        except Exception as e:
            test_result['exception'] = e
            test_result['message'] = 'Error running instrument tests.'

        finally:
            test_result['cond_test'] = 'Passed' if tc_pass else 'Failed'
            test_result['cond_data'] = tc_result
            test_result['temp_test'] = 'Passed' if tt_pass else 'Failed'
            test_result['temp_data'] = tt_result
            test_result['pres_test'] = 'Passed' if tp_pass else 'Failed'
            test_result['pres_data'] = tp_result
            test_result['success'] = 'Passed' if (tc_pass and tt_pass
                                                  and tp_pass) else 'Failed'

        self._driver_event(DriverAsyncEvent.TEST_RESULT, test_result)
        next_state = SBE37ProtocolState.COMMAND

        return (next_state, result)

    ########################################################################
    # Direct access handlers.
    ########################################################################

    def _handler_direct_access_enter(self, *args, **kwargs):
        """
        Enter direct access state.
        """
        # Tell driver superclass to send a state change event.
        # Superclass will query the state.
        self._driver_event(DriverAsyncEvent.STATE_CHANGE)

        self._sent_cmds = []

    def _handler_direct_access_exit(self, *args, **kwargs):
        """
        Exit direct access state.
        """
        pass

    def _handler_direct_access_execute_direct(self, data):
        """
        """
        next_state = None
        result = None

        self._do_cmd_direct(data)

        # add sent command to list for 'echo' filtering in callback
        self._sent_cmds.append(data)

        return (next_state, result)

    def _handler_direct_access_stop_direct(self):
        """
        @throw InstrumentProtocolException on invalid command
        """
        next_state = None
        result = None

        next_state = SBE37ProtocolState.COMMAND

        return (next_state, result)

    ########################################################################
    # Private helpers.
    ########################################################################

    def _send_wakeup(self):
        """
        Send a newline to attempt to wake the SBE37 device.
        """
        self._connection.send(SBE37_NEWLINE)

    def _update_params(self, *args, **kwargs):
        """
        Update the parameter dictionary. Wake the device then issue
        display status and display calibration commands. The parameter
        dict will match line output and udpate itself.
        @throws InstrumentTimeoutException if device cannot be timely woken.
        @throws InstrumentProtocolException if ds/dc misunderstood.
        """

        # Get old param dict config.
        old_config = self._param_dict.get_config()

        # Issue display commands and parse results.
        timeout = kwargs.get('timeout', SBE37_TIMEOUT)
        self._do_cmd_resp('ds', timeout=timeout)
        self._do_cmd_resp('dc', timeout=timeout)

        # Get new param dict config. If it differs from the old config,
        # tell driver superclass to publish a config change event.
        new_config = self._param_dict.get_config()
        if new_config != old_config:
            self._driver_event(DriverAsyncEvent.CONFIG_CHANGE)

    def _build_simple_command(self, cmd):
        """
        Build handler for basic SBE37 commands.
        @param cmd the simple sbe37 command to format.
        @retval The command to be sent to the device.
        """
        return cmd + SBE37_NEWLINE

    def _build_set_command(self, cmd, param, val):
        """
        Build handler for set commands. param=val followed by newline.
        String val constructed by param dict formatting function.
        @param param the parameter key to set.
        @param val the parameter value to set.
        @ retval The set command to be sent to the device.
        @throws InstrumentProtocolException if the parameter is not valid or
        if the formatting function could not accept the value passed.
        """
        try:
            str_val = self._param_dict.format(param, val)
            set_cmd = '%s=%s' % (param, str_val)
            set_cmd = set_cmd + SBE37_NEWLINE

        except KeyError:
            raise InstrumentParameterException('Unknown driver parameter %s' %
                                               param)

        return set_cmd

    def _parse_set_response(self, response, prompt):
        """
        Parse handler for set command.
        @param response command response string.
        @param prompt prompt following command response.        
        @throws InstrumentProtocolException if set command misunderstood.
        """
        if prompt != SBE37Prompt.COMMAND:
            raise InstrumentProtocolException(
                'Set command not recognized: %s' % response)

    def _parse_dsdc_response(self, response, prompt):
        """
        Parse handler for dsdc commands.
        @param response command response string.
        @param prompt prompt following command response.        
        @throws InstrumentProtocolException if dsdc command misunderstood.
        """
        if prompt != SBE37Prompt.COMMAND:
            raise InstrumentProtocolException(
                'dsdc command not recognized: %s.' % response)

        for line in response.split(SBE37_NEWLINE):
            self._param_dict.update(line)

    def _parse_ts_response(self, response, prompt):
        """
        Response handler for ts command.
        @param response command response string.
        @param prompt prompt following command response.
        @retval sample dictionary containig c, t, d values.
        @throws InstrumentProtocolException if ts command misunderstood.
        @throws InstrumentSampleException if response did not contain a sample
        """

        if prompt != SBE37Prompt.COMMAND:
            raise InstrumentProtocolException('ts command not recognized: %s',
                                              response)

        sample = None
        for line in response.split(SBE37_NEWLINE):
            sample = self._extract_sample(line, True)
            if sample:
                break

        if not sample:
            raise SampleException('Response did not contain sample: %s' %
                                  repr(response))

        return sample

    def _parse_test_response(self, response, prompt):
        """
        Do minimal checking of test outputs.
        @param response command response string.
        @param promnpt prompt following command response.
        @retval tuple of pass/fail boolean followed by response
        """

        success = False
        lines = response.split()
        if len(lines) > 2:
            data = lines[1:-1]
            bad_count = 0
            for item in data:
                try:
                    float(item)

                except ValueError:
                    bad_count += 1

            if bad_count == 0:
                success = True

        return (success, response)

    def got_data(self, data):
        """
        Callback for receiving new data from the device.
        """
        if self.get_current_state() == SBE37ProtocolState.DIRECT_ACCESS:
            # direct access mode
            if len(data) > 0:
                mi_logger.debug("SBE37Protocol._got_data(): <" + data + ">")
                if self._driver_event:
                    self._driver_event(DriverAsyncEvent.DIRECT_ACCESS, data)
                    # TODO: what about logging this as an event?
            return

        if len(data) > 0:
            # Call the superclass to update line and prompt buffers.
            CommandResponseInstrumentProtocol.got_data(self, data)

            # If in streaming mode, process the buffer for samples to publish.
            cur_state = self.get_current_state()
            if cur_state == SBE37ProtocolState.AUTOSAMPLE:
                if SBE37_NEWLINE in self._linebuf:
                    lines = self._linebuf.split(SBE37_NEWLINE)
                    self._linebuf = lines[-1]
                    for line in lines:
                        self._extract_sample(line)

    def _extract_sample(self, line, publish=True):
        """
        Extract sample from a response line if present and publish to agent.
        @param line string to match for sample.
        @param publsih boolean to publish sample (default True).
        @retval Sample dictionary if present or None.
        """
        sample = None
        match = self._sample_regex.match(line)
        if match:
            sample = {}
            sample['t'] = [float(match.group(1))]
            sample['c'] = [float(match.group(2))]
            sample['p'] = [float(match.group(3))]

            # Driver timestamp.
            sample['time'] = [time.time()]
            sample['stream_name'] = 'ctd_parsed'

            if self._driver_event:
                self._driver_event(DriverAsyncEvent.SAMPLE, sample)

        return sample

    def _build_param_dict(self):
        """
        Populate the parameter dictionary with SBE37 parameters.
        For each parameter key, add match stirng, match lambda function,
        and value formatting function for set commands.
        """
        # Add parameter handlers to parameter dict.
        self._param_dict.add(SBE37Parameter.OUTPUTSAL,
                             r'(do not )?output salinity with each sample',
                             lambda match: False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(
            SBE37Parameter.OUTPUTSV,
            r'(do not )?output sound velocity with each sample',
            lambda match: False
            if match.group(1) else True, self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.NAVG,
                             r'number of samples to average = (\d+)',
                             lambda match: int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.SAMPLENUM,
                             r'samplenumber = (\d+), free = \d+',
                             lambda match: int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.INTERVAL,
                             r'sample interval = (\d+) seconds',
                             lambda match: int(match.group(1)),
                             self._int_to_string)
        self._param_dict.add(SBE37Parameter.STORETIME,
                             r'(do not )?store time with each sample',
                             lambda match: False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(SBE37Parameter.TXREALTIME,
                             r'(do not )?transmit real-time data',
                             lambda match: False if match.group(1) else True,
                             self._true_false_to_string)
        self._param_dict.add(
            SBE37Parameter.SYNCMODE, r'serial sync mode (enabled|disabled)',
            lambda match: False if (match.group(1) == 'disabled') else True,
            self._true_false_to_string)
        self._param_dict.add(
            SBE37Parameter.SYNCWAIT,
            r'wait time after serial sync sampling = (\d+) seconds',
            lambda match: int(match.group(1)), self._int_to_string)
        self._param_dict.add(
            SBE37Parameter.TCALDATE,
            r'temperature: +((\d+)-([a-zA-Z]+)-(\d+))',
            lambda match: self._string_to_date(match.group(1), '%d-%b-%y'),
            self._date_to_string)
        self._param_dict.add(SBE37Parameter.TA0,
                             r' +TA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.TA1,
                             r' +TA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.TA2,
                             r' +TA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.TA3,
                             r' +TA3 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(
            SBE37Parameter.CCALDATE,
            r'conductivity: +((\d+)-([a-zA-Z]+)-(\d+))',
            lambda match: self._string_to_date(match.group(1), '%d-%b-%y'),
            self._date_to_string)
        self._param_dict.add(SBE37Parameter.CG,
                             r' +G = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CH,
                             r' +H = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CI,
                             r' +I = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CJ,
                             r' +J = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.WBOTC,
                             r' +WBOTC = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CTCOR,
                             r' +CTCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.CPCOR,
                             r' +CPCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(
            SBE37Parameter.PCALDATE, r'pressure .+ ((\d+)-([a-zA-Z]+)-(\d+))',
            lambda match: self._string_to_date(match.group(1), '%d-%b-%y'),
            self._date_to_string)
        self._param_dict.add(SBE37Parameter.PA0,
                             r' +PA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PA1,
                             r' +PA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PA2,
                             r' +PA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCA0,
                             r' +PTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCA1,
                             r' +PTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCA2,
                             r' +PTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCB0,
                             r' +PTCSB0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCB1,
                             r' +PTCSB1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.PTCB2,
                             r' +PTCSB2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.POFFSET,
                             r' +POFFSET = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(
            SBE37Parameter.RCALDATE, r'rtc: +((\d+)-([a-zA-Z]+)-(\d+))',
            lambda match: self._string_to_date(match.group(1), '%d-%b-%y'),
            self._date_to_string)
        self._param_dict.add(SBE37Parameter.RTCA0,
                             r' +RTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.RTCA1,
                             r' +RTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)
        self._param_dict.add(SBE37Parameter.RTCA2,
                             r' +RTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match: float(match.group(1)),
                             self._float_to_string)

    ########################################################################
    # Static helpers to format set commands.
    ########################################################################

    @staticmethod
    def _true_false_to_string(v):
        """
        Write a boolean value to string formatted for sbe37 set operations.
        @param v a boolean value.
        @retval A yes/no string formatted for sbe37 set operations.
        @throws InstrumentParameterException if value not a bool.
        """

        if not isinstance(v, bool):
            raise InstrumentParameterException('Value %s is not a bool.' %
                                               str(v))
        if v:
            return 'y'
        else:
            return 'n'

    @staticmethod
    def _int_to_string(v):
        """
        Write an int value to string formatted for sbe37 set operations.
        @param v An int val.
        @retval an int string formatted for sbe37 set operations.
        @throws InstrumentParameterException if value not an int.
        """

        if not isinstance(v, int):
            raise InstrumentParameterException('Value %s is not an int.' %
                                               str(v))
        else:
            return '%i' % v

    @staticmethod
    def _float_to_string(v):
        """
        Write a float value to string formatted for sbe37 set operations.
        @param v A float val.
        @retval a float string formatted for sbe37 set operations.
        @throws InstrumentParameterException if value is not a float.
        """

        if not isinstance(v, float):
            raise InstrumentParameterException('Value %s is not a float.' % v)
        else:
            return '%e' % v

    @staticmethod
    def _date_to_string(v):
        """
        Write a date tuple to string formatted for sbe37 set operations.
        @param v a date tuple: (day,month,year).
        @retval A date string formatted for sbe37 set operations.
        @throws InstrumentParameterException if date tuple is not valid.
        """

        if not isinstance(v, (list, tuple)):
            raise InstrumentParameterException(
                'Value %s is not a list, tuple.' % str(v))

        if not len(v) == 3:
            raise InstrumentParameterException('Value %s is not length 3.' %
                                               str(v))

        months = [
            'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
            'Oct', 'Nov', 'Dec'
        ]
        day = v[0]
        month = v[1]
        year = v[2]

        if len(str(year)) > 2:
            year = int(str(year)[-2:])

        if not isinstance(day, int) or day < 1 or day > 31:
            raise InstrumentParameterException(
                'Value %s is not a day of month.' % str(day))

        if not isinstance(month, int) or month < 1 or month > 12:
            raise InstrumentParameterException('Value %s is not a month.' %
                                               str(month))

        if not isinstance(year, int) or year < 0 or year > 99:
            raise InstrumentParameterException('Value %s is not a 0-99 year.' %
                                               str(year))

        return '%02i-%s-%02i' % (day, months[month - 1], year)

    @staticmethod
    def _string_to_date(datestr, fmt):
        """
        Extract a date tuple from an sbe37 date string.
        @param str a string containing date information in sbe37 format.
        @retval a date tuple.
        @throws InstrumentParameterException if datestr cannot be formatted to
        a date.
        """
        if not isinstance(datestr, str):
            raise InstrumentParameterException('Value %s is not a string.' %
                                               str(datestr))
        try:
            date_time = time.strptime(datestr, fmt)
            date = (date_time[2], date_time[1], date_time[0])

        except ValueError:
            raise InstrumentParameterException(
                'Value %s could not be formatted to a date.' % str(datestr))

        return date