Ejemplo n.º 1
0
    def __init__(self, callback=None):
        CommandResponseInstrumentProtocol.__init__(self, callback, Prompt, "\n")

        self._fsm = InstrumentFSM(State, Event, Event.ENTER_STATE, Event.EXIT_STATE, InstErrorCode.UNHANDLED_EVENT)
        self._fsm.add_handler(State.COMMAND_MODE, Event.COMMAND, self._handler_command_command)
        self._fsm.add_handler(State.COMMAND_MODE, Event.GET, self._handler_command_get)
        self._fsm.add_handler(State.COMMAND_MODE, Event.SET, self._handler_command_set)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.BREAK, self._handler_autosample_break)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.STOP, self._handler_autosample_stop)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.RESET, self._handler_reset)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.COMMAND, self._handler_autosample_command)
        self._fsm.add_handler(State.POLL_MODE, Event.AUTOSAMPLE, self._handler_poll_autosample)
        self._fsm.add_handler(State.POLL_MODE, Event.RESET, self._handler_reset)
        self._fsm.add_handler(State.POLL_MODE, Event.SAMPLE, self._handler_poll_sample)
        self._fsm.add_handler(State.POLL_MODE, Event.COMMAND, self._handler_poll_command)
        self._fsm.add_handler(State.UNKNOWN, Event.INITIALIZE, self._handler_initialize)
        self._fsm.start(State.UNKNOWN)

        self._add_build_handler(Command.SET, self._build_set_command)
        self._add_build_handler(Command.GET, self._build_param_fetch_command)
        self._add_build_handler(Command.SAVE, self._build_exec_command)
        self._add_build_handler(Command.EXIT, self._build_exec_command)
        self._add_build_handler(Command.EXIT_AND_RESET, self._build_exec_command)
        self._add_build_handler(Command.AUTOSAMPLE, self._build_control_command)
        self._add_build_handler(Command.RESET, self._build_control_command)
        self._add_build_handler(Command.BREAK, self._build_control_command)
        self._add_build_handler(Command.SAMPLE, self._build_control_command)
        self._add_build_handler(Command.STOP, self._build_control_command)

        self._add_response_handler(Command.SET, self._parse_set_response)
        # self._add_response_handler(Command.GET, self._parse_get_response)

        self._add_param_dict(
            Parameter.TELBAUD,
            r"Telemetry Baud Rate:\s+(\d+) bps",
            lambda match: int(match.group(1)),
            self._int_to_string,
        )

        self._add_param_dict(
            Parameter.MAXRATE, r"Maximum Frame Rate:\s+(\d+) Hz", lambda match: int(match.group(1)), self._int_to_string
        )
Ejemplo n.º 2
0
    def __init__(self, callback=None):
        """
        Creates an instance of this protocol. This basically sets up the
        state machine, which is initialized in the INIT state.
        """

        CommandResponseInstrumentProtocol.__init__(self, callback, BarsPrompt,
                                                   NEWLINE)

        self._outfile = sys.stdout
        self._outfile = file("protoc_output.txt", "w")

        self._fsm = InstrumentFSM(BarsProtocolState, BarsProtocolEvent,
                                  None,
                                  None,
                                  InstErrorCode.UNHANDLED_EVENT)

        # PRE_INIT
        self._fsm.add_handler(BarsProtocolState.PRE_INIT,
                              BarsProtocolEvent.INITIALIZE,
                              self._handler_pre_init_initialize)

        # COLLECTING_DATA
        self._fsm.add_handler(BarsProtocolState.COLLECTING_DATA,
                              BarsProtocolEvent.ENTER_MAIN_MENU,
                              self._handler_collecting_data_enter_main_menu)

        # MAIN_MENU
        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.RESTART_DATA_COLLECTION,
                              self._handler_main_menu_restart_data_collection)

        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.ENTER_CHANGE_PARAMS,
                              self._handler_main_menu_enter_change_params)

        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.SHOW_SYSTEM_DIAGNOSTICS,
                              self._handler_main_menu_show_system_diagnostics)

        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.ENTER_SET_SYSTEM_CLOCK,
                              self._handler_main_menu_enter_set_system_clock)

        # CHANGE_PARAMS_MENU
        self._fsm.add_handler(BarsProtocolState.CHANGE_PARAMS_MENU,
                              BarsProtocolEvent.EXIT_CHANGE_PARAMS,
                              self._handler_change_params_menu_exit)

        # we start in the PRE_INIT state
        self._fsm.start(BarsProtocolState.PRE_INIT)

        # add build command handlers
        self._add_build_handler(CONTROL_S, self._build_simple_cmd)
        self._add_build_handler(CONTROL_M, self._build_simple_cmd)
        for c in range(8):
            char = '%d' % c
            self._add_build_handler(char, self._build_simple_cmd)

        # add response handlers
        self._add_response_handler(CONTROL_S, self._control_s_response_handler)
Ejemplo n.º 3
0
class BarsInstrumentProtocol(CommandResponseInstrumentProtocol):
    """The instrument protocol classes to deal with a BARS sensor.

    """

    def __init__(self, callback=None):
        """
        Creates an instance of this protocol. This basically sets up the
        state machine, which is initialized in the INIT state.
        """

        CommandResponseInstrumentProtocol.__init__(self, callback, BarsPrompt,
                                                   NEWLINE)

        self._outfile = sys.stdout
        self._outfile = file("protoc_output.txt", "w")

        self._fsm = InstrumentFSM(BarsProtocolState, BarsProtocolEvent,
                                  None,
                                  None,
                                  InstErrorCode.UNHANDLED_EVENT)

        # PRE_INIT
        self._fsm.add_handler(BarsProtocolState.PRE_INIT,
                              BarsProtocolEvent.INITIALIZE,
                              self._handler_pre_init_initialize)

        # COLLECTING_DATA
        self._fsm.add_handler(BarsProtocolState.COLLECTING_DATA,
                              BarsProtocolEvent.ENTER_MAIN_MENU,
                              self._handler_collecting_data_enter_main_menu)

        # MAIN_MENU
        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.RESTART_DATA_COLLECTION,
                              self._handler_main_menu_restart_data_collection)

        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.ENTER_CHANGE_PARAMS,
                              self._handler_main_menu_enter_change_params)

        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.SHOW_SYSTEM_DIAGNOSTICS,
                              self._handler_main_menu_show_system_diagnostics)

        self._fsm.add_handler(BarsProtocolState.MAIN_MENU,
                              BarsProtocolEvent.ENTER_SET_SYSTEM_CLOCK,
                              self._handler_main_menu_enter_set_system_clock)

        # CHANGE_PARAMS_MENU
        self._fsm.add_handler(BarsProtocolState.CHANGE_PARAMS_MENU,
                              BarsProtocolEvent.EXIT_CHANGE_PARAMS,
                              self._handler_change_params_menu_exit)

        # we start in the PRE_INIT state
        self._fsm.start(BarsProtocolState.PRE_INIT)

        # add build command handlers
        self._add_build_handler(CONTROL_S, self._build_simple_cmd)
        self._add_build_handler(CONTROL_M, self._build_simple_cmd)
        for c in range(8):
            char = '%d' % c
            self._add_build_handler(char, self._build_simple_cmd)

        # add response handlers
        self._add_response_handler(CONTROL_S, self._control_s_response_handler)

    def _assert_state(self, state):
        cs = self.get_current_state()
        res = cs == state
        if not res:
            raise AssertionError("current state=%s, expected=%s" % (cs, state))

    def _build_simple_cmd(self, cmd):
        """
        """
        return cmd

    def _control_s_response_handler(self, response, prompt):
        """
        """
        log.debug("response='%s'  prompt='%s'" % (str(response), str(prompt)))

        # TODO
        return response

    def _got_data(self, data):
        """
        """
        #super(BarsInstrumentProtocol, self)._got_data(data)

        if self._outfile:
            os.write(self._outfile.fileno(), data)
            self._outfile.flush()

        if re.match(DATA_LINE_PATTERN, data):
            # clear prompt buffer
            self._promptbuf = ''

            self._process_streaming_data(data)
        else:
            self._promptbuf += data
            #self._show_buffer('_promptbuf', data)

    def _show_buffer(self, title, buffer, prefix='\n\t| '):
        """
        """
        msg = prefix + buffer.replace('\n', prefix)
        log.debug("%s:%s" % (title, msg))

    def _process_streaming_data(self, data):
        """
        """
        # TODO just printing data out for now
        self._show_buffer('_process_streaming_data', data, '\n\t! ')

    def get_current_state(self):
        """Gets the current state of the protocol."""
        return self._fsm.get_current_state()

    def get(self, params, *args, **kwargs):

        # TODO it only handles BarsParameter.TIME_BETWEEN_BURSTS

        if log.isEnabledFor(logging.DEBUG):
            log.debug("params=%s args=%s kwargs=%s" %
                      (str(params), str(args), str(kwargs)))

        #self._assert_state(DriverState.AUTOSAMPLE)

        assert isinstance(params, list)

        params = list(set(params))  # remove any duplicates

        result = {}
        for param in params:
            if param == BarsParameter.TIME_BETWEEN_BURSTS:
                value = self._get_cycle_time(params)
            else:
                value = InstErrorCode.INVALID_PARAMETER
            result[param] = value

        return result

    def _get_cycle_time(self, params):
        #
        # enter main menu
        #
        self._fsm.on_event(BarsProtocolEvent.ENTER_MAIN_MENU)
        self._assert_state(BarsProtocolState.MAIN_MENU)

        #
        # enter change param menu
        #
        self._fsm.on_event(BarsProtocolEvent.ENTER_CHANGE_PARAMS, params)
        self._assert_state(BarsProtocolState.CHANGE_PARAMS_MENU)

        #
        # save the menu to retrieve info below
        #
        menu = self._promptbuf

        #
        # exit change param menu
        #
        self._fsm.on_event(BarsProtocolEvent.EXIT_CHANGE_PARAMS)
        self._assert_state(BarsProtocolState.MAIN_MENU)

        #
        # return to COLLECTING_DATA
        #
        self._fsm.on_event(BarsProtocolEvent.RESTART_DATA_COLLECTION)
        self._assert_state(BarsProtocolState.COLLECTING_DATA)

        #
        # scan menu for requested value
        #
        seconds = None
        mo = re.search(CYCLE_TIME_PATTERN, menu)
        if mo is not None:
            cycle_time_str = mo.group(1)
            log.debug("scanned cycle_time_str='%s'" % str(cycle_time_str))
            seconds = bars.get_cycle_time_seconds(cycle_time_str)

        if seconds is None:
            raise InstrumentProtocolException(
                    msg="Unexpected: string could not be matched: %s" % menu)

        return seconds

    def set(self, params, *args, **kwargs):
        """
        """
        # TODO it only handles BarsParameter.TIME_BETWEEN_BURSTS

        if log.isEnabledFor(logging.DEBUG):
            log.debug("params=%s args=%s kwargs=%s" %
                      (str(params), str(args), str(kwargs)))

        assert isinstance(params, dict)

        updated_params = 0
        result = {}
        for (param, value) in params.items():
            if param == BarsParameter.TIME_BETWEEN_BURSTS:
                result[param] = self._set_cycle_time(value)
                if InstErrorCode.is_ok(result[param]):
                    updated_params += 1
            else:
                result[param] = InstErrorCode.INVALID_PARAMETER

        msg = "%s parameter(s) successfully set." % updated_params
        log.debug("announcing to driver: %s" % msg)
        self.announce_to_driver(DriverAnnouncement.CONFIG_CHANGE, msg=msg)
        return result

    def _set_cycle_time(self, seconds):
        if not isinstance(seconds, int):
            return InstErrorCode.INVALID_PARAM_VALUE

        if seconds < 15:
            return InstErrorCode.INVALID_PARAM_VALUE

        # TODO implement me!

        log.debug("_set_cycle_time NOT IMPLEMENTED YET!")

        return InstErrorCode.OK

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

    def connect(self, channels=None, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.DEBUG):
            log.debug("channels=%s args=%s kwargs=%s" %
                      (str(channels), str(args), str(kwargs)))

        channels = channels or [BarsChannel.INSTRUMENT]

        self._assert_state(BarsProtocolState.PRE_INIT)
        super(BarsInstrumentProtocol, self).connect(channels, *args, **kwargs)

        log.info("connected.")

        self._fsm.on_event(BarsProtocolEvent.INITIALIZE)

    def disconnect(self, channels=None, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.DEBUG):
            log.debug("channels=%s args=%s kwargs=%s" %
                      (str(channels), str(args), str(kwargs)))

        channels = channels or [BarsChannel.INSTRUMENT]

        #self._assert_state(BarsProtocolState.PRE_INIT)
        super(BarsInstrumentProtocol, self).disconnect(channels, *args,
                                                      **kwargs)

        log.info("disconnected.")

        #self._fsm.on_event(BarsProtocolEvent.INITIALIZE)

    ########################################################################
    # State handlers
    ########################################################################

    def _handler_pre_init_initialize(self, *args, **kwargs):
        """
        Handler to transition from PRE_INIT to appropriate state in the
        actual instrument, typically COLLECTING_DATA.
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        next_state = None
        result = None

        #TODO read from the instrument to determine current state
        #...

        # assume collecting data for the moment
        next_state = BarsProtocolState.COLLECTING_DATA

        return (next_state, result)

    def _handler_collecting_data_enter_main_menu(self, *args, **kwargs):
        """
        handler to enter main menu from collecting data
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        time_limit = time.time() + 60
        log.debug("### automatic ^S")
        got_prompt = False
        while not got_prompt and time.time() <= time_limit:
            log.debug("### sending ^S")
            result = None
            try:
                result = self._do_cmd_resp(CONTROL_S, timeout=2)
            except InstrumentTimeoutException:
                pass  # ignore

            # TODO remove the following except case when
            # InstrumentTimeoutException is used by the core class
            # consistently.
            except InstrumentProtocolException as e:
                if e.error_code != InstErrorCode.TIMEOUT:
                    raise

            time.sleep(1)
            log.debug("### ******** result = '%s'" % str(result))
            log.debug("### ******** linebuff = '%s'" % self._linebuf)
            log.debug("### ******** _promptbuf = '%s'" % self._promptbuf)
            string = self._promptbuf
            got_prompt = GENERIC_PROMPT_PATTERN.search(string) is not None

        if not got_prompt:
            raise InstrumentTimeoutException()

        log.debug("### got prompt. Sending one ^m to clean up any ^S leftover")
        result = self._do_cmd_resp(CONTROL_M, timeout=10)

        time.sleep(1)
        log.debug("### ******** result = '%s'" % str(result))
        log.debug("### ******** linebuff = '%s'" % repr(self._linebuf))
        log.debug("### ******** _promptbuf = '%s'" % repr(self._promptbuf))
        string = self._promptbuf
        got_prompt = GENERIC_PROMPT_PATTERN.search(string) is not None

        if not got_prompt:
            raise InstrumentProtocolException(
                    InstErrorCode.UNKNOWN_ERROR,
                    msg="Unexpected, should have gotten prompt after enter.")

        next_state = BarsProtocolState.MAIN_MENU

        return (next_state, result)

    def  _wakeup(self, timeout=10):
        """overwritten: no need to send anything"""
        return None

    def _handler_main_menu_restart_data_collection(self, *args, **kwargs):
        """
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        next_state = None
        result = None

        # send '1' not expecting response:
        result = self._do_cmd_no_resp('1', timeout=60)

        log.debug("restart_data_coll result='%s'" % str(result))

        # TODO confirm that data is streaming again?
        # ...

        next_state = BarsProtocolState.COLLECTING_DATA

        return (next_state, result)

    def _handler_main_menu_enter_change_params(self, *args, **kwargs):
        """
        handler to enter in the change params menu. This menu shows the
        current value of some parameters so it's used to scan for
        respective values if that's the case.
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        next_state = None
        result = None

        result = self._do_cmd_resp('2', timeout=10)

        log.debug("result='%s'" % str(result))

        next_state = BarsProtocolState.CHANGE_PARAMS_MENU

        return (next_state, result)

    def _handler_main_menu_show_system_diagnostics(self, *args, **kwargs):
        """
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        next_state = None
        result = None

        #TODO send '3' to instrument
        # ...

        # TODO wait for "How Many Scans do you want? --> "
        # ...

        # TODO what number of scans? -> presumably included in the params
        # ...
        num_scans = 3

        # TODO send num_scans
        # ...

        # TODO read response until "Press Enter to return to Main Menu." and
        # notify somebody about it

        # then, back to main menu:
        next_state = BarsProtocolState.MAIN_MENU

        return (next_state, result)

    def _handler_main_menu_enter_set_system_clock(self, *args, **kwargs):
        """
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        next_state = None
        result = None


        # TODO validate params, it should contain a datatime spec
        # ...

        #TODO send '4' to instrument
        # ...

        # TODO wait "Do you want to Change the Current Time? (0 = No, 1 = Yes) --> "
        # TODO enter 1

        # TODO complete interaction based on the given datetime:
#    Enter the Month (1-12)     : 1
#    Enter the Day (1-31)       : 26
#    Enter the Year (Two Digits): 12
#    Enter the Hour (0-23)      : 10
#    Enter the Minute (0-59)    : 51
#    Enter the Second (0-59)    : 14

        # TODO wat "Do you want to Change the Current Time? (0 = No, 1 = Yes) --> "
        # TODO enter 0

        # then, back to main menu:
        next_state = BarsProtocolState.MAIN_MENU

        return (next_state, result)

    def _handler_change_params_menu_exit(self, *args, **kwargs):
        """
        handler to exit change params menu
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        #
        # Send '3'
        #
        result = self._do_cmd_resp(BarsProtocolEvent.ENTER_CHANGE_PARAMS)

        next_state = BarsProtocolState.MAIN_MENU

        return (next_state, result)

    def _state_handler_waiting_for_system_info(self, *args, **kwargs):
        """
        Handler for BarsState.WAITING_FOR_SYSTEM_INFO
        """

        if log.isEnabledFor(logging.DEBUG):
            log.debug("args=%s kwargs=%s" % (str(args), str(kwargs)))

        next_state = None
        result = None

        #
        #TODO wait for some time, then read input stream from instrument
        # until we are sure the complete message has been
        # generated and captured. Then, send the message to somebody.

#        while not message generated:
#            continue waiting while gathering any input from instrument
#
        # etc.

        # TODO: what if we don't get the expected response?
        # ...

        next_state = BarsProtocolState.MAIN_MENU

        return (next_state, result)
Ejemplo n.º 4
0
    def __init__(self, initial_state=InstrumentAgentState.UNINITIALIZED):
        """
        Initialize instrument agent prior to pyon process initialization.
        Define state machine, initialize member variables.
        """
        ResourceAgent.__init__(self)
                
        # Instrument agent state machine.
        self._fsm = InstrumentFSM(InstrumentAgentState, InstrumentAgentEvent, InstrumentAgentEvent.ENTER,
                            InstrumentAgentEvent.EXIT, InstErrorCode.UNHANDLED_EVENT)
        
        # Populate state machine for all state-events.
        self._fsm.add_handler(InstrumentAgentState.POWERED_DOWN, InstrumentAgentEvent.ENTER, self._handler_powered_down_enter)
        self._fsm.add_handler(InstrumentAgentState.POWERED_DOWN, InstrumentAgentEvent.EXIT, self._handler_powered_down_exit)
        
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.ENTER, self._handler_uninitialized_enter)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.EXIT, self._handler_uninitialized_exit)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.POWER_DOWN, self._handler_uninitialized_power_down)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.INITIALIZE, self._handler_uninitialized_initialize)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.RESET, self._handler_uninitialized_reset)

        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.ENTER, self._handler_inactive_enter)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.EXIT, self._handler_inactive_exit)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.INITIALIZE, self._handler_inactive_initialize)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.RESET, self._handler_inactive_reset)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.GO_ACTIVE, self._handler_inactive_go_active)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.ENTER, self._handler_idle_enter)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.EXIT, self._handler_idle_exit)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.GO_INACTIVE, self._handler_idle_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.RESET, self._handler_idle_reset)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.RUN, self._handler_idle_run)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.ENTER, self._handler_stopped_enter)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.EXIT, self._handler_stopped_exit)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.GO_INACTIVE, self._handler_stopped_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.RESET, self._handler_stopped_reset)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.CLEAR, self._handler_stopped_clear)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.RESUME, self._handler_stopped_resume)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.ENTER, self._handler_observatory_enter)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.EXIT, self._handler_observatory_exit)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GO_INACTIVE, self._handler_observatory_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.RESET, self._handler_observatory_reset)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.CLEAR, self._handler_observatory_clear)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.PAUSE, self._handler_observatory_pause)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GO_STREAMING, self._handler_observatory_go_streaming)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GO_DIRECT_ACCESS, self._handler_observatory_go_direct_access)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GET_PARAMS, self._handler_get_params)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.SET_PARAMS, self._handler_observatory_set_params)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.EXECUTE_RESOURCE, self._handler_observatory_execute_resource)

        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.ENTER, self._handler_streaming_enter)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.EXIT, self._handler_streaming_exit)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GO_INACTIVE, self._handler_streaming_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.RESET, self._handler_streaming_reset)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GO_OBSERVATORY, self._handler_streaming_go_observatory)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GET_PARAMS, self._handler_get_params)

        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.ENTER, self._handler_direct_access_enter)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.EXIT, self._handler_direct_access_exit)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.GO_OBSERVATORY, self._handler_direct_access_go_observatory)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        ###############################################################################
        # Instrument agent internal parameters.
        ###############################################################################

        # State machine start state, defaults to unconfigured.
        self._initial_state = initial_state

        # Driver configuration. Passed as part of the spawn configuration
        # or with an initialize command. Sets driver specific
        # context.
        self._dvr_config = None
                
        # Process ID of the driver process. Useful to identify and signal
        # the process if necessary. Set by transition to inactive.
        self._dvr_pid = None
                
        # The driver process popen object. To terminate, signal, wait on,
        # or otherwise interact with the driver process via subprocess.
        # Set by transition to inactive.
        self._dvr_proc = None
        
        # The driver client for communicating to the driver process in
        # request-response or event publication. Set by transition to
        # inactive.
        self._dvr_client = None
                
        # UUID of the current transaction.
        self.transaction_id = None
        
        # List of pending transactions.
        self._pending_transactions = []
                                        
        # Dictionary of data stream IDs for data publishing. Constructed
        # by stream_config agent config member during process on_init.
        self._data_streams = {}
        
        # Dictionary of data stream publishers. Constructed by
        # stream_config agent config member during process on_init.
        self._data_publishers = {}

        # Factories for stream packets. Constructed by driver
        # configuration information on transition to inactive.
        self._packet_factories = {}
        
        # Stream registrar to create publishers. Used to create
        # stream publishers, set during process on_init.
        self._stream_registrar = None

        # Latitude value. Set by subscription to platform. Used to
        # append data packets prior to publication.
        self._lat = 0
        
        # Longitude value. Set by subscription to platform. Used to
        # append data packets prior to publication.
        self._lon = 0

        ###############################################################################
        # Instrument agent parameter capabilities.
        ###############################################################################
        
        self.aparam_ia_param = None
Ejemplo n.º 5
0
class InstrumentAgent(ResourceAgent):
    """
    ResourceAgent derived class for the instrument agent. This class
    logically abstracts instruments as taskable resources in the ION
    system. It directly provides common functionality (common state model,
    common resource interface, point of publication) and creates
    a driver process to specialize for particular hardware.
    """
    def __init__(self, initial_state=InstrumentAgentState.UNINITIALIZED):
        """
        Initialize instrument agent prior to pyon process initialization.
        Define state machine, initialize member variables.
        """
        ResourceAgent.__init__(self)
                
        # Instrument agent state machine.
        self._fsm = InstrumentFSM(InstrumentAgentState, InstrumentAgentEvent, InstrumentAgentEvent.ENTER,
                            InstrumentAgentEvent.EXIT, InstErrorCode.UNHANDLED_EVENT)
        
        # Populate state machine for all state-events.
        self._fsm.add_handler(InstrumentAgentState.POWERED_DOWN, InstrumentAgentEvent.ENTER, self._handler_powered_down_enter)
        self._fsm.add_handler(InstrumentAgentState.POWERED_DOWN, InstrumentAgentEvent.EXIT, self._handler_powered_down_exit)
        
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.ENTER, self._handler_uninitialized_enter)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.EXIT, self._handler_uninitialized_exit)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.POWER_DOWN, self._handler_uninitialized_power_down)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.INITIALIZE, self._handler_uninitialized_initialize)
        self._fsm.add_handler(InstrumentAgentState.UNINITIALIZED, InstrumentAgentEvent.RESET, self._handler_uninitialized_reset)

        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.ENTER, self._handler_inactive_enter)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.EXIT, self._handler_inactive_exit)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.INITIALIZE, self._handler_inactive_initialize)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.RESET, self._handler_inactive_reset)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.GO_ACTIVE, self._handler_inactive_go_active)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.INACTIVE, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.ENTER, self._handler_idle_enter)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.EXIT, self._handler_idle_exit)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.GO_INACTIVE, self._handler_idle_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.RESET, self._handler_idle_reset)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.RUN, self._handler_idle_run)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.IDLE, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.ENTER, self._handler_stopped_enter)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.EXIT, self._handler_stopped_exit)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.GO_INACTIVE, self._handler_stopped_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.RESET, self._handler_stopped_reset)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.CLEAR, self._handler_stopped_clear)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.RESUME, self._handler_stopped_resume)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.STOPPED, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.ENTER, self._handler_observatory_enter)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.EXIT, self._handler_observatory_exit)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GO_INACTIVE, self._handler_observatory_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.RESET, self._handler_observatory_reset)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.CLEAR, self._handler_observatory_clear)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.PAUSE, self._handler_observatory_pause)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GO_STREAMING, self._handler_observatory_go_streaming)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GO_DIRECT_ACCESS, self._handler_observatory_go_direct_access)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.GET_PARAMS, self._handler_get_params)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.SET_PARAMS, self._handler_observatory_set_params)
        self._fsm.add_handler(InstrumentAgentState.OBSERVATORY, InstrumentAgentEvent.EXECUTE_RESOURCE, self._handler_observatory_execute_resource)

        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.ENTER, self._handler_streaming_enter)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.EXIT, self._handler_streaming_exit)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GO_INACTIVE, self._handler_streaming_go_inactive)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.RESET, self._handler_streaming_reset)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GO_OBSERVATORY, self._handler_streaming_go_observatory)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)
        self._fsm.add_handler(InstrumentAgentState.STREAMING, InstrumentAgentEvent.GET_PARAMS, self._handler_get_params)

        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.ENTER, self._handler_direct_access_enter)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.EXIT, self._handler_direct_access_exit)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.GO_OBSERVATORY, self._handler_direct_access_go_observatory)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.GET_RESOURCE_COMMANDS, self._handler_get_resource_commands)
        self._fsm.add_handler(InstrumentAgentState.DIRECT_ACCESS, InstrumentAgentEvent.GET_RESOURCE_PARAMS, self._handler_get_resource_params)

        ###############################################################################
        # Instrument agent internal parameters.
        ###############################################################################

        # State machine start state, defaults to unconfigured.
        self._initial_state = initial_state

        # Driver configuration. Passed as part of the spawn configuration
        # or with an initialize command. Sets driver specific
        # context.
        self._dvr_config = None
                
        # Process ID of the driver process. Useful to identify and signal
        # the process if necessary. Set by transition to inactive.
        self._dvr_pid = None
                
        # The driver process popen object. To terminate, signal, wait on,
        # or otherwise interact with the driver process via subprocess.
        # Set by transition to inactive.
        self._dvr_proc = None
        
        # The driver client for communicating to the driver process in
        # request-response or event publication. Set by transition to
        # inactive.
        self._dvr_client = None
                
        # UUID of the current transaction.
        self.transaction_id = None
        
        # List of pending transactions.
        self._pending_transactions = []
                                        
        # Dictionary of data stream IDs for data publishing. Constructed
        # by stream_config agent config member during process on_init.
        self._data_streams = {}
        
        # Dictionary of data stream publishers. Constructed by
        # stream_config agent config member during process on_init.
        self._data_publishers = {}

        # Factories for stream packets. Constructed by driver
        # configuration information on transition to inactive.
        self._packet_factories = {}
        
        # Stream registrar to create publishers. Used to create
        # stream publishers, set during process on_init.
        self._stream_registrar = None

        # Latitude value. Set by subscription to platform. Used to
        # append data packets prior to publication.
        self._lat = 0
        
        # Longitude value. Set by subscription to platform. Used to
        # append data packets prior to publication.
        self._lon = 0

        ###############################################################################
        # Instrument agent parameter capabilities.
        ###############################################################################
        
        self.aparam_ia_param = None

    def on_init(self):
        """
        Instrument agent pyon process initialization.
        Init objects that depend on the container services and start state
        machine.
        """
        # The registrar to create publishers.
        self._stream_registrar = StreamPublisherRegistrar(process=self,
                                                    node=self.container.node)
        
        # Set the driver config from the agent config if present.
        self._dvr_config = self.CFG.get('driver_config', None)
        
        # Construct stream publishers.
        self._construct_data_publishers()

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


    ###############################################################################
    # Event callback and handling.
    ###############################################################################

    def evt_recv(self, evt):
        """
        Callback to receive asynchronous driver events.
        @param evt The driver event received.
        """
        log.info('Instrument agent %s received driver event %s', self._proc_name,
                 str(evt))
        
        try:
            if evt['type'] == 'sample':
                name = evt['name']
                value = evt['value']
                value['lat'] = [self._lat]
                value['lon'] = [self._lon]
                value['stream_id'] = self._data_streams[name]
                if isinstance(value, dict):
                    packet = self._packet_factories[name](**value)
                    self._data_publishers[name].publish(packet)        
                    log.info('Instrument agent %s published data packet.',
                             self._proc_name)
                    
        except (KeyError, TypeError) as e:
            pass
        
        except Exception as e:
            log.info('Instrument agent %s error %s', self._proc_name, str(e))

    ###############################################################################
    # Instrument agent state transition interface.
    # All the following commands are forwarded as a eponymous event to
    # the agent state machine and return the state handler result.
    ###############################################################################

    def acmd_power_up(self, *args, **kwargs):
        """
        Agent power_up command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.POWER_UP, *args, **kwargs)
    
    def acmd_power_down(self, *args, **kwargs):
        """
        Agent power_down command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.POWER_DOWN, *args, **kwargs)
    
    def acmd_initialize(self, *args, **kwargs):
        """
        Agent initialize command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.INITIALIZE, *args, **kwargs)

    def acmd_reset(self, *args, **kwargs):
        """
        Agent reset command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.RESET, *args, **kwargs)
    
    def acmd_go_active(self, *args, **kwargs):
        """
        Agent go_active command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GO_ACTIVE, *args, **kwargs)

    def acmd_go_inactive(self, *args, **kwargs):
        """
        Agent go_inactive command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GO_INACTIVE, *args, **kwargs)

    def acmd_run(self, *args, **kwargs):
        """
        Agent run command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.RUN, *args, **kwargs)

    def acmd_clear(self, *args, **kwargs):
        """
        Agent clear command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.CLEAR, *args, **kwargs)

    def acmd_pause(self, *args, **kwargs):
        """
        Agent pause command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.PAUSE, *args, **kwargs)

    def acmd_resume(self, *args, **kwargs):
        """
        Agent resume command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.RESUME, *args, **kwargs)

    def acmd_go_streaming(self, *args, **kwargs):
        """
        Agent go_streaming command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GO_STREAMING, *args, **kwargs)

    def acmd_go_direct_access(self, *args, **kwargs):
        """
        Agent go_direct_access command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GO_DIRECT_ACCESS, *args, **kwargs)

    def acmd_go_observatory(self, *args, **kwargs):
        """
        Agent go_observatory command. Forward with args to state machine.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GO_OBSERVATORY, *args, **kwargs)

    ###############################################################################
    # Misc instrument agent command interface.
    ###############################################################################

    def acmd_get_current_state(self, *args, **kwargs):
        """
        Query the agent current state.
        """
        return self._fsm.get_current_state()

    ###############################################################################
    # Instrument agent capabilities interface. These functions override base
    # class helper functinos for specialized instrument agent behavior.
    ###############################################################################

    def _get_resource_commands(self):
        """
        Get driver resource commands. Send event to state machine and return
        response or empty list if none.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GET_RESOURCE_COMMANDS) or []
    
    def _get_resource_params(self):
        """
        Get driver resource parameters. Send event to state machine and return
        response or empty list if none.
        """
        return self._fsm.on_event(InstrumentAgentEvent.GET_RESOURCE_PARAMS) or []

    ###############################################################################
    # Instrument agent resource interface. These functions override ResourceAgent
    # base class functions to specialize behavior for instrument driver resources.
    ###############################################################################
    
    def get_param(self, resource_id="", name=''):
        """
        Get driver resource parameters. Send get_params event and args to agent
        state machine to handle request.
        NOTE: Need to adjust the ResourceAgent class and client for instrument
        interface needs.
        @param resource_id
        @param name A list of (channel, name) tuples of driver parameter
        to retrieve
        @retval Dict of (channel, name) : value parameter values if handled.
        """
        params = name
        return self._fsm.on_event(InstrumentAgentEvent.GET_PARAMS, params) or {}
        
    def set_param(self, resource_id="", name='', value=''):
        """
        Set driver resource parameters. Send set_params event and args to agent
        state machine to handle set driver resource parameters request.
        NOTE: Need to adjust the ResourceAgent class and client for instrument
        interface needs.
        @param resource_id
        @param name a Dict of (channel, name) : value for driver parameters
        to be set.
        @retval Dict of (channel, name) : None or Error if handled.
        """
        params = name
        return self._fsm.on_event(InstrumentAgentEvent.SET_PARAMS, params) or {}
                
    def execute(self, resource_id="", command=None):
        """
        Execute driver resource command. Send execute_resource event and args
        to agent state machine to handle resource execute request.
        @param resource_id
        @param command agent command object containing the driver command
        to execute
        @retval Resrouce agent command response object if handled.
        """
        return self._fsm.on_event(InstrumentAgentEvent.EXECUTE_RESOURCE, command)
                
    ###############################################################################
    # Instrument agent transaction interface.
    ###############################################################################

    def acmd_start_transaction(self):
        """
        """
        pass
    
    def acmd_end_transaction(self):
        """
        """
        pass

    ###############################################################################
    # Powered down state handlers.
    # TBD. This state requires clarification of use.
    ###############################################################################

    def _handler_powered_down_enter(self, *args, **kwargs):
        """
        Handler upon entry to powered_down state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
    
    def _handler_powered_down_exit(self, *args, **kwargs):
        """
        Handler upon exit from powered_down state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    ###############################################################################
    # Uninitialized state handlers.
    # Default start state. The driver has not been configured or started.
    ###############################################################################

    def _handler_uninitialized_enter(self, *args, **kwargs):
        """
        Handler upon entry to uninitialized state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
    
    def _handler_uninitialized_exit(self,  *args, **kwargs):
        """
        Handler upon exit from uninitialized state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    def _handler_uninitialized_power_down(self,  *args, **kwargs):
        """
        Handler for power_down agent command in uninitialized state.
        """
        result = InstErrorCode.NOT_IMPLEMENTED
        next_state = None
        
        return (next_state, result)

    def _handler_uninitialized_initialize(self,  dvr_config=None, *args, **kwargs):
        """
        Handler for initialize agent command in uninitialized state.
        Attempt to start driver process with driver config supplied as
        argument or in agent configuration. Switch to inactive state if
        successful.
        """
        result = None
        next_state = None

        self._dvr_config = dvr_config or self._dvr_config
        result = self._start_driver(self._dvr_config)
        if not result:
            next_state = InstrumentAgentState.INACTIVE

        return (next_state, result)

    def _handler_uninitialized_reset(self,  *args, **kwargs):
        """
        Handler for reset agent command in uninitialized state.
        Exit and reenter uninitializeds state.
        """
        result = None
        next_state = InstrumentAgentState.UNINITIALIZED
        
        return (next_state, result)

    ###############################################################################
    # Inactive state handlers.
    # The driver is configured and started, but not connected.
    ###############################################################################

    def _handler_inactive_enter(self,  *args, **kwargs):
        """
        Handler upon entry to inactive state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())

    
    def _handler_inactive_exit(self,  *args, **kwargs):
        """
        Handler upon exit from inactive state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())


    def _handler_inactive_initialize(self,  dvr_config=None, *args, **kwargs):
        """
        Handler for initialize command in inactive state. Stop and restart
        driver process using new driver config if supplied.
        """
        result = None
        next_state = None
        
        result = self._stop_driver()
        if result:
            return (next_state, result)
        
        self._dvr_config = dvr_config or self._dvr_config
        result = self._start_driver(self._dvr_config)
        if not result:
            next_state = InstrumentAgentState.INACTIVE
                
        return (next_state, result)

    def _handler_inactive_reset(self,  *args, **kwargs):
        """
        Handler for reset agent command in inactive state.
        Stop the driver process and switch to unitinitalized state if
        successful.
        """
        result = None
        next_state = None
        result = self._stop_driver()
        if not result:
            next_state = InstrumentAgentState.UNINITIALIZED
        
        return (next_state, result)

    def _handler_inactive_go_active(self, dvr_comms=None, *args, **kwargs):
        """
        Handler for go_active agent command in inactive state.
        Attempt to establsih communications with all device channels.
        Switch to active state if any channels activated.
        """
        result = None
        next_state = None
        
        if not dvr_comms:
            dvr_comms = self._dvr_config.get('comms_config', None)
            
        cfg_result = self._dvr_client.cmd_dvr('configure', dvr_comms)
        
        channels = [key for (key, val) in cfg_result.iteritems() if not
            InstErrorCode.is_error(val)]
        
        con_result = self._dvr_client.cmd_dvr('connect', channels)

        result = cfg_result.copy()
        for (key, val) in con_result.iteritems():
            result[key] = val

        self._active_channels = self._dvr_client.cmd_dvr('get_active_channels')

        if len(self._active_channels)>0:
                next_state = InstrumentAgentState.IDLE
        
        return (next_state, result)

    ###############################################################################
    # Idle state handlers.
    ###############################################################################

    def _handler_idle_enter(self,  *args, **kwargs):
        """
        Handler upon entry to idle state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
                
    def _handler_idle_exit(self,  *args, **kwargs):
        """
        Handler upon exit from idle state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    def _handler_idle_go_inactive(self, *args, **kwargs):
        """
        Handler for go_inactive agent command in idle state.
        Attempt to disconnect and initialize all active driver channels.
        Swtich to inactive state if successful.
        """
        result = None
        next_state = None
        
        channels = self._dvr_client.cmd_dvr('get_active_channels')
        dis_result = self._dvr_client.cmd_dvr('disconnect', channels)
        
        [key for (key, val) in dis_result.iteritems() if not
            InstErrorCode.is_error(val)]
        
        init_result = self._dvr_client.cmd_dvr('initialize', channels)

        result = dis_result.copy()
        for (key, val) in init_result.iteritems():
            result[key] = val
            
        self._active_channels = self._dvr_client.cmd_dvr('get_active_channels')
            
        if len(self._active_channels)==0:
            next_state = InstrumentAgentState.INACTIVE
            
        return (next_state, result)

    def _handler_idle_reset(self,  *args, **kwargs):
        """
        Handler for reset agent command in idle state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_idle_run(self,  *args, **kwargs):
        """
        Handler for run agent command in idle state.
        Switch to observatory state.
        """
        result = None
        next_state = InstrumentAgentState.OBSERVATORY
        
        return (next_state, result)

    ###############################################################################
    # Stopped state handlers.
    ###############################################################################

    def _handler_stopped_enter(self,  *args, **kwargs):
        """
        Handler for entry into stopped state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
    
    def _handler_stopped_exit(self,  *args, **kwargs):
        """
        Handler for exit from stopped state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    def _handler_stopped_go_inactive(self,  *args, **kwargs):
        """
        Handler for go_inactive agent command in stopped state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_stopped_reset(self,  *args, **kwargs):
        """
        Handler for reset agent command in stopped state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_stopped_clear(self,  *args, **kwargs):
        """
        Handler for clear agent command in stopped state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_stopped_resume(self,  *args, **kwargs):
        """
        Handler for resume agent command in stopped state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    ###############################################################################
    # Observatory state handlers.
    ###############################################################################

    def _handler_observatory_enter(self,  *args, **kwargs):
        """
        Handler upon entry to observatory state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
    
    def _handler_observatory_exit(self,  *args, **kwargs):
        """
        Handler upon exit from observatory state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    def _handler_observatory_go_inactive(self,  *args, **kwargs):
        """
        Handler for go_inactive agent command in observatory state.
        Attempt to disconnect and initialize all active driver channels.
        Switch to inactive state if successful.
        """
        result = None
        next_state = None
        
        channels = self._dvr_client.cmd_dvr('get_active_channels')
        dis_result = self._dvr_client.cmd_dvr('disconnect', channels)
        
        [key for (key, val) in dis_result.iteritems() if not
            InstErrorCode.is_error(val)]
        
        init_result = self._dvr_client.cmd_dvr('initialize', channels)

        result = dis_result.copy()
        for (key, val) in init_result.iteritems():
            result[key] = val
            
        self._active_channels = self._dvr_client.cmd_dvr('get_active_channels')
            
        if len(self._active_channels)==0:
            next_state = InstrumentAgentState.INACTIVE
            
        return (next_state, result)

    def _handler_observatory_reset(self,  *args, **kwargs):
        """
        Handler for reset agent command in observatory state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_observatory_clear(self,  *args, **kwargs):
        """
        Handler for clear agent command in observatory state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_observatory_pause(self,  *args, **kwargs):
        """
        Handler for pause agent command in observatory state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_observatory_go_streaming(self,  *args, **kwargs):
        """
        Handler for go_streaming agent command in observatory state.
        Send start autosample command to driver and switch to streaming
        state if successful.
        """
        result = None
        next_state = None

        result = self._dvr_client.cmd_dvr('start_autosample', *args, **kwargs)
    
        if isinstance(result, dict):
            if any([val == None for val in result.values()]):
                next_state = InstrumentAgentState.STREAMING

        return (next_state, result)

    def _handler_observatory_go_direct_access(self,  *args, **kwargs):
        """
        Handler for go_direct_access agent ommand in observatory state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_get_params(self, params, *args, **kwargs):
        """
        Handler for get_params resource command in observatory state.
        Send get command to driver and return result.
        """
        result = self._dvr_client.cmd_dvr('get', params)
        next_state = None
        
        return (next_state, result)

    def _handler_observatory_set_params(self, params, *args, **kwargs):
        """
        Handler for set_params resource command in observatory state.
        Send the set command to the driver and return result.
        """
        result = self._dvr_client.cmd_dvr('set', params)
        next_state = None
        
        return (next_state, result)

    def _handler_observatory_execute_resource(self, command, *args, **kwargs):
        """
        Handler for execute_resource command in observatory state.
        Issue driver command and return the result.
        """
        result = None
        next_state = None

        if not command:
            raise iex.BadRequest("execute argument 'command' not present")
        if not command.command:
            raise iex.BadRequest("command not set")

        cmd_res = IonObject("AgentCommandResult", command_id=command.command_id,
                            command=command.command)
        cmd_res.ts_execute = get_ion_ts()
        command.command = 'execute_' + command.command
        res = self._dvr_client.cmd_dvr(command.command, *command.args,
                                           **command.kwargs)
        cmd_res.status = 0
        cmd_res.result = res
        result = cmd_res
        
        return (next_state, result)

    ###############################################################################
    # Streaming state handlers.
    ###############################################################################

    def _handler_streaming_enter(self,  *args, **kwargs):
        """
        Handler for entry to streaming state.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
    
    def _handler_streaming_exit(self,  *args, **kwargs):
        """
        Handler upon exit from streaming state.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    def _handler_streaming_go_inactive(self,  *args, **kwargs):
        """
        Handler for go_inactive agent command within streaming state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_streaming_reset(self,  *args, **kwargs):
        """
        Handler for reset agent command within streaming state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    def _handler_streaming_go_observatory(self,  *args, **kwargs):
        """
        Handler for go_observatory agent command within streaming state. Command
        driver to stop autosampling, and switch to observatory mode if
        successful.
        """
        result = None
        next_state = None
        
        result = self._dvr_client.cmd_dvr('stop_autosample', *args, **kwargs)
        
        if isinstance(result, dict):
            if all([val == None for val in result.values()]):
                next_state = InstrumentAgentState.OBSERVATORY
            
        return (next_state, result)

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

    def _handler_direct_access_enter(self,  *args, **kwargs):
        """
        Handler upon direct access entry.
        """
        log.info('Instrument agent entered state %s',
                 self._fsm.get_current_state())
    
    def _handler_direct_access_exit(self,  *args, **kwargs):
        """
        Handler upon direct access exit.
        """
        log.info('Instrument agent left state %s',
                 self._fsm.get_current_state())

    def _handler_direct_access_go_observatory(self,  *args, **kwargs):
        """
        Handler for go_observatory agent command within direct access state.
        """
        result = None
        next_state = None
        
        return (next_state, result)

    ###############################################################################
    # Get resource state handlers.
    # Available for all states with a valid driver process. 
    ###############################################################################

    def _handler_get_resource_params(self,  *args, **kwargs):
        """
        Handler for get_resource_params resource command. Send
        get_resource_params and args to driver and return result.
        """
        result = self._dvr_client.cmd_dvr('get_resource_params')
        next_state = None
        
        return (next_state, result)

    def _handler_get_resource_commands(self,  *args, **kwargs):
        """
        Handler for get_resource_commands resource command. Send
        get_resource_commands and args to driver and return result.
        """
        result = self._dvr_client.cmd_dvr('get_resource_commands')
        next_state = None

        return (next_state, result)

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

    def _start_driver(self, dvr_config):
        """
        Start the driver process and driver client.
        @param dvr_config The driver configuration.
        @param comms_config The driver communications configuration.
        @retval None or error.
        """
        try:        
            cmd_port = dvr_config['cmd_port']
            evt_port = dvr_config['evt_port']
            dvr_mod = dvr_config['dvr_mod']
            dvr_cls = dvr_config['dvr_cls']
            svr_addr = dvr_config['svr_addr']
            
        except (TypeError, KeyError):
            # Not a dict. or missing required parameter.
            log.error('Insturment agent %s missing required parameter in start_driver.',
                      self._proc_name)            
            return InstErrorCode.REQUIRED_PARAMETER
                
        # Launch driver process.
        self._dvr_proc = ZmqDriverProcess.launch_process(cmd_port, evt_port,
                                                         dvr_mod,  dvr_cls)

        self._dvr_proc.poll()
        if self._dvr_proc.returncode:
            # Error proc didn't start.
            log.error('Insturment agent %s driver process did not launch.',
                      self._proc_name)
            return InstErrorCode.AGENT_INIT_FAILED

        log.info('Insturment agent %s launched driver process.', self._proc_name)
        
        # Create client and start messaging.
        self._dvr_client = ZmqDriverClient(svr_addr, cmd_port, evt_port)
        self._dvr_client.start_messaging(self.evt_recv)
        log.info('Insturment agent %s driver process client started.',
                 self._proc_name)
        time.sleep(1)

        try:        
            retval = self._dvr_client.cmd_dvr('process_echo', 'Test.')
            log.info('Insturment agent %s driver process echo test: %s.',
                     self._proc_name, str(retval))
            
        except Exception:
            self._dvr_proc.terminate()
            self._dvr_proc.wait()
            self._dvr_proc = None
            self._dvr_client = None
            log.error('Insturment agent %s error commanding driver process.',
                      self._proc_name)            
            return InstErrorCode.AGENT_INIT_FAILED

        else:
            log.info('Insturment agent %s started its driver.', self._proc_name)
            self._construct_packet_factories()

    def _stop_driver(self):
        """
        Stop the driver process and driver client.
        @retval None.
        """
        if self._dvr_client:
            self._dvr_client.done()
            self._dvr_proc.wait()
            self._dvr_proc = None
            self._dvr_client = None
            self._clear_packet_factories()
            log.info('Insturment agent %s stopped its driver.', self._proc_name)
            
        time.sleep(1)

    def _construct_data_publishers(self):
        """
        Construct the stream publishers from the stream_config agent
        config variable.
        @retval None
        """
        stream_config = self.CFG.stream_config

        for (name, stream_id) in stream_config.iteritems():
            self._data_streams[name] = stream_id
            publisher = self._stream_registrar.create_publisher(stream_id=stream_id)
            self._data_publishers[name] = publisher
            log.info('Instrumen agent %s created publisher for stream %s',
                     self._proc_name, name)        
        
    def _construct_packet_factories(self):
        """
        Construct packet factories from packet_config member of the
        driver_config.
        @retval None
        """
        packet_config = self._dvr_config['packet_config']
        for (name, val) in packet_config.iteritems():
            if val:
                mod = val[0]
                cls = val[1]
                import_str = 'from %s import %s' % (mod, cls)
                ctor_str = 'ctor = %s' % cls
                
                try:
                    exec import_str
                    exec ctor_str
                    
                except Exception:
                    log.error('Instrument agent %s had error creating packet factories from %s.%s',
                              self._proc_name, mod, cls)
                
                else:
                    self._packet_factories[name] = ctor
                    log.info('Instrument agent %s created packet factory for stream %s',
                             self._proc_name, name)
                    
    def _clear_packet_factories(self):
        """
        Delete packet factories.
        @retval None
        """
        self._packet_factories.clear()
        log.info('Instrument agent %s deleted packet factories.', self._proc_name)
            
    ###############################################################################
    # Misc and test.
    ###############################################################################

    def test_ia(self):
        log.info('Hello from the instrument agent!')
Ejemplo n.º 6
0
    def __init__(self, prompts, newline, evt_callback):
        """
        """
        CommandResponseInstrumentProtocol.__init__(self, evt_callback, prompts, newline)
        
        # Build protocol state machine.
        self._fsm = InstrumentFSM(SBE37State, SBE37Event, SBE37Event.ENTER,
                            SBE37Event.EXIT, InstErrorCode.UNHANDLED_EVENT)
        
        # Add handlers for all events.
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.ENTER, self._handler_unconfigured_enter)
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.EXIT, self._handler_unconfigured_exit)
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.INITIALIZE, self._handler_unconfigured_initialize)
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.CONFIGURE, self._handler_unconfigured_configure)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.ENTER, self._handler_disconnected_enter)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.EXIT, self._handler_disconnected_exit)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.INITIALIZE, self._handler_disconnected_initialize)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.CONFIGURE, self._handler_disconnected_configure)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.CONNECT, self._handler_disconnected_connect)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.ENTER, self._handler_command_enter)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.EXIT, self._handler_command_exit)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.DISCONNECT, self._handler_command_disconnect)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.GET, self._handler_command_autosample_get)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.SET, self._handler_command_set)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.ACQUIRE_SAMPLE, self._handler_command_acquire_sample)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.START_AUTOSAMPLE, self._handler_command_start_autosample)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.TEST, self._handler_command_test)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.UPDATE_PARAMS, self._handler_command_update_params)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.ENTER, self._handler_autosample_enter)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.EXIT, self._handler_autosample_exit)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.STOP_AUTOSAMPLE, self._handler_autosample_stop_autosample)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.GET, self._handler_command_autosample_get)

        # Start state machine.
        self._fsm.start(SBE37State.UNCONFIGURED)

        # Add build command handlers.
        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('set', self._build_set_command)

        # Add parse response handlers.
        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)

        # 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)
        
        # Add parameter handlers to parameter dict.        
        self._add_param_dict(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._add_param_dict(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._add_param_dict(SBE37Parameter.NAVG,
                             r'number of samples to average = (\d+)',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(SBE37Parameter.SAMPLENUM,
                             r'samplenumber = (\d+), free = \d+',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(SBE37Parameter.INTERVAL,
                             r'sample interval = (\d+) seconds',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.TXREALTIME,
                             r'(do not )?transmit real-time data',
                             lambda match : False if match.group(1) else True,
                             self._true_false_to_string)
        self._add_param_dict(SBE37Parameter.SYNCMODE,
                             r'serial sync mode (enabled|disabled)',
                             lambda match : False if (match.group(1)=='disabled') else True,
                             self._true_false_to_string)
        self._add_param_dict(SBE37Parameter.SYNCWAIT,
                             r'wait time after serial sync sampling = (\d+) seconds',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.TA0,
                             r' +TA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.TA1,
                             r' +TA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.TA2,
                             r' +TA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.TA3,
                             r' +TA3 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.CG,
                             r' +G = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CH,
                             r' +H = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CI,
                             r' +I = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CJ,
                             r' +J = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.WBOTC,
                             r' +WBOTC = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CTCOR,
                             r' +CTCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CPCOR,
                             r' +CPCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.PA0,
                             r' +PA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PA1,
                             r' +PA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PA2,
                             r' +PA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCA0,
                             r' +PTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCA1,
                             r' +PTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCA2,
                             r' +PTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCB0,
                             r' +PTCSB0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCB1,
                             r' +PTCSB1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCB2,
                             r' +PTCSB2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.POFFSET,
                             r' +POFFSET = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.RTCA0,
                             r' +RTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.RTCA1,
                             r' +RTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.RTCA2,
                             r' +RTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
Ejemplo n.º 7
0
class SBE37Protocol(CommandResponseInstrumentProtocol):
    """
    """
    def __init__(self, prompts, newline, evt_callback):
        """
        """
        CommandResponseInstrumentProtocol.__init__(self, evt_callback, prompts, newline)
        
        # Build protocol state machine.
        self._fsm = InstrumentFSM(SBE37State, SBE37Event, SBE37Event.ENTER,
                            SBE37Event.EXIT, InstErrorCode.UNHANDLED_EVENT)
        
        # Add handlers for all events.
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.ENTER, self._handler_unconfigured_enter)
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.EXIT, self._handler_unconfigured_exit)
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.INITIALIZE, self._handler_unconfigured_initialize)
        self._fsm.add_handler(SBE37State.UNCONFIGURED, SBE37Event.CONFIGURE, self._handler_unconfigured_configure)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.ENTER, self._handler_disconnected_enter)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.EXIT, self._handler_disconnected_exit)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.INITIALIZE, self._handler_disconnected_initialize)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.CONFIGURE, self._handler_disconnected_configure)
        self._fsm.add_handler(SBE37State.DISCONNECTED, SBE37Event.CONNECT, self._handler_disconnected_connect)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.ENTER, self._handler_command_enter)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.EXIT, self._handler_command_exit)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.DISCONNECT, self._handler_command_disconnect)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.GET, self._handler_command_autosample_get)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.SET, self._handler_command_set)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.ACQUIRE_SAMPLE, self._handler_command_acquire_sample)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.START_AUTOSAMPLE, self._handler_command_start_autosample)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.TEST, self._handler_command_test)
        self._fsm.add_handler(SBE37State.COMMAND, SBE37Event.UPDATE_PARAMS, self._handler_command_update_params)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.ENTER, self._handler_autosample_enter)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.EXIT, self._handler_autosample_exit)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.STOP_AUTOSAMPLE, self._handler_autosample_stop_autosample)
        self._fsm.add_handler(SBE37State.AUTOSAMPLE, SBE37Event.GET, self._handler_command_autosample_get)

        # Start state machine.
        self._fsm.start(SBE37State.UNCONFIGURED)

        # Add build command handlers.
        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('set', self._build_set_command)

        # Add parse response handlers.
        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)

        # 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)
        
        # Add parameter handlers to parameter dict.        
        self._add_param_dict(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._add_param_dict(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._add_param_dict(SBE37Parameter.NAVG,
                             r'number of samples to average = (\d+)',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(SBE37Parameter.SAMPLENUM,
                             r'samplenumber = (\d+), free = \d+',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(SBE37Parameter.INTERVAL,
                             r'sample interval = (\d+) seconds',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.TXREALTIME,
                             r'(do not )?transmit real-time data',
                             lambda match : False if match.group(1) else True,
                             self._true_false_to_string)
        self._add_param_dict(SBE37Parameter.SYNCMODE,
                             r'serial sync mode (enabled|disabled)',
                             lambda match : False if (match.group(1)=='disabled') else True,
                             self._true_false_to_string)
        self._add_param_dict(SBE37Parameter.SYNCWAIT,
                             r'wait time after serial sync sampling = (\d+) seconds',
                             lambda match : int(match.group(1)),
                             self._int_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.TA0,
                             r' +TA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.TA1,
                             r' +TA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.TA2,
                             r' +TA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.TA3,
                             r' +TA3 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.CG,
                             r' +G = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CH,
                             r' +H = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CI,
                             r' +I = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CJ,
                             r' +J = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.WBOTC,
                             r' +WBOTC = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CTCOR,
                             r' +CTCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.CPCOR,
                             r' +CPCOR = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.PA0,
                             r' +PA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PA1,
                             r' +PA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PA2,
                             r' +PA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCA0,
                             r' +PTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCA1,
                             r' +PTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCA2,
                             r' +PTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCB0,
                             r' +PTCSB0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCB1,
                             r' +PTCSB1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.PTCB2,
                             r' +PTCSB2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.POFFSET,
                             r' +POFFSET = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(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._add_param_dict(SBE37Parameter.RTCA0,
                             r' +RTCA0 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.RTCA1,
                             r' +RTCA1 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)
        self._add_param_dict(SBE37Parameter.RTCA2,
                             r' +RTCA2 = (-?\d.\d\d\d\d\d\de[-+]\d\d)',
                             lambda match : float(match.group(1)),
                             self._float_to_string)

    ########################################################################
    # Protocol connection interface.
    ########################################################################

    def initialize(self, *args, **kwargs):
        """
        """
        
        # Construct state machine params and fire event.
        return self._fsm.on_event(SBE37Event.INITIALIZE, *args, **kwargs)
    
    def configure(self, *args, **kwargs):
        """
        """

        # Construct state machine params and fire event.
        return self._fsm.on_event(SBE37Event.CONFIGURE, *args, **kwargs)
    
    def connect(self, *args, **kwargs):
        """
        """

        # Construct state machine params and fire event.
        return self._fsm.on_event(SBE37Event.CONNECT, *args, **kwargs)
    
    def disconnect(self, *args, **kwargs):
        """
        """

        # Construct state machine params and fire event.
        return self._fsm.on_event(SBE37Event.DISCONNECT, *args, **kwargs)
    
    def detach(self, *args, **kwargs):
        """
        """

        # Construct state machine params and fire event.
        return self._fsm.on_event(SBE37Event.DETACH, *args, **kwargs)

    ########################################################################
    # Protocol command interface.
    ########################################################################

    def get(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.GET, *args, **kwargs)
    
    def set(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.SET, *args, **kwargs)

    def execute_direct(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.EXECUTE, *args, **kwargs)
    
    def execute_acquire_sample(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.ACQUIRE_SAMPLE, *args, **kwargs)

    def execute_start_autosample(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.START_AUTOSAMPLE, *args, **kwargs)

    def execute_stop_autosample(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.STOP_AUTOSAMPLE, *args, **kwargs)

    def execute_test(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.TEST, *args, **kwargs)

    def update_params(self, *args, **kwargs):
        """
        """
        return self._fsm.on_event(SBE37Event.UPDATE_PARAMS, *args, **kwargs)
            
    ########################################################################
    # Protocol query interface.
    ########################################################################
    
    
    def get_resource_commands(self):
        """
        """
        return [cmd for cmd in dir(self) if cmd.startswith('execute_')]    
    
    def get_resource_params(self):
        """
        """
        return self._get_param_dict_names()
        
    def get_current_state(self):
        """
        """
        return self._fsm.get_current_state()

    ########################################################################
    # State handlers
    ########################################################################

    ########################################################################
    # SBE37State.UNCONFIGURED
    ########################################################################
    
    def _handler_unconfigured_enter(self, *args, **kwargs):
        """
        """
        
        mi_logger.info('channel %s entered state %s', SBE37Channel.CTD,
                           SBE37State.UNCONFIGURED)
        self._publish_state_change(SBE37State.UNCONFIGURED)
        
        # Initialize throws no exceptions.
        InstrumentProtocol.initialize(self, *args, **kwargs)
    
    def _handler_unconfigured_exit(self, *args, **kwargs):
        """
        """
        pass

    def _handler_unconfigured_initialize(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None
        
        # Reenter initialize.
        next_state = SBE37State.UNCONFIGURED

        return (next_state, result)

    def _handler_unconfigured_configure(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None
        
        try:
            InstrumentProtocol.configure(self, *args, **kwargs)
                
        except (TypeError, KeyError, InstrumentConnectionException, IndexError):
            result = InstErrorCode.INVALID_PARAMETER
            next_state = None
                
        # Everything worked, set next state.
        else:
            next_state = SBE37State.DISCONNECTED
                
        return (next_state, result)
        
    ########################################################################
    # SBE37State.DISCONNECTED
    ########################################################################

    def _handler_disconnected_enter(self, *args, **kwargs):
        """
        """
        mi_logger.info('channel %s entered state %s',SBE37Channel.CTD,
                           SBE37State.DISCONNECTED)
        self._publish_state_change(SBE37State.DISCONNECTED)

    def _handler_disconnected_exit(self, *args, **kwargs):
        """
        """
        pass

    def _handler_disconnected_initialize(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None
        
        # Switch to unconfigured to initialize comms.
        next_state = SBE37State.UNCONFIGURED

        return (next_state, result)

    def _handler_disconnected_configure(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None
        
        try:
            InstrumentProtocol.configure(self, *args, **kwargs)
                
        except (TypeError, KeyError, InstrumentConnectionException, IndexError):
            result = InstErrorCode.INVALID_PARAMETER
            next_state = SBE37State.UNCONFIGURED

        return (next_state, result)

    def _handler_disconnected_connect(self, *args, **kwargs):
        """
        @throw InstrumentTimeoutException on timeout
        """        
        next_state = None
        result = None

        try:
            InstrumentProtocol.connect(self, *args, **kwargs)
            timeout = kwargs.get('timeout', 10)
            prompt = self._wakeup(timeout)
            if prompt == SBE37Prompt.COMMAND:
                next_state = SBE37State.COMMAND
    
            elif prompt == SBE37Prompt.AUTOSAMPLE:
                next_state = SBE37State.AUTOSAMPLE
        
        except InstrumentConnectionException:
            # Connection failed, fail and stay here.
            next_state = None
            result = InstErrorCode.DRIVER_CONNECT_FAILED
        
        except InstrumentTimeoutException:
            # Timeout connecting or waking device. Stay disconnected.
            InstrumentProtocol.disconnect(self, *args, **kwargs)
            next_state = None
            result = InstErrorCode.DRIVER_CONNECT_FAILED

        return (next_state, result)

    ########################################################################
    # SBE37State.COMMAND
    ########################################################################

    def _handler_command_enter(self, *args, **kwargs):
        """
        """
        mi_logger.info('channel %s entered state %s',SBE37Channel.CTD,
                           SBE37State.COMMAND)
        self._publish_state_change(SBE37State.COMMAND)
        self._update_params(*args, **kwargs)

    def _handler_command_exit(self, *args, **kwargs):
        """
        """
        pass

    def _handler_command_disconnect(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None
        
        try:
            mi_logger.info('DISCONNECTING')
            InstrumentProtocol.disconnect(self, *args, **kwargs)
            mi_logger.info('DONE DISCONNECTING')
            next_state = SBE37State.DISCONNECTED

        except InstrumentConnectionException:
            # Disconnect failed. Fail and stay here.
            next_state = None
            result = InstErrorCode.DISCONNECT_FAILED
            
        else:
            next_state = SBE37State.DISCONNECTED
            result = InstErrorCode.OK

        return (next_state, result)

    def _handler_command_set(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None

        try:
            result = self._do_cmd_resp('set', *args, **kwargs)            
            next_state = None
            
        except InstrumentTimeoutException:
            next_state = None
            result = InstErrorCode.TIMEOUT
        
        except IndexError:
            next_state = None
            result = InstErrorCode.REQUIRED_PARAMETER
        
        return (next_state, result)

    def _handler_command_acquire_sample(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None

        try:
            result = self._do_cmd_resp('ts', *args, **kwargs)
        
        except InstrumentTimeoutException:
            result = InstErrorCode.TIMEOUT

        return (next_state, result)

    def _handler_command_start_autosample(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None

        try:
            self._do_cmd_no_resp('startnow', *args, **kwargs)                
            next_state = SBE37State.AUTOSAMPLE
        
        except InstrumentTimeoutException:
            result = InstErrorCode.TIMEOUT

        return (next_state, result)

    def _handler_command_test(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None

        return (next_state, result)

    def _handler_command_update_params(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None

        try:
            self._update_params(*args, **kwargs)
        
        except InstrumentTimeoutError:
            result = InstErrorCode.TIMEOUT
        
        return (next_state, result)

    ########################################################################
    # SBE37State.AUTOSAMPLE
    ########################################################################

    def _handler_autosample_enter(self, *args, **kwargs):
        """
        """
        mi_logger.info('channel %s entered state %s',SBE37Channel.CTD,
                           SBE37State.AUTOSAMPLE)
        self._publish_state_change(SBE37State.AUTOSAMPLE)
        
    def _handler_autosample_exit(self,  *args, **kwargs):
        """
        """
        pass

    def _handler_autosample_stop_autosample(self, *args, **kwargs):
        """
        @throw InstrumentProtocolException on invalid command
        """
        next_state = None
        result = None

        try:
            prompt = None
            timeout = kwargs.get('timeout', 10)
            while prompt != SBE37Prompt.AUTOSAMPLE:
                prompt = self._wakeup(timeout)
            self._do_cmd_resp('stop', *args, **kwargs)
            prompt = None
            while prompt != SBE37Prompt.COMMAND:
                prompt = self._wakeup(timeout)
            next_state = SBE37State.COMMAND
            
        except InstrumentTimeoutException:
            result = InstErrorCode.TIMEOUT

        return (next_state, result)

    ########################################################################
    # SBE37State.COMMAND and SBE37State.AUTOSAMPLE common handlers.
    ########################################################################

    def _handler_command_autosample_get(self, *args, **kwargs):
        """
        """
        next_state = None
        result = None
        
        try:
            parameter = args[0]
            
        except IndexError:
            result = InstErrorCode.REQUIRED_PARAMETER
            
        else:
            try:
                result = self._get_param_dict(parameter)

            except KeyError:
                result = InstErrorCode.INVALID_PARAMETER
            
        return (next_state, result)

    ########################################################################
    # Private helpers
    ########################################################################
    
    def _got_data(self, data):
        """
        """
        CommandResponseInstrumentProtocol._got_data(self, data)
        
        # Only keep the latest characters in the prompt buffer.
        if len(self._promptbuf)>7:
            self._promptbuf = self._promptbuf[-7:]
            
        # If we are streaming, process the line buffer for samples.
        if self._fsm.get_current_state() == SBE37State.AUTOSAMPLE:
            self._process_streaming_data()
        
    def _process_streaming_data(self):
        """
        """
        if self.eoln in self._linebuf:
            lines = self._linebuf.split(SBE37_NEWLINE)
            self._linebuf = lines[-1]
            for line in lines:
                sample = self._extract_sample(line, True)
    
    def _send_wakeup(self):
        """
        """
        self._logger_client.send(SBE37_NEWLINE)

    def _update_params(self, *args, **kwargs):
        """
        """
        timeout = kwargs.get('timeout', 10)
        old_config = self._get_config_param_dict()
        self._do_cmd_resp('ds',timeout=timeout)
        self._do_cmd_resp('dc',timeout=timeout)
        new_config = self._get_config_param_dict()            
        if new_config != old_config:
            if self.send_event:
                event = {
                    'type' : 'config_change',
                    'value' : new_config
                }
                self.send_event(event)
        
    def _build_simple_command(self, cmd):
        """
        """
        return cmd+SBE37_NEWLINE
    
    def _build_set_command(self, cmd, param, val):
        """
        """
        str_val = self._format_param_dict(param, val)
        set_cmd = '%s=%s' % (param, str_val)
        set_cmd = set_cmd + SBE37_NEWLINE
        return set_cmd

    def _parse_dsdc_response(self, response, prompt):
        """
        """
        for line in response.split(SBE37_NEWLINE):
            self._update_param_dict(line)
        
    def _parse_ts_response(self, response, prompt):
        """
        """
        sample = None
        for line in response.split(SBE37_NEWLINE):
            sample = self._extract_sample(line, True)
            if sample: break
            
        return sample

    def _extract_sample(self, line, publish=True):
        """
        """
        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))]

            # Extract sound velocity and salinity if present.
            #if match.group(5) and match.group(7):
            #    sample['salinity'] = float(match.group(5))
            #    sample['sound_velocity'] = float(match.group(7))
            #elif match.group(5):
            #    if self._get_param_dict(SBE37Parameter.OUTPUTSAL):
            #        sample['salinity'] = float(match.group(5))
            #    elif self._get_param_dict(SBE37Parameter.OUTPUTSV):
            #        sample['sound_velocity'] = match.group(5)
        
            # Extract date and time if present.
            # sample_time = None
            #if  match.group(8):
            #    sample_time = time.strptime(match.group(8),', %d %b %Y, %H:%M:%S')
            #
            #elif match.group(15):
            #    sample_time = time.strptime(match.group(15),', %m-%d-%Y, %H:%M:%S')
            #
            #if sample_time:
            #    sample['time'] = \
            #        '%4i-%02i-%02iT:%02i:%02i:%02i' % sample_time[:6]

            # Add UTC time from driver in iso 8601 format.
            #sample['driver_time'] = datetime.datetime.utcnow().isoformat()

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


            if publish and self.send_event:
                event = {
                    'type':'sample',
                    'name':'ctd_parsed',
                    'value':sample
                }
                self.send_event(event)
                

        return sample            

    def _parse_set_response(self, response, prompt):
        """
        """
        if prompt == SBE37Prompt.COMMAND:
            return InstErrorCode.OK
        else:
            return InstErrorCode.BAD_DRIVER_COMMAND

    def _publish_state_change(self, state):
        """
        """
        if self.send_event:
            event = {
                'type': 'state_change',
                'value': state
            }
            self.send_event(event)


    ########################################################################
    # 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, or
            None if the input is not a valid bool.
        """
        
        if not isinstance(v,bool):
            return None
        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, or None if
            the input is not a valid int value.
        """
        
        if not isinstance(v,int):
            return None
        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, or None if
            the input is not a valid float value.
        """

        if not isinstance(v,float):
            return None
        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,
            or None if the input is not a valid date tuple.
        """

        if not isinstance(v,(list,tuple)):
            return None
        
        if not len(v)==3:
            return None
        
        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:
            return None
        
        if not isinstance(month,int) or month < 1 or month > 12:
            return None

        if not isinstance(year,int) or year < 0 or year > 99:
            return None
        
        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, or None if the input string is not valid.
        """
        if not isinstance(datestr,str):
            return None
        try:
            date_time = time.strptime(datestr,fmt)
            date = (date_time[2],date_time[1],date_time[0])

        except ValueError:
            return None
                        
        return date
Ejemplo n.º 8
0
class SatlanticPARInstrumentProtocol(CommandResponseInstrumentProtocol):
    """The instrument protocol classes to deal with a Satlantic PAR sensor.
    
    The protocol is a very simple command/response protocol with a few show
    commands and a few set commands.
    @todo Check for valid state transitions and handle requests appropriately
    possibly using better exceptions from the fsm.on_event() method
    """

    def __init__(self, callback=None):
        CommandResponseInstrumentProtocol.__init__(self, callback, Prompt, "\n")

        self._fsm = InstrumentFSM(State, Event, Event.ENTER_STATE, Event.EXIT_STATE, InstErrorCode.UNHANDLED_EVENT)
        self._fsm.add_handler(State.COMMAND_MODE, Event.COMMAND, self._handler_command_command)
        self._fsm.add_handler(State.COMMAND_MODE, Event.GET, self._handler_command_get)
        self._fsm.add_handler(State.COMMAND_MODE, Event.SET, self._handler_command_set)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.BREAK, self._handler_autosample_break)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.STOP, self._handler_autosample_stop)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.RESET, self._handler_reset)
        self._fsm.add_handler(State.AUTOSAMPLE_MODE, Event.COMMAND, self._handler_autosample_command)
        self._fsm.add_handler(State.POLL_MODE, Event.AUTOSAMPLE, self._handler_poll_autosample)
        self._fsm.add_handler(State.POLL_MODE, Event.RESET, self._handler_reset)
        self._fsm.add_handler(State.POLL_MODE, Event.SAMPLE, self._handler_poll_sample)
        self._fsm.add_handler(State.POLL_MODE, Event.COMMAND, self._handler_poll_command)
        self._fsm.add_handler(State.UNKNOWN, Event.INITIALIZE, self._handler_initialize)
        self._fsm.start(State.UNKNOWN)

        self._add_build_handler(Command.SET, self._build_set_command)
        self._add_build_handler(Command.GET, self._build_param_fetch_command)
        self._add_build_handler(Command.SAVE, self._build_exec_command)
        self._add_build_handler(Command.EXIT, self._build_exec_command)
        self._add_build_handler(Command.EXIT_AND_RESET, self._build_exec_command)
        self._add_build_handler(Command.AUTOSAMPLE, self._build_control_command)
        self._add_build_handler(Command.RESET, self._build_control_command)
        self._add_build_handler(Command.BREAK, self._build_control_command)
        self._add_build_handler(Command.SAMPLE, self._build_control_command)
        self._add_build_handler(Command.STOP, self._build_control_command)

        self._add_response_handler(Command.SET, self._parse_set_response)
        # self._add_response_handler(Command.GET, self._parse_get_response)

        self._add_param_dict(
            Parameter.TELBAUD,
            r"Telemetry Baud Rate:\s+(\d+) bps",
            lambda match: int(match.group(1)),
            self._int_to_string,
        )

        self._add_param_dict(
            Parameter.MAXRATE, r"Maximum Frame Rate:\s+(\d+) Hz", lambda match: int(match.group(1)), self._int_to_string
        )

    # The normal interface for a protocol. These should drive the FSM
    # transitions as they get things done.
    def get(self, *args, **kwargs):
        """ Get the given parameters from the instrument
        
        @param params The parameter values to get
        @retval Result of FSM event handle, hould be a dict of parameters and values
        @throws InstrumentProtocolException On invalid parameter
        """
        # Parameters checked in Handler
        result = self._fsm.on_event(Event.GET, *args, **kwargs)
        if result == None:
            raise InstrumentProtocolException(InstErrorCode.INCORRECT_STATE)
        assert isinstance(result, dict)
        return result

    def set(self, *args, **kwargs):
        """ Set the given parameters on the instrument
        
        @param params The dict of parameters and values to set
        @retval result of FSM event handle
        @throws InstrumentProtocolException On invalid parameter
        """
        # Parameters checked in handler
        result = self._fsm.on_event(Event.SET, *args, **kwargs)
        if result == None:
            raise InstrumentProtocolException(InstErrorCode.INCORRECT_STATE)
        assert isinstance(result, dict)
        return result

    def execute_save(self, *args, **kwargs):
        """ Execute the save command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        kwargs.update({KwargsKey.COMMAND: Command.SAVE})
        return self._fsm.on_event(Event.COMMAND, *args, **kwargs)

    def execute_exit(self, *args, **kwargs):
        """ Execute the exit command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        kwargs.update({KwargsKey.COMMAND: Command.EXIT})
        return self._fsm.on_event(Event.COMMAND, *args, **kwargs)

    def execute_exit_and_reset(self, *args, **kwargs):
        """ Execute the exit and reset command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        kwargs.update({KwargsKey.COMMAND: Command.EXIT_AND_RESET})
        return self._fsm.on_event(Event.COMMAND, *args, **kwargs)

    def execute_poll(self, *args, **kwargs):
        """ Execute the poll command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        kwargs.update({KwargsKey.COMMAND: Command.POLL})
        return self._fsm.on_event(Event.COMMAND, *args, **kwargs)

    def execute_reset(self, *args, **kwargs):
        """ Execute the reset command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        return self._fsm.on_event(Event.RESET, *args, **kwargs)

    def execute_break(self, *args, **kwargs):
        """ Execute the break command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        return self._fsm.on_event(Event.BREAK, *args, **kwargs)

    def execute_stop(self, *args, **kwargs):
        """ Execute the stop command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        return self._fsm.on_event(Event.STOP, *args, **kwargs)

    def execute_autosample(self, *args, **kwargs):
        """ Execute the autosample command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        return self._fsm.on_event(Event.AUTOSAMPLE, *args, **kwargs)

    def execute_sample(self, *args, **kwargs):
        """ Execute the sample command

        @retval None if nothing was done, otherwise result of FSM event handle
        @throws InstrumentProtocolException On invalid command or missing
        """
        return self._fsm.on_event(Event.SAMPLE, *args, **kwargs)

    def get_config(self):
        """ Get the entire configuration for the instrument
        
        @param params The parameters and values to set
        @retval None if nothing was done, otherwise result of FSM event handle
        Should be a dict of parameters and values
        @throws InstrumentProtocolException On invalid parameter
        """
        result = self.get([Parameter.TELBAUD, Parameter.MAXRATE])
        assert isinstance(result, dict)
        assert result.has_key(Parameter.TELBAUD)
        assert result.has_key(Parameter.MAXRATE)
        return result

    def restore_config(self, config=None):
        """ Apply a complete configuration.
        
        In this instrument, it is simply a compound set that must contain all
        of the parameters.
        @throws InstrumentProtocolException on missing or bad config
        """
        if config == None:
            raise InstrumentProtocolException(InstErrorCode.INVALID_PARAMETER)

        if (config.has_key(Parameter.TELBAUD)) and (config.has_key(Parameter.MAXRATE)):
            assert isinstance(config, dict)
            assert len(config) == 2
            self.set(config)
        else:
            raise InstrumentProtocolException(InstErrorCode.INVALID_PARAMETER)

    def get_status(self):
        """
        Get the current state of the state machine as the instrument
        doesnt maintain a status beyond its configuration and its active mode
        
        @retval Something from the State enum
        """
        return self._fsm.current_state()

    def initialize(self, *args, **kwargs):
        mi_logger.info("Initializing PAR sensor")
        self._fsm.on_event(Event.INITIALIZE, *args, **kwargs)

    ################
    # State handlers
    ################
    def _handler_initialize(self, *args, **kwargs):
        """Handle transition from UNKNOWN state to a known one.
        
        This method determines what state the device is in or gets it to a
        known state so that the instrument and protocol are in sync.
        @param params Parameters to pass to the state
        @retval return (next state, result)
        """
        next_state = None
        result = None

        # Break to command mode, then set next state to command mode
        if self._send_break(Command.BREAK):
            self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Initialized, in command mode")
            next_state = State.COMMAND_MODE

        return (next_state, result)

    def _handler_reset(self, *args, **kwargs):
        """Handle reset condition for all states.
        
        @param params Parameters to pass to the state
        @retval return (next state, result)
        """
        next_state = None
        result = None
        if self._send_break(Command.RESET):
            self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Reset!")
            next_state = State.AUTOSAMPLE_MODE

        return (next_state, result)

    def _handler_autosample_break(self, *args, **kwargs):
        """Handle State.AUTOSAMPLE_MODE Event.BREAK
        
        @param params Parameters to pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For hardware error
        """
        next_state = None
        result = None

        if self._send_break(Command.BREAK):
            self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Leaving auto sample!")
            next_state = State.COMMAND_MODE
        else:
            self.announce_to_driver(
                DriverAnnouncement.ERROR,
                error_code=InstErrorCode.HARDWARE_ERROR,
                msg="Could not break from autosample!",
            )
            raise InstrumentProtocolException(InstErrorCode.HARDWARE_ERROR)

        return (next_state, result)

    def _handler_autosample_stop(self, *args, **kwargs):
        """Handle State.AUTOSAMPLE_MODE Event.STOP
        
        @param params Parameters to pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For hardware error
        """
        next_state = None
        result = None

        if self._send_break(Command.STOP):
            self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Leaving auto sample!")
            next_state = State.POLL_MODE
        else:
            self.announce_to_driver(
                DriverAnnouncement.ERROR, error_code=InstErrorCode.HARDWARE_ERROR, msg="Could not stop autosample!"
            )
            raise InstrumentProtocolException(InstErrorCode.HARDWARE_ERROR)

        return (next_state, result)

    def _handler_autosample_command(self, *args, **kwargs):
        """Handle State.AUTOSAMPLE_MODE Event.COMMAND transition
        
        @param params Dict with "command" enum and "params" of the parameters to
        pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For invalid parameter
        """
        next_state = None
        result = None

        cmd = kwargs.get(KwargsKey.COMMAND, None)

        if cmd == Command.BREAK:
            result = self._fsm.on_event(Event.BREAK, *args, **kwargs)
        elif cmd == Command.STOP:
            result = self._fsm.on_event(Event.STOP, *args, **kwargs)
        elif cmd == Command.RESET:
            result = self._fsm.on_event(Event.RESET, *args, **kwargs)
        else:
            raise InstrumentProtocolException(InstErrorCode.INVALID_COMMAND)

        mi_logger.debug("next: %s, result: %s", next_state, result)
        return (next_state, result)

    def _handler_command_command(self, *args, **kwargs):
        """Handle State.COMMAND_MODE Event.COMMAND transition
        
        @param params Dict with "command" enum and "params" of the parameters to
        pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For invalid parameter
        """
        next_state = None
        result = None
        cmd = kwargs.get(KwargsKey.COMMAND, None)

        if cmd == Command.EXIT:
            result = self._do_cmd_no_resp(Command.EXIT, None)
            if result:
                self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Starting auto sample")
                next_state = State.AUTOSAMPLE_MODE

        elif cmd == Command.EXIT_AND_RESET:
            result = self._do_cmd_no_resp(Command.EXIT_AND_RESET, None)
            if result:
                self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Starting auto sample")
                next_state = State.AUTOSAMPLE_MODE

        elif cmd == Command.SAVE:
            result = self._do_cmd_no_resp(Command.SAVE, None)

        elif cmd == Command.POLL:
            try:
                kwargs.update({KwargsKey.COMMAND: Command.EXIT})
                result = self._fsm.on_event(Event.COMMAND, *args, **kwargs)
                result = self._fsm.on_event(Event.STOP, *args, **kwargs)
                result = self._fsm.on_event(Event.SAMPLE, *args, **kwargs)
                # result should have data, right?
                mi_logger.debug("Polled sample: %s", result)
                result = self._fsm.on_event(Event.AUTOSAMPLE, *args, **kwargs)
                result = self._fsm.on_event(Event.BREAK, *args, **kwargs)
            except (InstrumentTimeoutException, InstrumentProtocolException) as e:
                if self._fsm.current_state == State.AUTOSAMPLE_MODE:
                    result = self._fsm.on_event(Event.BREAK, *args, **kwargs)
                elif self._fsm.current_state == State.POLL_MODE:
                    result = self._fsm.on_event(Event.AUTOSAMPLE, *args, **kwargs)
                    result = self._fsm.on_event(Event.BREAK, *args, **kwargs)

        else:
            raise InstrumentProtocolException(InstErrorCode.INVALID_COMMAND)

        mi_logger.debug("next: %s, result: %s", next_state, result)
        return (next_state, result)

    def _handler_command_get(self, params=None, *args, **kwargs):
        """Handle getting data from command mode
         
        @param params List of the parameters to pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For invalid parameter
        """
        next_state = None
        result = None
        result_vals = {}

        if (params == None) or (not isinstance(params, list)):
            raise InstrumentProtocolException(InstErrorCode.INVALID_PARAMETER)

        for param in params:
            if not Parameter.has(param):
                raise InstrumentProtocolException(InstErrorCode.INVALID_PARAMETER)
                break
            result_vals[param] = self._do_cmd_resp(Command.GET, param)
        result = result_vals

        mi_logger.debug("next: %s, result: %s", next_state, result)
        return (next_state, result)

    def _handler_command_set(self, params, *args, **kwargs):
        """Handle setting data from command mode
         
        @param params Dict of the parameters and values to pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For invalid parameter
        """
        next_state = None
        result = None
        result_vals = {}

        if (params == None) or (not isinstance(params, dict)):
            raise InstrumentProtocolException(InstErrorCode.INVALID_PARAMETER)
        name_values = params
        for key in name_values.keys():
            if not Parameter.has(key):
                raise InstrumentProtocolException(InstErrorCode.INVALID_PARAMETER)
                break
            result_vals[key] = self._do_cmd_resp(Command.SET, key, name_values[key])
        """@todo raise a parameter error if there was a bad value"""
        result = result_vals

        mi_logger.debug("next: %s, result: %s", next_state, result)
        return (next_state, result)

    def _handler_poll_sample(self, *args, **kwargs):
        """Handle State.POLL_MODE Event.SAMPLE
        
        @retval return (next state, result)
        @throw InstrumentProtocolException For invalid command
        """
        next_state = None
        result = None

        result = self._do_cmd_resp(Command.SAMPLE, None)

        self.announce_to_driver(DriverAnnouncement.DATA_RECEIVED, msg=result)
        return (next_state, result)

    def _handler_poll_autosample(self, *args, **kwargs):
        """Handle State.POLL_MODE Event.AUTOSAMPLE
        
        @retval return (success/fail code, next state, result)
        """
        next_state = None
        result = None

        if self._do_cmd_no_resp(Command.AUTOSAMPLE, None):
            self.announce_to_driver(DriverAnnouncement.STATE_CHANGE, msg="Starting auto sample")
            next_state = State.AUTOSAMPLE_MODE

        return (next_state, result)

    def _handler_poll_command(self, *args, **kwargs):
        """Handle State.POLL_MODE Event.COMMAND transition
        
        @param params Dict with "command" enum and "params" of the parameters to
        pass to the state
        @retval return (next state, result)
        @throw InstrumentProtocolException For invalid command
        """
        next_state = None
        result = None
        result_vals = {}

        cmd = kwargs.get(KwargsKey.COMMAND, None)

        if cmd == Command.AUTOSAMPLE:
            result = self._fsm.on_event(Event.AUTOSAMPLE, *args, **kwargs)
        elif cmd == Command.RESET:
            result = self._fsm.on_event(Event.RESET, *args, **kwargs)
        elif cmd == Command.POLL:
            result = self._fsm.on_event(Event.SAMPLE, *args, **kwargs)
        else:
            raise InstrumentProtocolException(InstErrorCode.INVALID_COMMAND)

        mi_logger.debug("next: %s, result: %s", next_state, result)
        return (next_state, result)

    ###################################################################
    # Builders
    ###################################################################
    def _build_set_command(self, cmd, param, value):
        """
        Build a command that is ready to send out to the instrument. Checks for
        valid parameter name, only handles one value at a time.
        
        @param cmd The command...in this case, Command.SET
        @param param The name of the parameter to set. From Parameter enum
        @param value The value to set for that parameter
        @retval Returns string ready for sending to instrument
        """
        # Check to make sure all parameters are valid up front
        assert Parameter.has(param)
        assert cmd == Command.SET
        return "%s %s %s%s" % (Command.SET, param, value, self.eoln)

    def _build_param_fetch_command(self, cmd, param):
        """
        Build a command to fetch the desired argument.
        
        @param cmd The command being used (Command.GET in this case)
        @param param The name of the parameter to fetch
        @retval Returns string ready for sending to instrument
        """
        assert Parameter.has(param)
        return "%s %s%s" % (Command.GET, param, self.eoln)

    def _build_exec_command(self, cmd, param):
        """
        Builder for simple commands

        @param cmd The command being used (Command.GET in this case)
        @param param The name of the parameter to fetch
        @retval Returns string ready for sending to instrument        
        """
        assert param == None
        return "%s%s" % (cmd, self.eoln)

    def _build_control_command(self, cmd, param):
        """ Send a quick control char command
        
        @param cmd The control character to send
        @param param Unused parameters
        @retval The string wit the complete command (1 char)
        """
        return cmd

    ##################################################################
    # Response parsers
    ##################################################################
    def _parse_set_response(self, response, prompt):
        """Determine if a set was successful or not
        
        @param response What was sent back from the command that was sent
        @param prompt The prompt that was returned from the device
        """
        mi_logger.debug("Parsing SET response of %s with prompt %s", response, prompt)
        if (prompt != Prompt.COMMAND) or (response == Error.INVALID_COMMAND):
            return InstErrorCode.SET_DEVICE_ERR

    def _parse_get_response(self, response, prompt):
        """ Parse the response from the instrument for a couple of different
        query responses.
        
        @param response The response string from the instrument
        @param prompt The prompt received from the instrument
        @retval return The numerical value of the parameter in the known units
        """
        pass

    ###################################################################
    # Helpers
    ###################################################################
    def _wakeup(self, timeout):
        """There is no wakeup sequence for this instrument"""
        pass

    def _send_break(self, break_char, timeout=30):
        """Break out of autosample mode.
        
        Issue the proper sequence of stuff to get the device out of autosample
        mode. The character used will result in a different end state. Ctrl-S
        goes to poll mode, Ctrl-C goes to command mode. Ctrl-R resets. 
        @param break_char The character to send to get out of autosample.
        Should be Event.STOP, Event.BREAK, or Event.RESET.
        @retval return True for success, Error for failure
        @throw InstrumentTimeoutException
        @throw InstrumentProtocolException
        """
        if not ((break_char == Command.BREAK) or (break_char == Command.STOP) or (break_char == Command.RESET)):
            return False

        mi_logger.debug("Sending break char %s", break_char)
        # do the magic sequence of sending lots of characters really fast
        starttime = time.time()
        while True:
            self._do_cmd_no_resp(break_char, None)
            (prompt, result) = self._get_response(timeout)
            mi_logger.debug("Got prompt %s when trying to break", prompt)
            if prompt:
                return True
            else:
                if time.time() > starttime + timeout:
                    raise InstrumentTimeoutException(InstErrorCode.TIMEOUT)

        # catch all
        return False

    def _got_data(self, data):
        """ The comms object fires this when data is received
        
        @param data The chunk of data that was received
        """
        mi_logger.debug("*** Data received: %s, promptbuf: %s", data, self._promptbuf)
        CommandResponseInstrumentProtocol._got_data(self, data)

        # Only keep the latest characters in the prompt buffer.
        # if len(self._promptbuf)>7:
        #    self._promptbuf = self._promptbuf[-7:]

        # If we are streaming, process the line buffer for samples.
        if self._fsm.get_current_state() == State.AUTOSAMPLE_MODE:
            if self.eoln in self._linebuf:
                lines = self._linebuf.split(self.eoln)
                self._linebuf = lines[-1]
                for line in lines:
                    self.announce_to_driver(DriverAnnouncement.DATA_RECEIVED, msg=line)