def get_capabilities(self, resource_id='', current_state=True):
        """Introspect for agent capabilities.
        @param resource_id The id of the resource agent.
        @param current_state Flag indicating to return capabilities for current
        state only (default True).
        @retval List of AgentCapabilities objects.

        @param resource_id    str
        @param current_state    bool
        @retval capability_list    list
        """

        res_type = self._get_resource_type(resource_id)
        if self._has_agent(res_type):
            rac = ResourceAgentClient(resource_id=resource_id)
            return rac.get_capabilities(resource_id=resource_id,
                                        current_state=current_state)

        res_interface = self._get_type_interface(res_type)

        cap_list = []
        for param in res_interface['params'].keys():
            cap = AgentCapability(name=param, cap_type=CapabilityType.RES_PAR)
            cap_list.append(cap)

        for cmd in res_interface['commands'].keys():
            cap = AgentCapability(name=cmd, cap_type=CapabilityType.RES_CMD)
            cap_list.append(cap)

        return cap_list
    def get_capabilities(self, resource_id='', current_state=True):
        """Introspect for agent capabilities.
        @param resource_id The id of the resource agent.
        @param current_state Flag indicating to return capabilities for current
        state only (default True).
        @retval List of AgentCapabilities objects.

        @param resource_id    str
        @param current_state    bool
        @retval capability_list    list
        """

        res_type = self._get_resource_type(resource_id)
        if self._has_agent(res_type):
            rac = ResourceAgentClient(resource_id=resource_id)
            return rac.get_capabilities(resource_id=resource_id, current_state=current_state)

        res_interface = self._get_type_interface(res_type)

        cap_list = []
        for param in res_interface['params'].keys():
            cap = AgentCapability(name=param, cap_type=CapabilityType.RES_PAR)
            cap_list.append(cap)

        for cmd in res_interface['commands'].keys():
            cap = AgentCapability(name=cmd, cap_type=CapabilityType.RES_CMD)
            cap_list.append(cap)

        return cap_list
    def get_capabilities(self, resource_id='', current_state=True):
        """Introspect for agent capabilities.
        """
        res_type = self._get_resource_type(resource_id)
        if self._has_agent(res_type):
            rac = ResourceAgentClient(resource_id=resource_id)
            return rac.get_capabilities(resource_id=resource_id, current_state=current_state)

        res_interface = self._get_type_interface(res_type)

        cap_list = []
        for param in res_interface['params'].keys():
            cap = AgentCapability(name=param, cap_type=CapabilityType.RES_PAR)
            cap_list.append(cap)

        for cmd in res_interface['commands'].keys():
            cap = AgentCapability(name=cmd, cap_type=CapabilityType.RES_CMD)
            cap_list.append(cap)

        return cap_list
Beispiel #4
0
    def get_capabilities(self, resource_id='', current_state=True):
        """Introspect for agent capabilities.
        """
        res_type = self._get_resource_type(resource_id)
        if self._has_agent(res_type):
            rac = ResourceAgentClient(resource_id=resource_id)
            return rac.get_capabilities(resource_id=resource_id,
                                        current_state=current_state)

        res_interface = self._get_type_interface(res_type)

        cap_list = []
        for param in res_interface['params'].keys():
            cap = AgentCapability(name=param, cap_type=CapabilityType.RES_PAR)
            cap_list.append(cap)

        for cmd in res_interface['commands'].keys():
            cap = AgentCapability(name=cmd, cap_type=CapabilityType.RES_CMD)
            cap_list.append(cap)

        return cap_list
Beispiel #5
0
class RunInstrument(MiIntTestCase):
    """
    Main class for communicating with an instrument
    """
    def __init__(self, monitor=False, subscriber=False):
        self.driver_make = None
        self.driver_model = None
        self.driver_name = None
        self.driver_class = DRIVER_CLASS
        self.ip_address = None
        self.data_port = None
        self.command_port = None
        self.driver_version = None
        self._pagent = None
        self.monitor_window = monitor
        self.subcriber_window = subscriber
        self.stream_config = {}

        self._cleanups = []

    def _initialize(self):
        """
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """

        try:
            """
            Get the information for the driver.  This can be read from the yml
            files; the user can run switch_driver to change the current driver.
            """
            self.fetch_metadata()
            self.fetch_driver_class()
            self.fetch_comm_config()

            if not exists(PIPE_PATH):
                mkfifo(PIPE_PATH)

            if not exists(self.metadata.driver_dir()):
                raise DriverDoesNotExist(
                    "%s/%s/$%s" %
                    (self.metadata.driver_make, self.metadata.driver_model,
                     self.driver_name))

            driver_module = DRIVER_MODULE_ROOT + self.metadata.driver_make + '.' + self.metadata.driver_model + '.' + self.metadata.driver_name + DRIVER_MODULE_LEAF

            log.info('driver module: %s', driver_module)
            log.info('driver class: %s', self.driver_class)
            log.info('device address: %s', self.ip_address)
            log.info('device data port: %s', self.data_port)
            log.info('device command port: %s', self.command_port)
            log.info('log delimiter: %s', DELIM)
            log.info('work dir: %s', WORK_DIR)

            DVR_CONFIG.update({
                'dvr_mod': driver_module,
                'dvr_cls': self.driver_class
            })
            """
            self._support = DriverIntegrationTestSupport(driver_module,
                                                         self.driver_class,
                                                         self.ip_address,
                                                         self.data_port,
                                                         DELIM,
                                                         WORK_DIR)
            """

            # Start port agent, add stop to cleanup (not sure if that's
            # necessary yet).
            print(
                "------------------>>>> Starting Port Agent <<<<------------------"
            )
            self.start_pagent()

            # Start a monitor window if specified.
            if self.monitor_window:
                self.monitor_file = self._pagent.port_agent.logfname
                strXterm = "xterm -T InstrumentMonitor -sb -rightbar"
                #pOpenString = "xterm -T InstrumentMonitor -e tail -f " + self.monitor_file
                pOpenString = strXterm + " -e tail -f " + self.monitor_file

                x = subprocess.Popen(pOpenString, shell=True)
            """
            DHE: Added self._cleanups to make base classes happy
            """
            self.addCleanup(self.stop_pagent)

            # Start container.
            print(
                "------------------>>>> Starting Capability Container <<<<------------------"
            )
            self._start_container()

            # Bring up services in a deploy file (no need to message)
            print(
                "------------------>>>> Starting Deploy Services <<<<------------------"
            )
            self.container.start_rel_from_url('res/deploy/r2deploy.yml')

            # Setup stream config.
            self._build_stream_config()

            # Create agent config.
            agent_config = {
                'driver_config': DVR_CONFIG,
                'stream_config': self.stream_config,
                'agent': {
                    'resource_id': IA_RESOURCE_ID
                },
                'test_mode': True
            }

            # Start instrument agent.
            self._ia_pid = None
            print(
                "------------------>>>> Starting Instrument Agent <<<<------------------"
            )
            container_client = ContainerAgentClient(node=self.container.node,
                                                    name=self.container.name)
            self._ia_pid = container_client.spawn_process(name=IA_NAME,
                                                          module=IA_MOD,
                                                          cls=IA_CLS,
                                                          config=agent_config)
            log.info('Agent pid=%s.', str(self._ia_pid))

            # Start a resource agent client to talk with the instrument agent.
            self._ia_client = None
            self._ia_client = ResourceAgentClient(IA_RESOURCE_ID,
                                                  process=FakeProcess())
            log.info('Got ia client %s.', str(self._ia_client))

            if self.subcriber_window:
                self._start_data_subscribers(6)
                #self.addCleanup(self._stop_data_subscribers)

        except:
            log.error("initialize(): Exception occurred; shutting down.",
                      exc_info=True)
            return False

        else:
            return True

    ###############################################################################
    # Port agent helpers.
    ###############################################################################

    def start_pagent(self):
        """
        Construct and start the port agent.
        @retval port Port that was used for connection to agent
        """
        # Create port agent object.
        comm_config = self.comm_config

        config = {
            'device_addr': comm_config.device_addr,
            'device_port': comm_config.device_port,
            'command_port': comm_config.command_port,
            'data_port': comm_config.data_port,
            'process_type': PortAgentProcessType.UNIX,
            'log_level': 5,
        }

        self._pagent = PortAgentProcess.launch_process(config,
                                                       timeout=60,
                                                       test_mode=True)
        pid = self._pagent.get_pid()
        port = self._pagent.get_data_port()
        cmd_port = self._pagent.get_command_port()

        log.info('Started port agent pid %d listening at port %d', pid, port)

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

        return port

    def stop_pagent(self):
        """
        Stop the port agent.
        """
        if self._pagent:
            pid = self._pagent.get_pid()
            if pid:
                log.info('Stopping pagent pid %i', pid)
                self._pagent.stop()
            else:
                log.info('No port agent running.')

    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient()

        # Create streams and subscriptions for each stream named in driver.
        self.stream_config = {}

        streams = {
            'parsed': 'ctd_parsed_param_dict',
            'raw': 'ctd_raw_param_dict'
        }

        for (stream_name, param_dict_name) in streams.iteritems():
            pd_id = dataset_management.read_parameter_dictionary_by_name(
                DEFAULT_PARAM_DICT, id_only=True)
            if (not pd_id):
                log.error("No pd_id found for param_dict '%s'" %
                          DEFAULT_PARAM_DICT)

            stream_def_id = pubsub_client.create_stream_definition(
                name=stream_name, parameter_dictionary_id=pd_id)
            pd = None
            stream_id, stream_route = pubsub_client.create_stream(
                name=stream_name,
                exchange_point='science_data',
                stream_definition_id=stream_def_id)

            stream_config = dict(stream_route=stream_route,
                                 routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 stream_definition_ref=stream_def_id,
                                 parameter_dictionary=pd)

            self.stream_config[stream_name] = stream_config

    def _start_data_subscribers(self, count):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        #self._async_data_result = AsyncResult()

        strXterm = "xterm -T InstrumentScienceData -sb -rightbar "
        pOpenString = strXterm + " -e tail -f " + PIPE_PATH
        subprocess.Popen([
            'xterm', '-T', 'InstrumentScienceData', '-e', 'tail', '-f',
            PIPE_PATH
        ])

        #subprocess.Popen(pOpenString)

        #self.pipeData = open(PIPE_PATH, "w", 1)

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            print 'Received message on ' + str(stream_id) + ' (' + str(
                stream_route.exchange_point) + ',' + str(
                    stream_route.routing_key) + ')'
            log.info('Received message on %s (%s,%s)', stream_id,
                     stream_route.exchange_point, stream_route.routing_key)

            self.pipeData = open(PIPE_PATH, "w", 1)
            self.pipeData.write(str(message))
            self.pipeData.flush()
            self.pipeData.close()

            self._samples_received.append(message)
            #if len(self._samples_received) == count:
            #self._async_data_result.set()

        for (stream_name, stream_config) in self._stream_config.iteritems():

            stream_id = stream_config['stream_id']

            # Create subscriptions for each stream.

            exchange_name = '%s_queue' % stream_name
            self._purge_queue(exchange_name)
            sub = StandaloneStreamSubscriber(exchange_name, recv_data)
            sub.start()
            self._data_subscribers.append(sub)
            print 'stream_id: %s' % stream_id
            sub_id = pubsub_client.create_subscription(name=exchange_name,
                                                       stream_ids=[stream_id])
            pubsub_client.activate_subscription(sub_id)
            sub.subscription_id = sub_id  # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()

    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber, 'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(
                        subscriber.subscription_id)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id)
            subscriber.stop()

    def bring_instrument_active(self):
        """
        @brief Bring the agent up to COMMAND state, 
        """
        """
        DHE: Don't have an event subscriber yet

        # Set up a subscriber to collect error events.
        #self._start_event_subscriber('ResourceAgentResourceStateEvent', 6)
        #self.addCleanup(self._stop_event_subscriber)
        """

        try:
            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)

            cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
            retval = self._ia_client.execute_agent(cmd)

            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)

            res_state = self._ia_client.get_resource_state()
            print "DriverState: " + str(res_state)

            cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
            retval = self._ia_client.execute_agent(cmd)

            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)

            res_state = self._ia_client.get_resource_state()
            print "DriverState: " + str(res_state)
            """
            If the agent is in STREAMING state, it will not accept the run
            command.
            """
            if state != ResourceAgentState.STREAMING:
                cmd = AgentCommand(command=ResourceAgentEvent.RUN)
                retval = self._ia_client.execute_agent(cmd)

            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)

            res_state = self._ia_client.get_resource_state()
            print "DriverState: " + str(res_state)

        except:
            log.error(
                "bring_instrument_active(): Exception occurred; shutting down.",
                exc_info=True)
            return False

        else:
            return True
        """
        DHE: Don't have an event subscriber yet so we've received no events.
        self._async_event_result.get(timeout=2)
        print "Received: " + str(len(self._events_received)) + " events."
        """

    ###############################################################################
    # RunInstrument helpers.
    ###############################################################################
    def get_capabilities(self):
        """
        @brief Get exposed capabilities in current state.
        """

        retval = self._ia_client.get_capabilities()

        # Validate capabilities for state UNINITIALIZED.
        self.agt_cmds = [
            x.name for x in retval if x.cap_type == CapabilityType.AGT_CMD
        ]
        self.agt_pars = [
            x.name for x in retval if x.cap_type == CapabilityType.AGT_PAR
        ]
        self.res_cmds = [
            x.name for x in retval if x.cap_type == CapabilityType.RES_CMD
        ]
        self.res_pars = [
            x.name for x in retval if x.cap_type == CapabilityType.RES_PAR
        ]

        print "\n------------------>>>> Current Capabilities <<<<------------------"
        print "Agent Commands: " + str(self.agt_cmds)
        #print "Agent Parameters: " + str(self.agt_pars)
        print "Resource Commands: " + str(self.res_cmds)
        #print "Resource Parameters: " + str(self.res_pars)

    def send_agent_command(self, command):
        """
        @brief Send a command to the agent. 
        """

        DA_WAIT_PERIOD = 60
        waiting = False
        print "Input command: " + str(command)
        if command == 'RESOURCE_AGENT_EVENT_GO_DIRECT_ACCESS':
            cmd = AgentCommand(command=command,
                               kwargs={
                                   'session_type': DirectAccessTypes.telnet,
                                   'session_timeout': 600,
                                   'inactivity_timeout': 600
                               })
            waiting = True
        else:
            cmd = AgentCommand(command=command)

        retval = self._ia_client.execute_agent(cmd)
        print "Results of command: " + str(retval)
        while waiting:
            print "Waiting " + str(
                DA_WAIT_PERIOD) + " seconds for you to test direct access."
            gevent.sleep(DA_WAIT_PERIOD)
            still_waiting = prompt.text('Still waiting? (y/n)')
            if still_waiting is 'n':
                waiting = False

    def send_driver_command(self, command):
        """
        @brief Send a command to the instrument through the instrument agent.
        First determine whether it's a get or set, which are handled separately.
        """

        if command == DriverEvent.GET:
            self._get_param()
        elif command == DriverEvent.SET:
            self._set_param()
        else:
            print "Input command: " + str(command)
            cmd = AgentCommand(command=command)
            retval = self._ia_client.execute_resource(cmd)
            print "Results of command: " + str(retval)

    def _get_param(self):
        """
        @brief Get a single parameter from the instrument (will be updated to get 
        multiple later).
        """

        _all_params = self._ia_client.get_resource('DRIVER_PARAMETER_ALL')
        print "Parameters you can get are: " + str(_all_params)
        _param_valid = False
        while _param_valid is False:
            _param = prompt.text('\nEnter a single parameter')
            if _param in _all_params:
                _param_valid = True
            else:
                print 'Invalid parameter: ' + _param

        reply = self._ia_client.get_resource([_param])
        print 'Reply is :' + str(reply)

    def _set_param(self):
        """
        @brief Set a single parameter
        """

        _all_params = self._ia_client.get_resource('DRIVER_PARAMETER_ALL')
        print "Parameters you can set are: " + str(_all_params)
        _param_valid = False
        while _param_valid is False:
            _param = prompt.text('\nEnter a single parameter')
            if _param in _all_params:
                _param_valid = True
            else:
                print 'Invalid parameter: ' + _param

        _value = prompt.text('Enter value')
        _value = _value.lower()
        """
        DHE: Need to convert to native types here; can't be string; this is a 
        problem for the UI because we need a way to get the metadata about 
        each param to the UI.
        """
        if _value == 'true':
            _value = True
        elif _value == 'false':
            _value = False

        param_dict = {_param: _value}
        self._ia_client.set_resource(param_dict)

    def fetch_metadata(self):
        """
        @brief collect metadata from the user
        """

        self.metadata = Metadata()
        self.driver_make = self.metadata.driver_make
        self.driver_model = self.metadata.driver_model
        self.driver_name = self.metadata.driver_name

        if not (self.driver_make and self.driver_model and self.driver_name):
            self.driver_make = prompt.text('Driver Make', self.driver_make)
            self.driver_model = prompt.text('Driver Model', self.driver_model)
            self.driver_name = prompt.text('Driver Name', self.driver_name)

        if not (self.driver_class):
            self.driver_class = prompt.text('Driver Class', self.driver_class)

        self.metadata = Metadata(self.driver_make, self.driver_model,
                                 self.driver_name)

    def fetch_comm_config(self):
        """
        @brief collect connection information for the logger from the user
        """

        config_path = "%s/%s" % (self.metadata.driver_dir(),
                                 CommConfig.config_filename())
        self.comm_config = CommConfig.get_config_from_console(config_path)
        self.comm_config.display_config()
        #self.comm_config.get_from_console()
        self.ip_address = self.comm_config.device_addr
        self.data_port = self.comm_config.data_port
        self.command_port = self.comm_config.command_port

        if not (self.ip_address):
            self.ip_address = prompt.text('Instrument IP Address',
                                          self.ip_address)

        if not (self.data_port):
            continuing = True
            while continuing:
                sport = prompt.text('Instrument Port', self.data_port)
                try:
                    self.data_port = int(sport)
                    continuing = False
                except ValueError as e:
                    print "Error converting port to number: " + str(e)
                    print "Please enter a valid port number.\n"

    def fetch_driver_class(self):
        self.driver_class = prompt.text('Driver Class', self.driver_class)

    def get_user_command(self, text='Enter command'):

        command = prompt.text(text)
        return command

    def run(self):
        """
        @brief Run it.
        """

        print(
            "------------------>>>> Starting RunInstrument <<<<------------------"
        )
        """
        initialize; returns True if successful, else False.
        """
        continuing = self._initialize()
        """
        bring_instrument_active; returns True if successful, else False
        """
        if (continuing):
            continuing = self.bring_instrument_active()

        PROMPT = 'Enter command (\'quit\' to exit)'
        text = PROMPT
        while continuing:
            try:
                """
                Get a list of the currently available capabilities
                """
                self.get_capabilities()
                command = self.get_user_command(text)
                text = PROMPT
                if command == 'quit':
                    continuing = False
                elif command in self.agt_cmds:
                    self.send_agent_command(command)
                elif command in self.res_cmds:
                    self.send_driver_command(command)
                else:
                    text = 'Invalid Command: ' + command + '\n' + PROMPT
            except:
                log.error("run(): Exception occurred; shutting down.",
                          exc_info=True)
                continuing = False

        self.stop_pagent()
        print(
            "------------------>>>> Stopping RunInstrument <<<<------------------"
        )
class TestInstrumentAgent(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """ 
    def setUp(self):
        """
        Initialize test members.
        Start port agent.
        Start container and client.
        Start streams and subscribers.
        Start agent, client.
        """
                
        self._support = DriverIntegrationTestSupport(DRV_MOD,
                                                     DRV_CLS,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DELIM,
                                                     WORK_DIR)
        # Start port agent, add stop to cleanup.
        self._pagent = None        
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)    
        
        # Start container.
        self._start_container()
        
        # Bring up services in a deploy file (no need to message)
        self.container.start_rel_from_url('res/deploy/r2dm.yml')

        # Start data suscribers, add stop to cleanup.
        # Define stream_config.
        self._no_samples = None
        self._async_data_result = AsyncResult()
        self._data_greenlets = []
        self._stream_config = {}
        self._samples_received = []
        self._data_subscribers = []
        self._start_data_subscribers()
        self.addCleanup(self._stop_data_subscribers)

        # Start event subscribers, add stop to cleanup.
        self._no_events = None
        self._async_event_result = AsyncResult()
        self._events_received = []
        self._event_subscribers = []
        self._start_event_subscribers()
        self.addCleanup(self._stop_event_subscribers)
                
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : self._stream_config,
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True
        }
        
        # Start instrument agent.
        self._ia_pid = None
        log.debug("TestInstrumentAgent.setup(): starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
                                                name=self.container.name)
        self._ia_pid = container_client.spawn_process(name=IA_NAME,
                                                      module=IA_MOD, 
                                                      cls=IA_CLS, 
                                                      config=agent_config)      
        log.info('Agent pid=%s.', str(self._ia_pid))
        
        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = None
        self._ia_client = ResourceAgentClient(IA_RESOURCE_ID, process=FakeProcess())
        log.info('Got ia client %s.', str(self._ia_client))        
        
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """
        port = self._support.start_pagent()
        
        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr' : 'localhost',
            'port' : port
        }
        
    def _start_data_subscribers(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        # A callback for processing subscribed-to data.
        def consume_data(message, headers):
            log.info('Subscriber received data message: %s.', str(message))
            self._samples_received.append(message)
            if self._no_samples and self._no_samples == len(self._samples_received):
                self._async_data_result.set()
                
        # Create a stream subscriber registrar to create subscribers.
        subscriber_registrar = StreamSubscriberRegistrar(process=self.container,
                                                node=self.container.node)

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}
        self._data_subscribers = []
        for (stream_name, val) in PACKET_CONFIG.iteritems():
            stream_def = ctd_stream_definition(stream_id=None)
            stream_def_id = pubsub_client.create_stream_definition(
                                                    container=stream_def)        
            stream_id = pubsub_client.create_stream(
                        name=stream_name,
                        stream_definition_id=stream_def_id,
                        original=True,
                        encoding='ION R2')
            self._stream_config[stream_name] = stream_id
            
            # Create subscriptions for each stream.
            exchange_name = '%s_queue' % stream_name
            sub = subscriber_registrar.create_subscriber(exchange_name=exchange_name,
                                                         callback=consume_data)
            self._listen(sub)
            self._data_subscribers.append(sub)
            query = StreamQuery(stream_ids=[stream_id])
            sub_id = pubsub_client.create_subscription(\
                                query=query, exchange_name=exchange_name)
            pubsub_client.activate_subscription(sub_id)
            
    def _listen(self, sub):
        """
        Pass in a subscriber here, this will make it listen in a background greenlet.
        """
        gl = spawn(sub.listen)
        self._data_greenlets.append(gl)
        sub._ready_event.wait(timeout=5)
        return gl
                                 
    def _stop_data_subscribers(self):
        """
        Stop the data subscribers on cleanup.
        """
        for sub in self._data_subscribers:
            sub.stop()
        for gl in self._data_greenlets:
            gl.kill()
            
    def _start_event_subscribers(self):
        """
        Create subscribers for agent and driver events.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.', 
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._no_events and self._no_events == len(self._event_received):
                self._async_event_result.set()
                
        event_sub = EventSubscriber(event_type="DeviceEvent", callback=consume_event)
        event_sub.activate()
        self._event_subscribers.append(event_sub)
        
    def _stop_event_subscribers(self):
        """
        Stop event subscribers on cleanup.
        """
        for sub in self._event_subscribers:
            sub.deactivate()
        
    def assertSampleDict(self, val):
        """
        Verify the value is a sample dictionary for the sbe37.
        """
        #{'p': [-6.945], 'c': [0.08707], 't': [20.002], 'time': [1333752198.450622]}        
        self.assertTrue(isinstance(val, dict))
        self.assertTrue(val.has_key('c'))
        self.assertTrue(val.has_key('t'))
        self.assertTrue(val.has_key('p'))
        self.assertTrue(val.has_key('time'))
        c = val['c'][0]
        t = val['t'][0]
        p = val['p'][0]
        time = val['time'][0]
    
        self.assertTrue(isinstance(c, float))
        self.assertTrue(isinstance(t, float))
        self.assertTrue(isinstance(p, float))
        self.assertTrue(isinstance(time, float))

    def assertParamDict(self, pd, all_params=False):
        """
        Verify all device parameters exist and are correct type.
        """
        if all_params:
            self.assertEqual(set(pd.keys()), set(PARAMS.keys()))
            for (key, type_val) in PARAMS.iteritems():
                if type_val == list or type_val == tuple:
                    self.assertTrue(isinstance(pd[key], (list, tuple)))
                else:
                    self.assertTrue(isinstance(pd[key], type_val))
                    
        else:
            for (key, val) in pd.iteritems():
                self.assertTrue(PARAMS.has_key(key))
                self.assertTrue(isinstance(val, PARAMS[key]))
    
    def assertParamVals(self, params, correct_params):
        """
        Verify parameters take the correct values.
        """
        self.assertEqual(set(params.keys()), set(correct_params.keys()))
        for (key, val) in params.iteritems():
            correct_val = correct_params[key]
            if isinstance(val, float):
                # Verify to 5% of the larger value.
                max_val = max(abs(val), abs(correct_val))
                self.assertAlmostEqual(val, correct_val, delta=max_val*.01)

            elif isinstance(val, (list, tuple)):
                # list of tuple.
                self.assertEqual(list(val), list(correct_val))
            
            else:
                # int, bool, str.
                self.assertEqual(val, correct_val)

    def test_initialize(self):
        """
        Test agent initialize command. This causes creation of
        driver process and transition to inactive.
        """

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
                
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
    def test_states(self):
        """
        Test agent state transitions.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)
                
        cmd = AgentCommand(command='pause')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STOPPED)
 
        cmd = AgentCommand(command='resume')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)
 
        cmd = AgentCommand(command='clear')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='pause')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STOPPED)

        cmd = AgentCommand(command='clear')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_get_set(self):
        """
        Test instrument driver get and set interface.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Retrieve all resource parameters.                
        reply = self._ia_client.get_param(SBE37Parameter.ALL)
        self.assertParamDict(reply, True)
        orig_config = reply
        
        # Retrieve a subset of resource parameters.
        params = [
            SBE37Parameter.TA0,
            SBE37Parameter.INTERVAL,
            SBE37Parameter.STORETIME
        ]
        reply = self._ia_client.get_param(params)
        self.assertParamDict(reply)
        orig_params = reply

        # Set a subset of resource parameters.
        new_params = {
            SBE37Parameter.TA0 : (orig_params[SBE37Parameter.TA0] * 2),
            SBE37Parameter.INTERVAL : (orig_params[SBE37Parameter.INTERVAL] + 1),
            SBE37Parameter.STORETIME : (not orig_params[SBE37Parameter.STORETIME])
        }
        self._ia_client.set_param(new_params)
        check_new_params = self._ia_client.get_param(params)
        self.assertParamVals(check_new_params, new_params)
        
        # Reset the parameters back to their original values.
        self._ia_client.set_param(orig_params)
        reply = self._ia_client.get_param(SBE37Parameter.ALL)
        reply.pop(SBE37Parameter.SAMPLENUM)
        orig_config.pop(SBE37Parameter.SAMPLENUM)
        self.assertParamVals(reply, orig_config)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_poll(self):
        """
        Test observatory polling function.
        """
        
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)
        
        # Lets get 3 samples.
        self._no_samples = 3
        
        # Poll for a few samples.
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)        

        # Assert we got 3 samples.
        self._async_data_result.get(timeout=10)
        self.assertTrue(len(self._samples_received)==3)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)        
        
    def test_autosample(self):
        """
        Test instrument driver execute interface to start and stop streaming
        mode.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Make sure the sampling rate and transmission are sane.                
        params = {
            SBE37Parameter.NAVG : 1,
            SBE37Parameter.INTERVAL : 5,
            SBE37Parameter.TXREALTIME : True
        }
        self._ia_client.set_param(params)

        self._no_samples = 2

        # Begin streaming.                
        cmd = AgentCommand(command='go_streaming')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STREAMING)
 
        # Wait for some samples to roll in.
        gevent.sleep(15)
 
        # Halt streaming.
        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Assert we got some samples.
        self._async_data_result.get(timeout=10)
        self.assertTrue(len(self._samples_received)>=2)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_capabilities(self):
        """
        Test the ability to retrieve agent and resource parameter and command
        capabilities.
        """
        acmds = self._ia_client.get_capabilities(['AGT_CMD'])
        acmds = [item[1] for item in acmds]
        self.assertEqual(acmds, AGT_CMDS)
        apars = self._ia_client.get_capabilities(['AGT_PAR'])
        apars = [item[1] for item in apars]
        
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)
        
        rcmds = self._ia_client.get_capabilities(['RES_CMD'])
        rcmds = [item[1] for item in rcmds]
        self.assertEqual(rcmds, CMDS)
        
        rpars = self._ia_client.get_capabilities(['RES_PAR'])
        rpars = [item[1] for item in rpars]
        self.assertEqual(rpars, SBE37Parameter.list())
                
        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
    @unittest.skip('Never written')
    def test_errors(self):
        """
        Test illegal behavior and replies.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        # Can't go active in unitialized state.
        # Status 660 is state error.
        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        log.info('GO ACTIVE CMD %s',str(retval))
        self.assertEquals(retval.status, 660)
        
        # Can't command driver in this state.
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertEqual(reply.status, 660)
        
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # OK, I can do this now.        
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)

        # 404 unknown agent command.
        cmd = AgentCommand(command='kiss_edward')
        retval = self._ia_client.execute_agent(cmd)
        self.assertEquals(retval.status, 404)
        
        # 670 unknown driver command.
        cmd = AgentCommand(command='acquire_sample_please')
        retval = self._ia_client.execute(cmd)
        self.assertEqual(retval.status, 670)

        # 630 Parameter error.
        with self.assertRaises(InstParameterError):
            reply = self._ia_client.get_param('bogus bogus')

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
        
        
    @unittest.skip('Direct access test to be finished by adding the telnet client, manual for now.')
    def test_direct_access(self):
        """
        Test agent direct_access command. This causes creation of
        driver process and transition to direct access.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='go_direct_access', 
                           #kwargs={'session_type':DirectAccessTypes.telnet,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                                   'session_timeout':600,
                                   'inactivity_timeout':600})
        retval = self._ia_client.execute_agent(cmd)
        print("go_direct_access retval=" + str(retval))       
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.DIRECT_ACCESS)

        # sleep to let tester run telnet client manually
        print "test sleeping to run telnet client"
        time.sleep(60)

        # Halt DA.
        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
class TestIntExternalObservatoryAgentService(IonIntegrationTestCase):

    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url(rel_url='res/deploy/r2eoi.yml')

#        self.eoas_cli = ExternalObservatoryAgentServiceClient()
#        self.rr_cli = ResourceRegistryServiceClient()
        self.dams_cli = DataAcquisitionManagementServiceClient()
        self.dpms_cli = DataProductManagementServiceClient()

        self._setup_ncom()
        self._setup_hfr()

#        eoas_proc = self.container.proc_manager.procs_by_name['external_data_agent_management']
#        log.debug("Got EOAS Process: %s" % eoas_proc)
        self._ncom_agt_cli = ResourceAgentClient(resource_id=self.ncom_ds_id, name='external_observatory_agent', process=FakeProcess())
        log.debug("Got a ResourceAgentClient: res_id=%s" % self._ncom_agt_cli.resource_id)

        self._hfr_agt_cli = ResourceAgentClient(resource_id=self.hfr_ds_id, name='external_observatory_agent', process=FakeProcess())
        log.debug("Got a ResourceAgentClient: res_id=%s" % self._hfr_agt_cli.resource_id)

    def _setup_ncom(self):
        # TODO: some or all of this (or some variation) should move to DAMS

        # Create and register the necessary resources/objects

        eda = ExternalDatasetAgent()
        eda_id = self.dams_cli.create_external_dataset_agent(eda)

        eda_inst = ExternalDatasetAgentInstance()
        eda_inst_id = self.dams_cli.create_external_dataset_agent_instance(eda_inst, external_dataset_agent_id=eda_id)


        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(), contact=ContactInformation())
#        dprov.institution.name = "OOI CGSN"
        dprov.contact.name = "Robert Weller"
        dprov.contact.email = "*****@*****.**"

        # Create DataSource
        dsrc = DataSource(protocol_type="DAP", institution=Institution(), contact=ContactInformation())
        #        dsrc.connection_params["base_data_url"] = "http://ooi.whoi.edu/thredds/dodsC/"
        dsrc.connection_params["base_data_url"] = ""
        dsrc.contact.name="Rich Signell"
        dsrc.contact.email = "*****@*****.**"

        # Create ExternalDataset
        dset = ExternalDataset(name="test", dataset_description=DatasetDescription(), update_description=UpdateDescription(), contact=ContactInformation())

        #        dset.dataset_description.parameters["dataset_path"] = "ooi/AS02CPSM_R_M.nc"
        dset.dataset_description.parameters["dataset_path"] = "test_data/ncom.nc"
        dset.dataset_description.parameters["temporal_dimension"] = "time"
        dset.dataset_description.parameters["zonal_dimension"] = "lon"
        dset.dataset_description.parameters["meridional_dimension"] = "lat"

        # Create DataSourceModel
        dsrc_model = DataSourceModel(name="dap_model")
        dsrc_model.model = "DAP"
        dsrc_model.data_handler_module = "eoi.agent.handler.dap_external_data_handler"
        dsrc_model.data_handler_class = "DapExternalDataHandler"

        ## Run everything through DAMS
        ds_id = self.ncom_ds_id = self.dams_cli.create_external_dataset(external_dataset=dset)
        ext_dprov_id = self.dams_cli.create_external_data_provider(external_data_provider=dprov)
        ext_dsrc_id = self.dams_cli.create_data_source(data_source=dsrc)
        ext_dsrc_model_id = self.dams_cli.create_data_source_model(dsrc_model)

        # Register the ExternalDataset
        dproducer_id = self.dams_cli.register_external_data_set(external_dataset_id=ds_id)

        ## Associate everything
        # Convenience method
#        self.dams_cli.assign_eoi_resources(external_data_provider_id=ext_dprov_id, data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id, external_dataset_id=ds_id, external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)


        # Or using each method
        self.dams_cli.assign_data_source_to_external_data_provider(data_source_id=ext_dsrc_id, external_data_provider_id=ext_dprov_id)
        self.dams_cli.assign_data_source_to_data_model(data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id)
        self.dams_cli.assign_external_dataset_to_data_source(external_dataset_id=ds_id, data_source_id=ext_dsrc_id)
        self.dams_cli.assign_external_dataset_to_agent_instance(external_dataset_id=ds_id, agent_instance_id=eda_inst_id)
#        self.dams_cli.assign_external_dataset_agent_to_data_model(external_data_agent_id=eda_id, data_source_model_id=ext_dsrc_model_id)
#        self.dams_cli.assign_external_data_agent_to_agent_instance(external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Generate the data product and associate it to the ExternalDataset
        dprod = DataProduct(name='ncom_product', description='raw ncom product')
        dproduct_id = self.dpms_cli.create_data_product(data_product=dprod)

        self.dams_cli.assign_data_product(input_resource_id=ds_id, data_product_id=dproduct_id, create_stream=True)

    def _setup_hfr(self):
        # TODO: some or all of this (or some variation) should move to DAMS

        # Create and register the necessary resources/objects

        eda = ExternalDatasetAgent()
        eda_id = self.dams_cli.create_external_dataset_agent(eda)

        eda_inst = ExternalDatasetAgentInstance()
        eda_inst_id = self.dams_cli.create_external_dataset_agent_instance(eda_inst, external_dataset_agent_id=eda_id)

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(), contact=ContactInformation())
#        dprov.institution.name = "HFR UCSD"

        # Create DataSource
        dsrc = DataSource(protocol_type="DAP", institution=Institution(), contact=ContactInformation())
        dsrc.connection_params["base_data_url"] = "http://hfrnet.ucsd.edu:8080/thredds/dodsC/"

        # Create ExternalDataset
        dset = ExternalDataset(name="UCSD HFR", dataset_description=DatasetDescription(), update_description=UpdateDescription(), contact=ContactInformation())
        dset.dataset_description.parameters["dataset_path"] = "HFRNet/USEGC/6km/hourly/RTV"
#        dset.dataset_description.parameters["dataset_path"] = "test_data/hfr.nc"
        dset.dataset_description.parameters["temporal_dimension"] = "time"
        dset.dataset_description.parameters["zonal_dimension"] = "lon"
        dset.dataset_description.parameters["meridional_dimension"] = "lat"

        # Create DataSourceModel
        dsrc_model = DataSourceModel(name="dap_model")
        dsrc_model.model = "DAP"
        dsrc_model.data_handler_module = "eoi.agent.handler.dap_external_data_handler"
        dsrc_model.data_handler_class = "DapExternalDataHandler"

        ## Run everything through DAMS
        ds_id = self.hfr_ds_id = self.dams_cli.create_external_dataset(external_dataset=dset)
        ext_dprov_id = self.dams_cli.create_external_data_provider(external_data_provider=dprov)
        ext_dsrc_id = self.dams_cli.create_data_source(data_source=dsrc)
        ext_dsrc_model_id = self.dams_cli.create_data_source_model(dsrc_model)

        # Register the ExternalDataset
        dproducer_id = self.dams_cli.register_external_data_set(external_dataset_id=ds_id)

        ## Associate everything
        # Convenience method
#        self.dams_cli.assign_eoi_resources(external_data_provider_id=ext_dprov_id, data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id, external_dataset_id=ds_id, external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Or using each method
        self.dams_cli.assign_data_source_to_external_data_provider(data_source_id=ext_dsrc_id, external_data_provider_id=ext_dprov_id)
        self.dams_cli.assign_data_source_to_data_model(data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id)
        self.dams_cli.assign_external_dataset_to_data_source(external_dataset_id=ds_id, data_source_id=ext_dsrc_id)
        self.dams_cli.assign_external_dataset_to_agent_instance(external_dataset_id=ds_id, agent_instance_id=eda_inst_id)
#        self.dams_cli.assign_external_dataset_agent_to_data_model(external_data_agent_id=eda_id, data_source_model_id=ext_dsrc_model_id)
#        self.dams_cli.assign_external_data_agent_to_agent_instance(external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Generate the data product and associate it to the ExternalDataset
        dprod = DataProduct(name='hfr_product', description='raw hfr product')
        dproduct_id = self.dpms_cli.create_data_product(data_product=dprod)

        self.dams_cli.assign_data_product(input_resource_id=ds_id, data_product_id=dproduct_id, create_stream=True)



########## Tests ##########

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_get_capabilities(self):
        # Get all the capabilities
        caps = self._ncom_agt_cli.get_capabilities()
        log.debug("all capabilities: %s" % caps)
        lst=[['RES_CMD', 'acquire_data'], ['RES_CMD', 'acquire_data_by_request'],
            ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'], ['RES_CMD', 'compare'],
            ['RES_CMD', 'get_attributes'], ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
            ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._ncom_agt_cli.get_capabilities(capability_types=['RES_CMD'])
        log.debug("resource commands: %s" % caps)
        lst=[['RES_CMD', 'acquire_data'], ['RES_CMD', 'acquire_data_by_request'],
            ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'], ['RES_CMD', 'compare'],
            ['RES_CMD', 'get_attributes'], ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
            ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._ncom_agt_cli.get_capabilities(capability_types=['RES_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._ncom_agt_cli.get_capabilities(capability_types=['AGT_CMD'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._ncom_agt_cli.get_capabilities(capability_types=['AGT_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_get_attrs(self):
        cmd = AgentCommand(command_id="111", command="get_attributes")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_get_fingerprint(self):
        cmd = AgentCommand(command_id="111", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_single_worker(self):
        cmd = AgentCommand(command_id="111", command="get_attributes")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="112", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_multi_worker(self):
        cmd = AgentCommand(command_id="111", command="has_new_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="111", command="has_new_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._hfr_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="112", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="112", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._hfr_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_acquire_data(self):
        cmd = AgentCommand(command_id="113", command="acquire_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_acquire_new_data(self):
        cmd = AgentCommand(command_id="113", command="acquire_new_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)

    @unittest.skip("Underlying method not yet implemented")
    def test_set_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.set_param(resource_id=res_id, name="param", value="value")

    @unittest.skip("Underlying method not yet implemented")
    def test_get_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.get_param(resource_id=res_id, name="param")

    @unittest.skip("Underlying method not yet implemented")
    def test_execute_agent(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.execute_agent(resource_id=res_id)

    @unittest.skip("Underlying method not yet implemented")
    def test_set_agent_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.set_agent_param(resource_id=res_id, name="param", value="value")

    @unittest.skip("Underlying method not yet implemented")
    def test_get_agent_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.get_agent_param(resource_id=res_id, name="param")
class TestPlatformAgent(IonIntegrationTestCase, HelperTestMixin):
    @classmethod
    def setUpClass(cls):
        HelperTestMixin.setUpClass()

    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url("res/deploy/r2deploy.yml")

        self._pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        self.PLATFORM_CONFIG = {"platform_id": self.PLATFORM_ID, "driver_config": DVR_CONFIG}

        # Start data suscribers, add stop to cleanup.
        # Define stream_config.
        self._async_data_result = AsyncResult()
        self._data_greenlets = []
        self._stream_config = {}
        self._samples_received = []
        self._data_subscribers = []
        self._start_data_subscribers()
        self.addCleanup(self._stop_data_subscribers)

        self._agent_config = {
            "agent": {"resource_id": PA_RESOURCE_ID},
            "stream_config": self._stream_config,
            # pass platform config here
            "platform_config": self.PLATFORM_CONFIG,
        }

        log.debug("launching with agent_config=%s", str(self._agent_config))

        self._launcher = LauncherFactory.createLauncher()
        self._pid = self._launcher.launch(self.PLATFORM_ID, self._agent_config)

        log.debug("LAUNCHED PLATFORM_ID=%r", self.PLATFORM_ID)

        # Start a resource agent client to talk with the agent.
        self._pa_client = ResourceAgentClient(PA_RESOURCE_ID, process=FakeProcess())
        log.info("Got pa client %s." % str(self._pa_client))

    def tearDown(self):
        try:
            self._launcher.cancel_process(self._pid)
        finally:
            super(TestPlatformAgent, self).tearDown()

    def _start_data_subscribers(self):
        """
        """

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}
        self._data_subscribers = []

        #
        # TODO retrieve appropriate stream definitions; for the moment, using
        # adhoc_get_stream_names
        #

        # A callback for processing subscribed-to data.
        def consume_data(message, stream_route, stream_id):
            log.info("Subscriber received data message: %s." % str(message))
            self._samples_received.append(message)
            self._async_data_result.set()

        for stream_name in adhoc_get_stream_names():
            log.info("creating stream %r ...", stream_name)

            # TODO use appropriate exchange_point
            stream_id, stream_route = self._pubsub_client.create_stream(name=stream_name, exchange_point="science_data")

            log.info("create_stream(%r): stream_id=%r, stream_route=%s", stream_name, stream_id, str(stream_route))

            pdict = adhoc_get_parameter_dictionary(stream_name)
            stream_config = dict(
                stream_route=stream_route.routing_key, stream_id=stream_id, parameter_dictionary=pdict.dump()
            )

            self._stream_config[stream_name] = stream_config
            log.info("_stream_config[%r]= %r", stream_name, stream_config)

            # Create subscriptions for each stream.
            exchange_name = "%s_queue" % stream_name
            self._purge_queue(exchange_name)
            sub = StandaloneStreamSubscriber(exchange_name, consume_data)
            sub.start()
            self._data_subscribers.append(sub)
            sub_id = self._pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id])
            self._pubsub_client.activate_subscription(sub_id)
            sub.subscription_id = sub_id

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()

    def _stop_data_subscribers(self):
        """
        Stop the data subscribers on cleanup.
        """
        for sub in self._data_subscribers:
            if hasattr(sub, "subscription_id"):
                try:
                    self._pubsub_client.deactivate_subscription(sub.subscription_id)
                except:
                    pass
                self._pubsub_client.delete_subscription(sub.subscription_id)
            sub.stop()

    def _get_state(self):
        state = self._pa_client.get_agent_state()
        return state

    def _assert_state(self, state):
        self.assertEquals(self._get_state(), state)

    # def _execute_agent(self, cmd, timeout=TIMEOUT):
    def _execute_agent(self, cmd):
        log.info("_execute_agent: cmd=%r kwargs=%r ...", cmd.command, cmd.kwargs)
        time_start = time.time()
        # retval = self._pa_client.execute_agent(cmd, timeout=timeout)
        retval = self._pa_client.execute_agent(cmd)
        elapsed_time = time.time() - time_start
        log.info("_execute_agent: cmd=%r elapsed_time=%s, retval = %s", cmd.command, elapsed_time, str(retval))
        return retval

    def _reset(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RESET)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.UNINITIALIZED)

    def _ping_agent(self):
        retval = self._pa_client.ping_agent()
        self.assertIsInstance(retval, str)

    def _ping_resource(self):
        cmd = AgentCommand(command=PlatformAgentEvent.PING_RESOURCE)
        if self._get_state() == PlatformAgentState.UNINITIALIZED:
            # should get ServerError: "Command not handled in current state"
            with self.assertRaises(ServerError):
                # self._pa_client.execute_agent(cmd, timeout=TIMEOUT)
                self._pa_client.execute_agent(cmd)
        else:
            # In all other states the command should be accepted:
            retval = self._execute_agent(cmd)
            self.assertEquals("PONG", retval.result)

    def _get_metadata(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_METADATA)
        retval = self._execute_agent(cmd)
        md = retval.result
        self.assertIsInstance(md, dict)
        # TODO verify possible subset of required entries in the dict.
        log.info("GET_METADATA = %s", md)

    def _get_ports(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_PORTS)
        retval = self._execute_agent(cmd)
        md = retval.result
        self.assertIsInstance(md, dict)
        # TODO verify possible subset of required entries in the dict.
        log.info("GET_PORTS = %s", md)

    def _set_up_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID
        port_attrName = self.PORT_ATTR_NAME

        kwargs = dict(port_id=port_id, attributes={port_attrName: self.VALID_PORT_ATTR_VALUE})
        cmd = AgentCommand(command=PlatformAgentEvent.SET_UP_PORT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("SET_UP_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertTrue(port_attrName in result[port_id])

    def _turn_on_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.TURN_ON_PORT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("TURN_ON_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], bool)

    def _turn_off_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.TURN_OFF_PORT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("TURN_OFF_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], bool)

    def _get_resource(self):
        attrNames = self.ATTR_NAMES
        cur_time = get_ion_ts()
        #
        # OOIION-631 Note: I'm asked to only use get_ion_ts() as a basis for
        # using system time. However, other associated supporting (and more
        # "numeric") routines would be convenient. In particular, note the
        # following operation to subtract a number of seconds for my request:
        #
        from_time = str(int(cur_time) - 50000)  # a 50-sec time window
        kwargs = dict(attr_names=attrNames, from_time=from_time)
        cmd = AgentCommand(command=PlatformAgentEvent.GET_RESOURCE, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attr_name in attrNames:
            self._verify_valid_attribute_id(attr_name, attr_values)

    def _set_resource(self):
        attrNames = self.ATTR_NAMES
        writ_attrNames = self.WRITABLE_ATTR_NAMES

        # do valid settings:

        # TODO more realistic value depending on attribute's type
        attrs = [(attrName, self.VALID_ATTR_VALUE) for attrName in attrNames]
        log.info("%r: setting attributes=%s", self.PLATFORM_ID, attrs)
        kwargs = dict(attrs=attrs)
        cmd = AgentCommand(command=PlatformAgentEvent.SET_RESOURCE, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attrName in attrNames:
            if attrName in writ_attrNames:
                self._verify_valid_attribute_id(attrName, attr_values)
            else:
                self._verify_not_writable_attribute_id(attrName, attr_values)

        # try invalid settings:

        # set invalid values to writable attributes:
        attrs = [(attrName, self.INVALID_ATTR_VALUE) for attrName in writ_attrNames]
        log.info("%r: setting attributes=%s", self.PLATFORM_ID, attrs)
        kwargs = dict(attrs=attrs)
        cmd = AgentCommand(command=PlatformAgentEvent.SET_RESOURCE, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attrName in writ_attrNames:
            self._verify_attribute_value_out_of_range(attrName, attr_values)

    def _initialize(self):
        self._assert_state(PlatformAgentState.UNINITIALIZED)
        #        kwargs = dict(plat_config=self.PLATFORM_CONFIG)
        #        cmd = AgentCommand(command=PlatformAgentEvent.INITIALIZE, kwargs=kwargs)
        cmd = AgentCommand(command=PlatformAgentEvent.INITIALIZE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.INACTIVE)

    def _go_active(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GO_ACTIVE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.IDLE)

    def _run(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RUN)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _go_inactive(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GO_INACTIVE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.INACTIVE)

    def _get_subplatform_ids(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_SUBPLATFORM_IDS)
        retval = self._execute_agent(cmd)
        self.assertIsInstance(retval.result, list)
        self.assertTrue(x in retval.result for x in self.SUBPLATFORM_IDS)
        return retval.result

    def _start_event_dispatch(self):
        kwargs = dict(params="TODO set params")
        cmd = AgentCommand(command=PlatformAgentEvent.START_EVENT_DISPATCH, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        self.assertTrue(retval.result is not None)
        return retval.result

    def _wait_for_a_data_sample(self):
        log.info("waiting for reception of a data sample...")
        self._async_data_result.get(timeout=15)
        # just wait for at least one -- see consume_data above
        self.assertTrue(len(self._samples_received) >= 1)

    def _stop_event_dispatch(self):
        cmd = AgentCommand(command=PlatformAgentEvent.STOP_EVENT_DISPATCH)
        retval = self._execute_agent(cmd)
        self.assertTrue(retval.result is not None)
        return retval.result

    def test_capabilities(self):

        # log.info("test_capabilities starting.  Default timeout=%s", TIMEOUT)
        log.info("test_capabilities starting.  Default timeout=%i", CFG.endpoint.receive.timeout)

        agt_cmds_all = [
            PlatformAgentEvent.INITIALIZE,
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GO_ACTIVE,
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RUN,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.SET_UP_PORT,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.START_EVENT_DISPATCH,
            PlatformAgentEvent.STOP_EVENT_DISPATCH,
        ]

        def sort_caps(caps):
            agt_cmds = []
            agt_pars = []
            res_cmds = []
            res_pars = []

            if len(caps) > 0 and isinstance(caps[0], AgentCapability):
                agt_cmds = [x.name for x in caps if x.cap_type == CapabilityType.AGT_CMD]
                agt_pars = [x.name for x in caps if x.cap_type == CapabilityType.AGT_PAR]
                res_cmds = [x.name for x in caps if x.cap_type == CapabilityType.RES_CMD]
                res_pars = [x.name for x in caps if x.cap_type == CapabilityType.RES_PAR]

            elif len(caps) > 0 and isinstance(caps[0], dict):
                agt_cmds = [x["name"] for x in caps if x["cap_type"] == CapabilityType.AGT_CMD]
                agt_pars = [x["name"] for x in caps if x["cap_type"] == CapabilityType.AGT_PAR]
                res_cmds = [x["name"] for x in caps if x["cap_type"] == CapabilityType.RES_CMD]
                res_pars = [x["name"] for x in caps if x["cap_type"] == CapabilityType.RES_PAR]

            return agt_cmds, agt_pars, res_cmds, res_pars

        agt_pars_all = ["example"]  # 'cause ResourceAgent defines aparam_example
        res_pars_all = []
        res_cmds_all = []

        ##################################################################
        # UNINITIALIZED
        ##################################################################

        self._assert_state(PlatformAgentState.UNINITIALIZED)

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state UNINITIALIZED.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_uninitialized = [PlatformAgentEvent.INITIALIZE, PlatformAgentEvent.GET_RESOURCE_CAPABILITIES]
        self.assertItemsEqual(agt_cmds, agt_cmds_uninitialized)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states.
        retval = self._pa_client.get_capabilities(current_state=False)

        # Validate all capabilities as read from state UNINITIALIZED.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        self._initialize()

        ##################################################################
        # INACTIVE
        ##################################################################

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state INACTIVE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_inactive = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GO_ACTIVE,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        self.assertItemsEqual(agt_cmds, agt_cmds_inactive)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state INACTIVE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        self._go_active()

        ##################################################################
        # IDLE
        ##################################################################

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state IDLE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_idle = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RUN,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        self.assertItemsEqual(agt_cmds, agt_cmds_idle)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states as read from IDLE.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state IDLE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        self._run()

        ##################################################################
        # COMMAND
        ##################################################################

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state COMMAND
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_command = [
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.SET_UP_PORT,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.START_EVENT_DISPATCH,
            PlatformAgentEvent.STOP_EVENT_DISPATCH,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_command)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        # Get exposed capabilities in all states as read from state COMMAND.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state COMMAND
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_all)
        self.assertItemsEqual(res_pars, res_pars_all)

        self._go_inactive()
        self._reset()

    def test_go_active_and_run(self):

        # log.info("test_go_active_and_run starting.  Default timeout=%s", TIMEOUT)
        log.info("test_capabilities starting.  Default timeout=%i", CFG.endpoint.receive.timeout)

        self._ping_agent()

        #        self._ping_resource() skipping this here while the timeout issue
        #                              on r2_light and other builds is investigated.

        self._initialize()
        self._go_active()
        self._run()

        self._ping_agent()
        self._ping_resource()

        self._get_metadata()
        self._get_ports()
        self._get_subplatform_ids()

        self._get_resource()
        self._set_resource()

        self._set_up_port()
        self._turn_on_port()

        self._start_event_dispatch()

        self._wait_for_a_data_sample()

        self._stop_event_dispatch()

        self._turn_off_port()

        self._go_inactive()
        self._reset()
class TestPlatformAgent(IonIntegrationTestCase, HelperTestMixin):
    @classmethod
    def setUpClass(cls):
        HelperTestMixin.setUpClass()

        # Use the network definition provided by RSN OMS directly.
        rsn_oms = CIOMSClientFactory.create_instance(DVR_CONFIG["oms_uri"])
        network_definition = RsnOmsUtil.build_network_definition(rsn_oms)
        network_definition_ser = NetworkUtil.serialize_network_definition(network_definition)
        if log.isEnabledFor(logging.DEBUG):
            log.debug("NetworkDefinition serialization:\n%s", network_definition_ser)

        cls.PLATFORM_CONFIG = {
            "platform_id": cls.PLATFORM_ID,
            "driver_config": DVR_CONFIG,
            "network_definition": network_definition_ser,
        }

        NetworkUtil._gen_open_diagram(network_definition.pnodes[cls.PLATFORM_ID])

    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url("res/deploy/r2deploy.yml")

        self._pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        # Start data subscribers, add stop to cleanup.
        # Define stream_config.
        self._async_data_result = AsyncResult()
        self._data_greenlets = []
        self._stream_config = {}
        self._samples_received = []
        self._data_subscribers = []
        self._start_data_subscribers()
        self.addCleanup(self._stop_data_subscribers)

        # start event subscriber:
        self._async_event_result = AsyncResult()
        self._event_subscribers = []
        self._events_received = []
        self.addCleanup(self._stop_event_subscribers)
        self._start_event_subscriber()

        self._agent_config = {
            "agent": {"resource_id": PA_RESOURCE_ID},
            "stream_config": self._stream_config,
            # pass platform config here
            "platform_config": self.PLATFORM_CONFIG,
        }

        if log.isEnabledFor(logging.TRACE):
            log.trace("launching with agent_config=%s" % str(self._agent_config))

        self._launcher = LauncherFactory.createLauncher()
        self._pid = self._launcher.launch(self.PLATFORM_ID, self._agent_config)

        log.debug("LAUNCHED PLATFORM_ID=%r", self.PLATFORM_ID)

        # Start a resource agent client to talk with the agent.
        self._pa_client = ResourceAgentClient(PA_RESOURCE_ID, process=FakeProcess())
        log.info("Got pa client %s." % str(self._pa_client))

    def tearDown(self):
        try:
            self._launcher.cancel_process(self._pid)
        finally:
            super(TestPlatformAgent, self).tearDown()

    def _start_data_subscribers(self):
        """
        """

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}
        self._data_subscribers = []

        #
        # TODO retrieve appropriate stream definitions; for the moment, using
        # adhoc_get_stream_names
        #

        # A callback for processing subscribed-to data.
        def consume_data(message, stream_route, stream_id):
            log.info("Subscriber received data message: %s." % str(message))
            self._samples_received.append(message)
            self._async_data_result.set()

        for stream_name in adhoc_get_stream_names():
            log.info("creating stream %r ...", stream_name)

            # TODO use appropriate exchange_point
            stream_id, stream_route = self._pubsub_client.create_stream(name=stream_name, exchange_point="science_data")

            log.info("create_stream(%r): stream_id=%r, stream_route=%s", stream_name, stream_id, str(stream_route))

            pdict = adhoc_get_parameter_dictionary(stream_name)
            stream_config = dict(
                stream_route=stream_route.routing_key, stream_id=stream_id, parameter_dictionary=pdict.dump()
            )

            self._stream_config[stream_name] = stream_config
            log.info("_stream_config[%r]= %r", stream_name, stream_config)

            # Create subscriptions for each stream.
            exchange_name = "%s_queue" % stream_name
            self._purge_queue(exchange_name)
            sub = StandaloneStreamSubscriber(exchange_name, consume_data)
            sub.start()
            self._data_subscribers.append(sub)
            sub_id = self._pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id])
            self._pubsub_client.activate_subscription(sub_id)
            sub.subscription_id = sub_id

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()

    def _stop_data_subscribers(self):
        """
        Stop the data subscribers on cleanup.
        """
        for sub in self._data_subscribers:
            if hasattr(sub, "subscription_id"):
                try:
                    self._pubsub_client.deactivate_subscription(sub.subscription_id)
                except:
                    pass
                self._pubsub_client.delete_subscription(sub.subscription_id)
            sub.stop()

    def _start_event_subscriber(self, event_type="DeviceEvent", sub_type="platform_event"):
        """
        Starts event subscriber for events of given event_type ("DeviceEvent"
        by default) and given sub_type ("platform_event" by default).
        """

        def consume_event(evt, *args, **kwargs):
            # A callback for consuming events.
            log.info("Event subscriber received evt: %s.", str(evt))
            self._events_received.append(evt)
            self._async_event_result.set(evt)

        sub = EventSubscriber(event_type=event_type, sub_type=sub_type, callback=consume_event)

        sub.start()
        log.info("registered event subscriber for event_type=%r, sub_type=%r", event_type, sub_type)

        self._event_subscribers.append(sub)
        sub._ready_event.wait(timeout=EVENT_TIMEOUT)

    def _stop_event_subscribers(self):
        """
        Stops the event subscribers on cleanup.
        """
        try:
            for sub in self._event_subscribers:
                if hasattr(sub, "subscription_id"):
                    try:
                        self.pubsubcli.deactivate_subscription(sub.subscription_id)
                    except:
                        pass
                    self.pubsubcli.delete_subscription(sub.subscription_id)
                sub.stop()
        finally:
            self._event_subscribers = []

    def _get_state(self):
        state = self._pa_client.get_agent_state()
        return state

    def _assert_state(self, state):
        self.assertEquals(self._get_state(), state)

    # def _execute_agent(self, cmd, timeout=TIMEOUT):
    def _execute_agent(self, cmd):
        log.info("_execute_agent: cmd=%r kwargs=%r ...", cmd.command, cmd.kwargs)
        time_start = time.time()
        # retval = self._pa_client.execute_agent(cmd, timeout=timeout)
        retval = self._pa_client.execute_agent(cmd)
        elapsed_time = time.time() - time_start
        log.info("_execute_agent: cmd=%r elapsed_time=%s, retval = %s", cmd.command, elapsed_time, str(retval))
        return retval

    def _reset(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RESET)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.UNINITIALIZED)

    def _ping_agent(self):
        retval = self._pa_client.ping_agent()
        self.assertIsInstance(retval, str)

    def _ping_resource(self):
        cmd = AgentCommand(command=PlatformAgentEvent.PING_RESOURCE)
        if self._get_state() == PlatformAgentState.UNINITIALIZED:
            # should get ServerError: "Command not handled in current state"
            with self.assertRaises(ServerError):
                # self._pa_client.execute_agent(cmd, timeout=TIMEOUT)
                self._pa_client.execute_agent(cmd)
        else:
            # In all other states the command should be accepted:
            retval = self._execute_agent(cmd)
            self.assertEquals("PONG", retval.result)

    def _get_metadata(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_METADATA)
        retval = self._execute_agent(cmd)
        md = retval.result
        self.assertIsInstance(md, dict)
        # TODO verify possible subset of required entries in the dict.
        log.info("GET_METADATA = %s", md)

    def _get_ports(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_PORTS)
        retval = self._execute_agent(cmd)
        md = retval.result
        self.assertIsInstance(md, dict)
        # TODO verify possible subset of required entries in the dict.
        log.info("GET_PORTS = %s", md)

    def _connect_instrument(self):
        #
        # TODO more realistic settings for the connection
        #
        port_id = self.PORT_ID
        instrument_id = self.INSTRUMENT_ID
        instrument_attributes = self.INSTRUMENT_ATTRIBUTES_AND_VALUES

        kwargs = dict(port_id=port_id, instrument_id=instrument_id, attributes=instrument_attributes)
        cmd = AgentCommand(command=PlatformAgentEvent.CONNECT_INSTRUMENT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("CONNECT_INSTRUMENT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], dict)
        returned_attrs = self._verify_valid_instrument_id(instrument_id, result[port_id])
        if isinstance(returned_attrs, dict):
            for attrName in instrument_attributes:
                self.assertTrue(attrName in returned_attrs)

    def _get_connected_instruments(self):
        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("GET_CONNECTED_INSTRUMENTS = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], dict)
        instrument_id = self.INSTRUMENT_ID
        self.assertTrue(instrument_id in result[port_id])

    def _disconnect_instrument(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID
        instrument_id = self.INSTRUMENT_ID

        kwargs = dict(port_id=port_id, instrument_id=instrument_id)
        cmd = AgentCommand(command=PlatformAgentEvent.DISCONNECT_INSTRUMENT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("DISCONNECT_INSTRUMENT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], dict)
        self.assertTrue(instrument_id in result[port_id])
        self._verify_instrument_disconnected(instrument_id, result[port_id][instrument_id])

    def _turn_on_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.TURN_ON_PORT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("TURN_ON_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertEquals(result[port_id], NormalResponse.PORT_TURNED_ON)

    def _turn_off_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.TURN_OFF_PORT, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("TURN_OFF_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertEquals(result[port_id], NormalResponse.PORT_TURNED_OFF)

    def _get_resource(self):
        attrNames = self.ATTR_NAMES
        #
        # OOIION-631: use get_ion_ts() as a basis for using system time, which is
        # a string.
        #
        cur_time = get_ion_ts()
        from_time = str(int(cur_time) - 50000)  # a 50-sec time window
        kwargs = dict(attr_names=attrNames, from_time=from_time)
        cmd = AgentCommand(command=PlatformAgentEvent.GET_RESOURCE, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attr_name in attrNames:
            self._verify_valid_attribute_id(attr_name, attr_values)

    def _set_resource(self):
        attrNames = self.ATTR_NAMES
        writ_attrNames = self.WRITABLE_ATTR_NAMES

        # do valid settings:

        # TODO more realistic value depending on attribute's type
        attrs = [(attrName, self.VALID_ATTR_VALUE) for attrName in attrNames]
        log.info("%r: setting attributes=%s", self.PLATFORM_ID, attrs)
        kwargs = dict(attrs=attrs)
        cmd = AgentCommand(command=PlatformAgentEvent.SET_RESOURCE, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attrName in attrNames:
            if attrName in writ_attrNames:
                self._verify_valid_attribute_id(attrName, attr_values)
            else:
                self._verify_not_writable_attribute_id(attrName, attr_values)

        # try invalid settings:

        # set invalid values to writable attributes:
        attrs = [(attrName, self.INVALID_ATTR_VALUE) for attrName in writ_attrNames]
        log.info("%r: setting attributes=%s", self.PLATFORM_ID, attrs)
        kwargs = dict(attrs=attrs)
        cmd = AgentCommand(command=PlatformAgentEvent.SET_RESOURCE, kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attrName in writ_attrNames:
            self._verify_attribute_value_out_of_range(attrName, attr_values)

    def _initialize(self):
        self._assert_state(PlatformAgentState.UNINITIALIZED)
        cmd = AgentCommand(command=PlatformAgentEvent.INITIALIZE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.INACTIVE)

    def _go_active(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GO_ACTIVE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.IDLE)

    def _run(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RUN)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _pause(self):
        cmd = AgentCommand(command=PlatformAgentEvent.PAUSE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.STOPPED)

    def _resume(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RESUME)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _start_resource_monitoring(self):
        cmd = AgentCommand(command=PlatformAgentEvent.START_MONITORING)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.MONITORING)

    def _wait_for_a_data_sample(self):
        log.info("waiting for reception of a data sample...")
        # just wait for at least one -- see consume_data
        self._async_data_result.get(timeout=DATA_TIMEOUT)
        self.assertTrue(len(self._samples_received) >= 1)
        log.info("Received samples: %s", len(self._samples_received))

    def _stop_resource_monitoring(self):
        cmd = AgentCommand(command=PlatformAgentEvent.STOP_MONITORING)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _go_inactive(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GO_INACTIVE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.INACTIVE)

    def _get_subplatform_ids(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_SUBPLATFORM_IDS)
        retval = self._execute_agent(cmd)
        self.assertIsInstance(retval.result, list)
        self.assertTrue(x in retval.result for x in self.SUBPLATFORM_IDS)
        return retval.result

    def _wait_for_external_event(self):
        log.info("waiting for reception of an external event...")
        # just wait for at least one -- see consume_event
        self._async_event_result.get(timeout=EVENT_TIMEOUT)
        self.assertTrue(len(self._events_received) >= 1)
        log.info("Received events: %s", len(self._events_received))

    def _check_sync(self):
        cmd = AgentCommand(command=PlatformAgentEvent.CHECK_SYNC)
        retval = self._execute_agent(cmd)
        log.info("CHECK_SYNC result: %s", retval.result)
        self.assertTrue(retval.result is not None)
        self.assertEquals(retval.result[0:3], "OK:")
        return retval.result

    def test_capabilities(self):

        agt_cmds_all = [
            PlatformAgentEvent.INITIALIZE,
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GO_ACTIVE,
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RUN,
            PlatformAgentEvent.CLEAR,
            PlatformAgentEvent.PAUSE,
            PlatformAgentEvent.RESUME,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.CONNECT_INSTRUMENT,
            PlatformAgentEvent.DISCONNECT_INSTRUMENT,
            PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.START_MONITORING,
            PlatformAgentEvent.STOP_MONITORING,
            PlatformAgentEvent.CHECK_SYNC,
        ]

        def sort_caps(caps):
            agt_cmds = []
            agt_pars = []
            res_cmds = []
            res_pars = []

            if len(caps) > 0 and isinstance(caps[0], AgentCapability):
                agt_cmds = [x.name for x in caps if x.cap_type == CapabilityType.AGT_CMD]
                agt_pars = [x.name for x in caps if x.cap_type == CapabilityType.AGT_PAR]
                res_cmds = [x.name for x in caps if x.cap_type == CapabilityType.RES_CMD]
                res_pars = [x.name for x in caps if x.cap_type == CapabilityType.RES_PAR]

            elif len(caps) > 0 and isinstance(caps[0], dict):
                agt_cmds = [x["name"] for x in caps if x["cap_type"] == CapabilityType.AGT_CMD]
                agt_pars = [x["name"] for x in caps if x["cap_type"] == CapabilityType.AGT_PAR]
                res_cmds = [x["name"] for x in caps if x["cap_type"] == CapabilityType.RES_CMD]
                res_pars = [x["name"] for x in caps if x["cap_type"] == CapabilityType.RES_PAR]

            return agt_cmds, agt_pars, res_cmds, res_pars

        agt_pars_all = ["example"]  # 'cause ResourceAgent defines aparam_example
        res_pars_all = []
        res_cmds_all = []

        ##################################################################
        # UNINITIALIZED
        ##################################################################

        self._assert_state(PlatformAgentState.UNINITIALIZED)

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state UNINITIALIZED.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_uninitialized = [PlatformAgentEvent.INITIALIZE, PlatformAgentEvent.GET_RESOURCE_CAPABILITIES]
        self.assertItemsEqual(agt_cmds, agt_cmds_uninitialized)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states.
        retval = self._pa_client.get_capabilities(current_state=False)

        # Validate all capabilities as read from state UNINITIALIZED.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        ##################################################################
        # INACTIVE
        ##################################################################
        self._initialize()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state INACTIVE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_inactive = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GO_ACTIVE,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        self.assertItemsEqual(agt_cmds, agt_cmds_inactive)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state INACTIVE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        ##################################################################
        # IDLE
        ##################################################################
        self._go_active()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state IDLE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_idle = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RUN,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        self.assertItemsEqual(agt_cmds, agt_cmds_idle)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states as read from IDLE.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state IDLE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        ##################################################################
        # COMMAND
        ##################################################################
        self._run()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state COMMAND
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_command = [
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.PAUSE,
            PlatformAgentEvent.CLEAR,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.CONNECT_INSTRUMENT,
            PlatformAgentEvent.DISCONNECT_INSTRUMENT,
            PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.START_MONITORING,
            PlatformAgentEvent.CHECK_SYNC,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_command)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        ##################################################################
        # STOPPED
        ##################################################################
        self._pause()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state STOPPED
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_stopped = [
            PlatformAgentEvent.RESUME,
            PlatformAgentEvent.CLEAR,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_stopped)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        # back to COMMAND:
        self._resume()

        ##################################################################
        # MONITORING
        ##################################################################
        self._start_resource_monitoring()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state MONITORING
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_monitoring = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.CONNECT_INSTRUMENT,
            PlatformAgentEvent.DISCONNECT_INSTRUMENT,
            PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.STOP_MONITORING,
            PlatformAgentEvent.CHECK_SYNC,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_monitoring)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        # return to COMMAND state:
        self._stop_resource_monitoring()

        ###################
        # ALL CAPABILITIES
        ###################

        # Get exposed capabilities in all states as read from state COMMAND.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state COMMAND
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_all)
        self.assertItemsEqual(res_pars, res_pars_all)

        self._go_inactive()
        self._reset()

    def test_some_state_transitions(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._initialize()  # -> INACTIVE
        self._reset()  # -> UNINITIALIZED
        self._initialize()  # -> INACTIVE
        self._go_active()  # -> IDLE
        self._reset()  # -> UNINITIALIZED
        self._initialize()  # -> INACTIVE
        self._go_active()  # -> IDLE
        self._run()  # -> COMMAND
        self._pause()  # -> STOPPED
        self._resume()  # -> COMMAND

        self._reset()  # -> UNINITIALIZED

    def test_get_set_resources(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._get_resource()
        self._set_resource()

        self._go_inactive()
        self._reset()

    def test_some_commands(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._ping_agent()
        self._ping_resource()

        self._get_metadata()
        self._get_ports()
        self._get_subplatform_ids()

        self._go_inactive()
        self._reset()

    def test_resource_monitoring(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._start_resource_monitoring()
        self._wait_for_a_data_sample()
        self._stop_resource_monitoring()

        self._go_inactive()
        self._reset()

    def test_external_event_dispatch(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._wait_for_external_event()

        self._go_inactive()
        self._reset()

    def test_connect_disconnect_instrument(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._connect_instrument()
        self._turn_on_port()

        self._get_connected_instruments()

        self._turn_off_port()
        self._disconnect_instrument()

        self._go_inactive()
        self._reset()

    def test_check_sync(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._check_sync()

        self._connect_instrument()
        self._check_sync()

        self._disconnect_instrument()
        self._check_sync()

        self._go_inactive()
        self._reset()
class TestInstrumentAgent(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """

    def setUp(self):
        """
        Setup the test environment to exersice use of instrumet agent, including:
        * define driver_config parameters.
        * create container with required services and container client.
        * create publication stream ids for each driver data stream.
        * create stream_config parameters.
        * create and activate subscriptions for agent data streams.
        * spawn instrument agent process and create agent client.
        * add cleanup functions to cause subscribers to get stopped.
        """
        
        # Names of agent data streams to be configured.
        parsed_stream_name = 'ctd_parsed'        
        raw_stream_name = 'ctd_raw'        

        # Driver configuration.
        self.driver_config = {
            'svr_addr': 'localhost',
            'cmd_port': 5556,
            'evt_port': 5557,
            'dvr_mod': 'ion.services.mi.drivers.sbe37_driver',
            'dvr_cls': 'SBE37Driver',
            'comms_config': {
                SBE37Channel.CTD: {
                    'method':'ethernet',
                    'device_addr': '137.110.112.119',
                    'device_port': 4001,
                    'server_addr': 'localhost',
                    'server_port': 8888
                }                
            },
            'packet_config' : {
                parsed_stream_name : ('prototype.sci_data.ctd_stream',
                                'ctd_stream_packet'),
                raw_stream_name : None
            }
        }

        # Start container.
        self._start_container()

        # Establish endpoint with container.
        self._container_client = ContainerAgentClient(node=self.container.node,
                                                      name=self.container.name)
        
        # Bring up services in a deploy file.        
        self._container_client.start_rel_from_url('res/deploy/r2dm.yml')

        # Create a pubsub client to create streams.
        self._pubsub_client = PubsubManagementServiceClient(
                                                    node=self.container.node)

        # Create parsed stream. The stream name must match one
        # used by the driver to label packet data.
        parsed_stream_def = ctd_stream_definition(stream_id=None)
        parsed_stream_def_id = self._pubsub_client.create_stream_definition(
                                                    container=parsed_stream_def)        
        parsed_stream_id = self._pubsub_client.create_stream(
                        name=parsed_stream_name,
                        stream_definition_id=parsed_stream_def_id,
                        original=True,
                        encoding='ION R2')

        # Create raw stream. The stream name must match one used by the
        # driver to label packet data. This stream does not yet have a
        # packet definition so will not be published.
        raw_stream_def = ctd_stream_definition(stream_id=None)
        raw_stream_def_id = self._pubsub_client.create_stream_definition(
                                                    container=raw_stream_def)        
        raw_stream_id = self._pubsub_client.create_stream(name=raw_stream_name,
                        stream_definition_id=raw_stream_def_id,
                        original=True,
                        encoding='ION R2')
        
        # Define stream configuration.
        self.stream_config = {
            parsed_stream_name : parsed_stream_id,
            raw_stream_name : raw_stream_id
        }

        # A callback for processing subscribed-to data.
        def consume(message, headers):
            log.info('Subscriber received message: %s', str(message))

        # Create a stream subscriber registrar to create subscribers.
        subscriber_registrar = StreamSubscriberRegistrar(process=self.container,
                                                node=self.container.node)

        # Create and activate parsed data subscription.
        parsed_sub = subscriber_registrar.create_subscriber(exchange_name=\
                                            'parsed_queue', callback=consume)
        parsed_sub.start()
        parsed_query = StreamQuery(stream_ids=[parsed_stream_id])
        parsed_sub_id = self._pubsub_client.create_subscription(\
                            query=parsed_query, exchange_name='parsed_queue')
        self._pubsub_client.activate_subscription(parsed_sub_id)

        # Create and activate raw data subscription.
        raw_sub = subscriber_registrar.create_subscriber(exchange_name=\
                                                'raw_queue', callback=consume)
        raw_sub.start()
        raw_query = StreamQuery(stream_ids=[raw_stream_id])
        raw_sub_id = self._pubsub_client.create_subscription(\
                            query=raw_query, exchange_name='raw_queue')
        self._pubsub_client.activate_subscription(raw_sub_id)

        # Create agent config.
        self.agent_config = {
            'driver_config' : self.driver_config,
            'stream_config' : self.stream_config
        }

        # Launch an instrument agent process.
        self._ia_name = 'agent007'
        self._ia_mod = 'ion.services.mi.instrument_agent'
        self._ia_class = 'InstrumentAgent'
        self._ia_pid = self._container_client.spawn_process(name=self._ia_name,
                                       module=self._ia_mod, cls=self._ia_class,
                                       config=self.agent_config)      
        log.info('got pid=%s', str(self._ia_pid))
        
        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = ResourceAgentClient('123xyz', name=self._ia_pid,
                                              process=FakeProcess())
        log.info('got ia client %s', str(self._ia_client))        
        
        # Add cleanup function to stop subscribers.        
        def stop_subscriber(sub_list):
            for sub in sub_list:
                sub.stop()            
        self.addCleanup(stop_subscriber, [parsed_sub, raw_sub])
                
    def test_initialize(self):
        """
        Test agent initialize command. This causes creation of
        driver process and transition to inactive.
        """
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)        
        time.sleep(2)
        
        caps = self._ia_client.get_capabilities()
        log.info('Capabilities: %s',str(caps))
        
        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)

    def test_go_active(self):
        """
        Test agent go_active command. This causes a driver process to
        launch a connection broker, connect to device hardware, determine
        entry state of driver and intialize driver parameters.
        """
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)        
        time.sleep(2)
        
        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='go_inactive')
        retval = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        time.sleep(2)

    def test_get_set(self):
        """
        Test instrument driver resource get/set interface. This tests
        getting and setting driver reousrce paramters in various syntaxes and
        validates results including persistence on device hardware.
        """
        cmd = AgentCommand(command='initialize')
        reply = self._ia_client.execute_agent(cmd)        
        time.sleep(2)
        
        cmd = AgentCommand(command='go_active')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='run')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        get_params = [
            (SBE37Channel.CTD, SBE37Parameter.ALL)            
        ]
        reply = self._ia_client.get_param(get_params)
        time.sleep(2)

        self.assertIsInstance(reply, dict)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.TA2)], float)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.PTCA1)], float)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.TCALDATE)], (tuple, list))

        # Set up a param dict of the original values.
        old_ta2 = reply[(SBE37Channel.CTD, SBE37Parameter.TA2)]
        old_ptca1 = reply[(SBE37Channel.CTD, SBE37Parameter.PTCA1)]
        old_tcaldate = reply[(SBE37Channel.CTD, SBE37Parameter.TCALDATE)]

        orig_params = {
            (SBE37Channel.CTD, SBE37Parameter.TA2): old_ta2,
            (SBE37Channel.CTD, SBE37Parameter.PTCA1): old_ptca1,
            (SBE37Channel.CTD, SBE37Parameter.TCALDATE): old_tcaldate            
        }

        # Set up a param dict of new values.
        new_ta2 = old_ta2*2
        new_ptcal1 = old_ptca1*2
        new_tcaldate = list(old_tcaldate)
        new_tcaldate[2] = new_tcaldate[2] + 1
        
        new_params = {
            (SBE37Channel.CTD, SBE37Parameter.TA2): new_ta2,
            (SBE37Channel.CTD, SBE37Parameter.PTCA1): new_ptcal1,
            (SBE37Channel.CTD, SBE37Parameter.TCALDATE): new_tcaldate
        }

        # Set the params to their new values.
        reply = self._ia_client.set_param(new_params)
        time.sleep(2)

        # Check overall success and success of the individual paramters.
        self.assertIsInstance(reply, dict)
        
        # Get the same paramters back from the driver.
        get_params = [
            (SBE37Channel.CTD, SBE37Parameter.TA2),
            (SBE37Channel.CTD, SBE37Parameter.PTCA1),
            (SBE37Channel.CTD, SBE37Parameter.TCALDATE)
        ]
        reply = self._ia_client.get_param(get_params)
        time.sleep(2)

        # Check success, and check that the parameters were set to the
        # new values.
        self.assertIsInstance(reply, dict)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.TA2)], float)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.PTCA1)], float)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.TCALDATE)], (tuple, list))
        self.assertAlmostEqual(reply[(SBE37Channel.CTD, SBE37Parameter.TA2)], new_ta2, delta=abs(0.01*new_ta2))
        self.assertAlmostEqual(reply[(SBE37Channel.CTD, SBE37Parameter.PTCA1)], new_ptcal1, delta=abs(0.01*new_ptcal1))
        self.assertEqual(reply[(SBE37Channel.CTD, SBE37Parameter.TCALDATE)], new_tcaldate)

        # Set the paramters back to their original values.        
        reply = self._ia_client.set_param(orig_params)
        self.assertIsInstance(reply, dict)

        # Get the parameters back from the driver.
        reply = self._ia_client.get_param(get_params)

        # Check overall and individual sucess, and that paramters were
        # returned to their original values.
        self.assertIsInstance(reply, dict)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.TA2)], float)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.PTCA1)], float)
        self.assertIsInstance(reply[(SBE37Channel.CTD, SBE37Parameter.TCALDATE)], (tuple, list))
        self.assertAlmostEqual(reply[(SBE37Channel.CTD, SBE37Parameter.TA2)], old_ta2, delta=abs(0.01*old_ta2))
        self.assertAlmostEqual(reply[(SBE37Channel.CTD, SBE37Parameter.PTCA1)], old_ptca1, delta=abs(0.01*old_ptca1))
        self.assertEqual(reply[(SBE37Channel.CTD, SBE37Parameter.TCALDATE)], old_tcaldate)

        time.sleep(2)

        cmd = AgentCommand(command='go_inactive')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='reset')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

    def test_poll(self):
        """
        Test instrument driver resource execute interface to do polled
        sampling.
        """
        cmd = AgentCommand(command='initialize')
        reply = self._ia_client.execute_agent(cmd)        
        time.sleep(2)
        
        cmd = AgentCommand(command='go_active')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='run')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='go_inactive')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='reset')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)


    def test_autosample(self):
        """
        Test instrument driver execute interface to start and stop streaming
        mode.
        """
        cmd = AgentCommand(command='initialize')
        reply = self._ia_client.execute_agent(cmd)        
        time.sleep(2)
        
        cmd = AgentCommand(command='go_active')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='run')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='go_streaming')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(30)
        
        cmd = AgentCommand(command='go_observatory')
        while True:
            reply = self._ia_client.execute_agent(cmd)
            result = reply.result
            if isinstance(result, dict):
                if all([val == None for val in result.values()]):
                    break
            time.sleep(2)
        time.sleep(2)
        
        cmd = AgentCommand(command='go_inactive')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)

        cmd = AgentCommand(command='reset')
        reply = self._ia_client.execute_agent(cmd)
        time.sleep(2)
Beispiel #11
0
class ExternalDatasetAgentTestBase(object):

    # Agent parameters.
    EDA_RESOURCE_ID = '123xyz'
    EDA_NAME = 'ExampleEDA'
    EDA_MOD = 'ion.agents.data.external_dataset_agent'
    EDA_CLS = 'ExternalDatasetAgent'
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """
    def setUp(self):
        """
        Initialize test members.
        """

        #        log.warn('Starting the container')
        # Start container.
        self._start_container()

        # Bring up services in a deploy file
        #        log.warn('Starting the rel')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        # Create a pubsub client to create streams.
        #        log.warn('Init a pubsub client')
        self._pubsub_client = PubsubManagementServiceClient(
            node=self.container.node)
        #        log.warn('Init a ContainerAgentClient')
        self._container_client = ContainerAgentClient(node=self.container.node,
                                                      name=self.container.name)

        # Data async and subscription  TODO: Replace with new subscriber
        self._finished_count = None
        #TODO: Switch to gevent.queue.Queue
        self._async_finished_result = AsyncResult()
        self._finished_events_received = []
        self._finished_event_subscriber = None
        self._start_finished_event_subscriber()
        self.addCleanup(self._stop_finished_event_subscriber)

        # TODO: Finish dealing with the resources and whatnot
        # TODO: DVR_CONFIG and (potentially) stream_config could both be reconfigured in self._setup_resources()
        self._setup_resources()

        #TG: Setup/configure the granule logger to log granules as they're published

        # Create agent config.
        agent_config = {
            'driver_config': self.DVR_CONFIG,
            'stream_config': {},
            'agent': {
                'resource_id': self.EDA_RESOURCE_ID
            },
            'test_mode': True
        }

        # Start instrument agent.
        self._ia_pid = None
        log.debug('TestInstrumentAgent.setup(): starting EDA.')
        self._ia_pid = self._container_client.spawn_process(
            name=self.EDA_NAME,
            module=self.EDA_MOD,
            cls=self.EDA_CLS,
            config=agent_config)
        log.info('Agent pid=%s.', str(self._ia_pid))

        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = None
        self._ia_client = ResourceAgentClient(self.EDA_RESOURCE_ID,
                                              process=FakeProcess())
        log.info('Got ia client %s.', str(self._ia_client))

    ########################################
    # Private "setup" functions
    ########################################

    def _setup_resources(self):
        raise NotImplementedError(
            '_setup_resources must be implemented in the subclass')

    def create_stream_and_logger(self, name, stream_id=''):
        if not stream_id or stream_id is '':
            stream_id = self._pubsub_client.create_stream(name=name,
                                                          encoding='ION R2')

        pid = self._container_client.spawn_process(
            name=name + '_logger',
            module='ion.processes.data.stream_granule_logger',
            cls='StreamGranuleLogger',
            config={'process': {
                'stream_id': stream_id
            }})
        log.info(
            'Started StreamGranuleLogger \'{0}\' subscribed to stream_id={1}'.
            format(pid, stream_id))

        return stream_id

    def _start_finished_event_subscriber(self):
        def consume_event(*args, **kwargs):
            if args[0].description == 'TestingFinished':
                log.debug('TestingFinished event received')
                self._finished_events_received.append(args[0])
                if self._finished_count and self._finished_count == len(
                        self._finished_events_received):
                    log.debug('Finishing test...')
                    self._async_finished_result.set(
                        len(self._finished_events_received))
                    log.debug(
                        'Called self._async_finished_result.set({0})'.format(
                            len(self._finished_events_received)))

        self._finished_event_subscriber = EventSubscriber(
            event_type='DeviceEvent', callback=consume_event)
        self._finished_event_subscriber.activate()

    def _stop_finished_event_subscriber(self):
        if self._finished_event_subscriber:
            self._finished_event_subscriber.deactivate()
            self._finished_event_subscriber = None

    ########################################
    # Custom assertion functions
    ########################################

    def assertListsEqual(self, lst1, lst2):
        lst1.sort()
        lst2.sort()
        return lst1 == lst2

    def assertSampleDict(self, val):
        """
        Verify the value is a sample dictionary for the sbe37.
        """
        #{'p': [-6.945], 'c': [0.08707], 't': [20.002], 'time': [1333752198.450622]}
        self.assertTrue(isinstance(val, dict))
        self.assertTrue(val.has_key('c'))
        self.assertTrue(val.has_key('t'))
        self.assertTrue(val.has_key('p'))
        self.assertTrue(val.has_key('time'))
        c = val['c'][0]
        t = val['t'][0]
        p = val['p'][0]
        time = val['time'][0]

        self.assertTrue(isinstance(c, float))
        self.assertTrue(isinstance(t, float))
        self.assertTrue(isinstance(p, float))
        self.assertTrue(isinstance(time, float))

    def assertParamDict(self, pd, all_params=False):
        """
        Verify all device parameters exist and are correct type.
        """
        if all_params:
            self.assertEqual(set(pd.keys()), set(PARAMS.keys()))
            for (key, type_val) in PARAMS.iteritems():
                if type_val == list or type_val == tuple:
                    self.assertTrue(isinstance(pd[key], (list, tuple)))
                else:
                    self.assertTrue(isinstance(pd[key], type_val))

        else:
            for (key, val) in pd.iteritems():
                self.assertTrue(PARAMS.has_key(key))
                self.assertTrue(isinstance(val, PARAMS[key]))

    def assertParamVals(self, params, correct_params):
        """
        Verify parameters take the correct values.
        """
        self.assertEqual(set(params.keys()), set(correct_params.keys()))
        for (key, val) in params.iteritems():
            correct_val = correct_params[key]
            if isinstance(val, float):
                # Verify to 5% of the larger value.
                max_val = max(abs(val), abs(correct_val))
                self.assertAlmostEqual(val, correct_val, delta=max_val * .01)

            elif isinstance(val, (list, tuple)):
                # list of tuple.
                self.assertEqual(list(val), list(correct_val))

            else:
                # int, bool, str.
                self.assertEqual(val, correct_val)

    ########################################
    # Test functions
    ########################################

    def test_acquire_data(self):
        cmd = AgentCommand(command='initialize')
        _ = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='go_active')
        _ = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='run')
        _ = self._ia_client.execute_agent(cmd)

        self._finished_count = 3

        log.info('Send an unconstrained request for data (\'new data\')')
        cmd = AgentCommand(command='acquire_data')
        self._ia_client.execute(cmd)

        log.info(
            'Send a second unconstrained request for data (\'new data\'), should be rejected'
        )
        cmd = AgentCommand(command='acquire_data')
        self._ia_client.execute(cmd)

        config_mods = {}

        log.info(
            'Send a constrained request for data: constraints = HIST_CONSTRAINTS_1'
        )
        config_mods['stream_id'] = self.create_stream_and_logger(
            name='stream_id_for_historical_1')
        config_mods['constraints'] = self.HIST_CONSTRAINTS_1
        cmd = AgentCommand(command='acquire_data', args=[config_mods])
        self._ia_client.execute(cmd)

        log.info(
            'Send a second constrained request for data: constraints = HIST_CONSTRAINTS_2'
        )
        config_mods['stream_id'] = self.create_stream_and_logger(
            name='stream_id_for_historical_2')
        config_mods['constraints'] = self.HIST_CONSTRAINTS_2
        #        config={'stream_id':'second_historical','TESTING':True, 'constraints':self.HIST_CONSTRAINTS_2}
        cmd = AgentCommand(command='acquire_data', args=[config_mods])
        self._ia_client.execute(cmd)

        finished = self._async_finished_result.get(timeout=10)
        self.assertEqual(finished, self._finished_count)

        cmd = AgentCommand(command='reset')
        _ = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_acquire_data_while_streaming(self):
        # Test instrument driver execute interface to start and stop streaming mode.
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Make sure the polling interval is appropriate for a test
        params = {'POLLING_INTERVAL': 5}
        self._ia_client.set_param(params)

        self._finished_count = 2

        # Begin streaming.
        cmd = AgentCommand(command='go_streaming')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STREAMING)

        config = get_safe(self.DVR_CONFIG, 'dh_cfg', {})

        log.info(
            'Send a constrained request for data: constraints = HIST_CONSTRAINTS_1'
        )
        config['stream_id'] = self.create_stream_and_logger(
            name='stream_id_for_historical_1')
        config['constraints'] = self.HIST_CONSTRAINTS_1
        cmd = AgentCommand(command='acquire_data', args=[config])
        reply = self._ia_client.execute(cmd)
        self.assertNotEqual(reply.status, 660)

        gevent.sleep(12)

        # Halt streaming.
        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Assert that data was received
        self._async_finished_result.get(timeout=10)
        self.assertTrue(len(self._finished_events_received) >= 3)

        cmd = AgentCommand(command='reset')
        _ = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_streaming(self):
        # Test instrument driver execute interface to start and stop streaming mode.
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Make sure the polling interval is appropriate for a test
        params = {'POLLING_INTERVAL': 5}
        self._ia_client.set_param(params)

        self._finished_count = 3

        # Begin streaming.
        cmd = AgentCommand(command='go_streaming')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STREAMING)

        # Wait for some samples to roll in.
        gevent.sleep(12)

        # Halt streaming.
        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Assert that data was received
        self._async_finished_result.get(timeout=10)
        self.assertTrue(len(self._finished_events_received) >= 3)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_observatory(self):
        # Test instrument driver get and set interface.

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Retrieve all resource parameters.
        reply = self._ia_client.get_param('DRIVER_PARAMETER_ALL')
        self.assertParamDict(reply, True)
        orig_config = reply

        ## Retrieve a subset of resource parameters.
        params = ['POLLING_INTERVAL']
        reply = self._ia_client.get_param(params)
        self.assertParamDict(reply)
        orig_params = reply

        # Set a subset of resource parameters.
        new_params = {
            'POLLING_INTERVAL': (orig_params['POLLING_INTERVAL'] * 2),
        }
        self._ia_client.set_param(new_params)
        check_new_params = self._ia_client.get_param(params)
        self.assertParamVals(check_new_params, new_params)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_get_set_param(self):
        cmd = AgentCommand(command='initialize')
        _ = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='go_active')
        _ = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='run')
        _ = self._ia_client.execute_agent(cmd)

        # Get a couple parameters
        retval = self._ia_client.get_param(
            ['POLLING_INTERVAL', 'PATCHABLE_CONFIG_KEYS'])
        log.debug('Retrieved parameters from agent: {0}'.format(retval))
        self.assertTrue(isinstance(retval, dict))
        self.assertEqual(type(retval['POLLING_INTERVAL']), int)
        self.assertEqual(type(retval['PATCHABLE_CONFIG_KEYS']), list)

        # Attempt to get a parameter that doesn't exist
        log.debug('Try getting a non-existent parameter \'BAD_PARAM\'')
        self.assertRaises(InstParameterError, self._ia_client.get_param,
                          ['BAD_PARAM'])

        # Set the polling_interval to a new value, then get it to make sure it set properly
        self._ia_client.set_param({'POLLING_INTERVAL': 10})
        retval = self._ia_client.get_param(['POLLING_INTERVAL'])
        log.debug('Retrieved parameters from agent: {0}'.format(retval))
        self.assertTrue(isinstance(retval, dict))
        self.assertEqual(retval['POLLING_INTERVAL'], 10)

        # Attempt to set a parameter that doesn't exist
        log.debug('Try setting a non-existent parameter \'BAD_PARAM\'')
        self.assertRaises(InstParameterError, self._ia_client.set_param,
                          {'BAD_PARAM': 'bad_val'})

        # Attempt to set one parameter that does exist, and one that doesn't
        self.assertRaises(InstParameterError, self._ia_client.set_param, {
            'POLLING_INTERVAL': 20,
            'BAD_PARAM': 'bad_val'
        })

        retval = self._ia_client.get_param(['POLLING_INTERVAL'])
        log.debug('Retrieved parameters from agent: {0}'.format(retval))
        self.assertTrue(isinstance(retval, dict))
        self.assertEqual(retval['POLLING_INTERVAL'], 20)

        cmd = AgentCommand(command='reset')
        _ = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_initialize(self):
        # Test agent initialize command. This causes creation of driver process and transition to inactive.

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_states(self):
        # Test agent state transitions.

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='pause')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STOPPED)

        cmd = AgentCommand(command='resume')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='clear')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='pause')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STOPPED)

        cmd = AgentCommand(command='clear')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        self._finished_count = 1

        cmd = AgentCommand(command='go_streaming')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STREAMING)

        gevent.sleep(5)

        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        self._async_finished_result.get(timeout=5)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_capabilities(self):
        # Test the ability to retrieve agent and resource parameter and command capabilities.
        acmds = self._ia_client.get_capabilities(['AGT_CMD'])
        log.debug('Agent Commands: {0}'.format(acmds))
        acmds = [item[1] for item in acmds]
        self.assertListsEqual(acmds, AGT_CMDS.keys())
        apars = self._ia_client.get_capabilities(['AGT_PAR'])
        log.debug('Agent Parameters: {0}'.format(apars))

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        rcmds = self._ia_client.get_capabilities(['RES_CMD'])
        log.debug('Resource Commands: {0}'.format(rcmds))
        rcmds = [item[1] for item in rcmds]
        self.assertListsEqual(rcmds, CMDS.keys())

        rpars = self._ia_client.get_capabilities(['RES_PAR'])
        log.debug('Resource Parameters: {0}'.format(rpars))
        rpars = [item[1] for item in rpars]
        self.assertListsEqual(rpars, PARAMS.keys())

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_errors(self):
        # Test illegal behavior and replies.

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        # Can't go active in unitialized state.
        # Status 660 is state error.
        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        log.info('GO ACTIVE CMD %s', str(retval))
        self.assertEquals(retval.status, 660)

        # Can't command driver in this state.
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertEqual(reply.status, 660)

        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # 404 unknown agent command.
        cmd = AgentCommand(command='kiss_edward')
        retval = self._ia_client.execute_agent(cmd)
        self.assertEquals(retval.status, 404)

        # 670 unknown driver command.
        cmd = AgentCommand(command='acquire_sample_please')
        retval = self._ia_client.execute(cmd)
        self.assertEqual(retval.status, 670)

        # 630 Parameter error.
        self.assertRaises(InstParameterError, self._ia_client.get_param,
                          'bogus bogus')

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
Beispiel #12
0
class TestIntExternalObservatoryAgent(IonIntegrationTestCase):
    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url(rel_url='res/deploy/r2eoi.yml')

        self.dams_cli = DataAcquisitionManagementServiceClient()
        self.dpms_cli = DataProductManagementServiceClient()

        eda = ExternalDatasetAgent()
        self.eda_id = self.dams_cli.create_external_dataset_agent(eda)

        eda_inst = ExternalDatasetAgentInstance()
        self.eda_inst_id = self.dams_cli.create_external_dataset_agent_instance(
            eda_inst, external_dataset_agent_id=self.eda_id)

        self._setup_ncom()
        proc_name = self.ncom_ds_id + '_worker'
        config = {}
        config['process'] = {'name': proc_name, 'type': 'agent'}
        config['process']['eoa'] = {'dataset_id': self.ncom_ds_id}
        pid = self.container.spawn_process(
            name=proc_name,
            module='eoi.agent.external_observatory_agent',
            cls='ExternalObservatoryAgent',
            config=config)

        queue_id = "%s.%s" % (self.container.id, pid)

        log.debug(
            "Spawned worker process ==> proc_name: %s\tproc_id: %s\tqueue_id: %s"
            % (proc_name, pid, queue_id))

        self._agent_cli = ResourceAgentClient(self.ncom_ds_id,
                                              name=pid,
                                              process=FakeProcess())
        log.debug("Got a ResourceAgentClient: res_id=%s" %
                  self._agent_cli.resource_id)

    def _setup_ncom(self):
        # TODO: some or all of this (or some variation) should move to DAMS

        # Create and register the necessary resources/objects

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(),
                                     contact=ContactInformation())
        #        dprov.institution.name = "OOI CGSN"
        dprov.contact.name = "Robert Weller"
        dprov.contact.email = "*****@*****.**"

        # Create DataSource
        dsrc = DataSource(protocol_type="DAP",
                          institution=Institution(),
                          contact=ContactInformation())
        #        dsrc.connection_params["base_data_url"] = "http://ooi.whoi.edu/thredds/dodsC/"
        dsrc.connection_params["base_data_url"] = ""
        dsrc.contact.name = "Rich Signell"
        dsrc.contact.email = "*****@*****.**"

        # Create ExternalDataset
        dset = ExternalDataset(name="test",
                               dataset_description=DatasetDescription(),
                               update_description=UpdateDescription(),
                               contact=ContactInformation())

        #        dset.dataset_description.parameters["dataset_path"] = "ooi/AS02CPSM_R_M.nc"
        dset.dataset_description.parameters[
            "dataset_path"] = "test_data/ncom.nc"
        dset.dataset_description.parameters["temporal_dimension"] = "time"
        dset.dataset_description.parameters["zonal_dimension"] = "lon"
        dset.dataset_description.parameters["meridional_dimension"] = "lat"

        # Create DataSourceModel
        dsrc_model = DataSourceModel(name="dap_model")
        dsrc_model.model = "DAP"
        dsrc_model.data_handler_module = "eoi.agent.handler.dap_external_data_handler"
        dsrc_model.data_handler_class = "DapExternalDataHandler"

        ## Run everything through DAMS
        ds_id = self.ncom_ds_id = self.dams_cli.create_external_dataset(
            external_dataset=dset)
        ext_dprov_id = self.dams_cli.create_external_data_provider(
            external_data_provider=dprov)
        ext_dsrc_id = self.dams_cli.create_data_source(data_source=dsrc)
        ext_dsrc_model_id = self.dams_cli.create_data_source_model(dsrc_model)

        # Register the ExternalDataset
        dproducer_id = self.dams_cli.register_external_data_set(
            external_dataset_id=ds_id)

        ## Associate everything
        # Convenience method
        #        self.dams_cli.assign_eoi_resources(external_data_provider_id=ext_dprov_id, data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id, external_dataset_id=ds_id, external_data_agent_id=self.eda_id, agent_instance_id=self.eda_inst_id)

        # Or using each method
        self.dams_cli.assign_data_source_to_external_data_provider(
            data_source_id=ext_dsrc_id, external_data_provider_id=ext_dprov_id)
        self.dams_cli.assign_data_source_to_data_model(
            data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id)
        self.dams_cli.assign_external_dataset_to_data_source(
            external_dataset_id=ds_id, data_source_id=ext_dsrc_id)
        self.dams_cli.assign_external_dataset_to_agent_instance(
            external_dataset_id=ds_id, agent_instance_id=self.eda_inst_id)
        #        self.dams_cli.assign_external_data_agent_to_agent_instance(external_data_agent_id=self.eda_id, agent_instance_id=self.eda_inst_id)

        # Generate the data product and associate it to the ExternalDataset
        dprod = DataProduct(name='ncom_product',
                            description='raw ncom product')
        dproduct_id = self.dpms_cli.create_data_product(data_product=dprod)

        self.dams_cli.assign_data_product(input_resource_id=ds_id,
                                          data_product_id=dproduct_id,
                                          create_stream=True)

########## Tests ##########

#    @unittest.skip("Currently broken due to resource/agent refactorings")

    def test_get_capabilities(self):
        # Get all the capabilities
        caps = self._agent_cli.get_capabilities()
        log.debug("all capabilities: %s" % caps)
        lst = [['RES_CMD', 'acquire_data'],
               ['RES_CMD', 'acquire_data_by_request'],
               ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'],
               ['RES_CMD', 'compare'], ['RES_CMD', 'get_attributes'],
               ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
               ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._agent_cli.get_capabilities(capability_types=['RES_CMD'])
        log.debug("resource commands: %s" % caps)
        lst = [['RES_CMD', 'acquire_data'],
               ['RES_CMD', 'acquire_data_by_request'],
               ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'],
               ['RES_CMD', 'compare'], ['RES_CMD', 'get_attributes'],
               ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
               ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._agent_cli.get_capabilities(capability_types=['RES_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._agent_cli.get_capabilities(capability_types=['AGT_CMD'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._agent_cli.get_capabilities(capability_types=['AGT_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_get_attrs(self):
        cmd = AgentCommand(command_id="111", command="get_attributes")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._agent_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)
class ExternalDatasetAgentTestBase(object):

    # Agent parameters.
    EDA_RESOURCE_ID = '123xyz'
    EDA_NAME = 'ExampleEDA'
    EDA_MOD = 'ion.agents.data.external_dataset_agent'
    EDA_CLS = 'ExternalDatasetAgent'

    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """
    def setUp(self):
        """
        Initialize test members.
        """

        #log.warn('Starting the container')
        # Start container.
        self._start_container()

        # Bring up services in a deploy file
        #log.warn('Starting the rel')
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        # Create a pubsub client to create streams.
        #        log.warn('Init a pubsub client')
        self._pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        #        log.warn('Init a ContainerAgentClient')
        self._container_client = ContainerAgentClient(node=self.container.node, name=self.container.name)

        # Data async and subscription  TODO: Replace with new subscriber
        self._finished_count = None
        #TODO: Switch to gevent.queue.Queue
        self._async_finished_result = AsyncResult()
        self._finished_events_received = []
        self._finished_event_subscriber = None
        self._start_finished_event_subscriber()
        self.addCleanup(self._stop_finished_event_subscriber)

        # TODO: Finish dealing with the resources and whatnot
        # TODO: DVR_CONFIG and (potentially) stream_config could both be reconfigured in self._setup_resources()
        self._setup_resources()

        #TG: Setup/configure the granule logger to log granules as they're published

        # Create agent config.
        agent_config = {
            'driver_config': self.DVR_CONFIG,
            'stream_config': {},
            'agent': {'resource_id': self.EDA_RESOURCE_ID},
            'test_mode': True
        }

        # Start instrument agent.
        self._ia_pid = None
        log.debug('TestInstrumentAgent.setup(): starting EDA.')
        self._ia_pid = self._container_client.spawn_process(
            name=self.EDA_NAME,
            module=self.EDA_MOD,
            cls=self.EDA_CLS,
            config=agent_config
        )
        log.info('Agent pid=%s.', str(self._ia_pid))

        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = None
        self._ia_client = ResourceAgentClient(self.EDA_RESOURCE_ID, process=FakeProcess())
        log.info('Got ia client %s.', str(self._ia_client))

    ########################################
    # Private "setup" functions
    ########################################

    def _setup_resources(self):
        raise NotImplementedError('_setup_resources must be implemented in the subclass')

    def create_stream_and_logger(self, name, stream_id='', pdict=None):

        stream_def_id = ''
        if not stream_id or stream_id is '':
            if pdict:
                stream_def_id = self._pubsub_client.create_stream_definition(parameter_dictionary=pdict.dump(), stream_type='stream')
            stream_id, route = self._pubsub_client.create_stream(name=name, exchange_point='science_data', stream_definition_id=stream_def_id)
        else:
            route = self._pubsub_client.read_stream_route(stream_id=stream_id)
            stream_def = self._pubsub_client.read_stream_definition(stream_id=stream_id)
            stream_def_id = stream_def._id

        pid = self._container_client.spawn_process(
            name=name + '_logger',
            module='ion.processes.data.stream_granule_logger',
            cls='StreamGranuleLogger',
            config={'process': {'stream_id': stream_id}}
        )
        log.info('Started StreamGranuleLogger \'{0}\' subscribed to stream_id={1}'.format(pid, stream_id))

        return stream_id, route, stream_def_id

    def _start_finished_event_subscriber(self):

        def consume_event(*args, **kwargs):
            if args[0].description == 'TestingFinished':
                log.debug('TestingFinished event received')
                self._finished_events_received.append(args[0])
                if self._finished_count and self._finished_count == len(self._finished_events_received):
                    log.debug('Finishing test...')
                    self._async_finished_result.set(len(self._finished_events_received))
                    log.debug('Called self._async_finished_result.set({0})'.format(len(self._finished_events_received)))

        self._finished_event_subscriber = EventSubscriber(event_type='DeviceEvent', callback=consume_event)
        self._finished_event_subscriber.start()

    def _stop_finished_event_subscriber(self):
        if self._finished_event_subscriber:
            self._finished_event_subscriber.stop()
            self._finished_event_subscriber = None

    ########################################
    # Custom assertion functions
    ########################################
    def assertListsEqual(self, lst1, lst2):
        lst1.sort()
        lst2.sort()
        return lst1 == lst2

    def assertSampleDict(self, val):
        """
        Verify the value is a sample dictionary for the sbe37.
        """
        #{'p': [-6.945], 'c': [0.08707], 't': [20.002], 'time': [1333752198.450622]}
        self.assertTrue(isinstance(val, dict))
        self.assertTrue('c' in val)
        self.assertTrue('t' in val)
        self.assertTrue('p' in val)
        self.assertTrue('time' in val)
        c = val['c'][0]
        t = val['t'][0]
        p = val['p'][0]
        time = val['time'][0]

        self.assertTrue(isinstance(c, float))
        self.assertTrue(isinstance(t, float))
        self.assertTrue(isinstance(p, float))
        self.assertTrue(isinstance(time, float))

    def assertParamDict(self, pd, all_params=False):
        """
        Verify all device parameters exist and are correct type.
        """
        if all_params:
            self.assertEqual(set(pd.keys()), set(PARAMS.keys()))
            for (key, type_val) in PARAMS.iteritems():
                if type_val == list or type_val == tuple:
                    self.assertTrue(isinstance(pd[key], (list, tuple)))
                else:
                    self.assertTrue(isinstance(pd[key], type_val))

        else:
            for (key, val) in pd.iteritems():
                self.assertTrue(key in PARAMS)
                self.assertTrue(isinstance(val, PARAMS[key]))

    def assertParamVals(self, params, correct_params):
        """
        Verify parameters take the correct values.
        """
        self.assertEqual(set(params.keys()), set(correct_params.keys()))
        for (key, val) in params.iteritems():
            correct_val = correct_params[key]
            if isinstance(val, float):
                # Verify to 5% of the larger value.
                max_val = max(abs(val), abs(correct_val))
                self.assertAlmostEqual(val, correct_val, delta=max_val * .01)

            elif isinstance(val, (list, tuple)):
                # list of tuple.
                self.assertEqual(list(val), list(correct_val))

            else:
                # int, bool, str.
                self.assertEqual(val, correct_val)

    ########################################
    # Test functions
    ########################################
    def test_acquire_data_while_streaming(self):
        # Test instrument driver execute interface to start and stop streaming mode.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        params = {
            'POLLING_INTERVAL': 3
        }
        self._ia_client.set_resource(params)

        self._finished_count = 1

        cmd = AgentCommand(command=DriverEvent.START_AUTOSAMPLE)
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STREAMING)

        config = get_safe(self.DVR_CONFIG, 'dh_cfg', {})
        log.info('Send a constrained request for data: constraints = HIST_CONSTRAINTS_1')
        config['stream_id'], config['stream_route'], _ = self.create_stream_and_logger(name='stream_id_for_historical_1')
        config['constraints'] = self.HIST_CONSTRAINTS_1
        cmd = AgentCommand(command=DriverEvent.ACQUIRE_SAMPLE, args=[config])
        self._ia_client.execute_resource(cmd)

        cmd = AgentCommand(command=DriverEvent.STOP_AUTOSAMPLE)
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        finished = self._async_finished_result.get(timeout=120)
        self.assertEqual(finished, self._finished_count)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_acquire_data(self):

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        log.warn('Send an unconstrained request for data (\'new data\')')
        cmd = AgentCommand(command=DriverEvent.ACQUIRE_SAMPLE)
        self._ia_client.execute_resource(command=cmd)
        state = self._ia_client.get_agent_state()
        log.info(state)
        self.assertEqual(state, ResourceAgentState.COMMAND)

        self._finished_count = 2

        config_mods = {}

        log.info('Send a constrained request for data: constraints = HIST_CONSTRAINTS_1')
        config_mods['stream_id'], config_mods['stream_route'], _ = self.create_stream_and_logger(name='stream_id_for_historical_1')
        config_mods['constraints'] = self.HIST_CONSTRAINTS_1
        cmd = AgentCommand(command=DriverEvent.ACQUIRE_SAMPLE, args=[config_mods])
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        log.info('Send a second constrained request for data: constraints = HIST_CONSTRAINTS_2')
        config_mods['stream_id'], config_mods['stream_route'], _ = self.create_stream_and_logger(name='stream_id_for_historical_2')
        config_mods['constraints'] = self.HIST_CONSTRAINTS_2
        cmd = AgentCommand(command=DriverEvent.ACQUIRE_SAMPLE, args=[config_mods])
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        finished = self._async_finished_result.get(timeout=120)
        self.assertEqual(finished, self._finished_count)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_streaming(self):
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        params = {
            'POLLING_INTERVAL': 3
        }
        self._ia_client.set_resource(params)

        self._finished_count = 3

        cmd = AgentCommand(command=DriverEvent.START_AUTOSAMPLE)
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STREAMING)

        #Assert that data was received
        #        self._async_finished_result.get(timeout=600)
        #        self.assertTrue(len(self._finished_events_received) >= 3)

        cmd = AgentCommand(command=DriverEvent.STOP_AUTOSAMPLE)
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_command(self):
        # Test instrument driver get and set interface.

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        # Retrieve all resource parameters.
        reply = self._ia_client.get_resource(params=['DRIVER_PARAMETER_ALL'])
        self.assertParamDict(reply, True)

        ## Retrieve a subset of resource parameters.
        params = [
            'POLLING_INTERVAL'
        ]
        reply = self._ia_client.get_resource(params=params)
        self.assertParamDict(reply)
        orig_params = reply

        # Set a subset of resource parameters.
        new_params = {
            'POLLING_INTERVAL': (orig_params['POLLING_INTERVAL'] * 2),
            }
        self._ia_client.set_resource(params=new_params)
        check_new_params = self._ia_client.get_resource(params)
        self.assertParamVals(check_new_params, new_params)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_get_set_resource(self):
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)

        # Get a couple parameters
        retval = self._ia_client.get_resource(['POLLING_INTERVAL', 'PATCHABLE_CONFIG_KEYS'])
        log.debug('Retrieved parameters from agent: {0}'.format(retval))
        self.assertTrue(isinstance(retval, dict))
        self.assertEqual(type(retval['POLLING_INTERVAL']), int)
        self.assertEqual(type(retval['PATCHABLE_CONFIG_KEYS']), list)

        # Attempt to get a parameter that doesn't exist
        log.debug('Try getting a non-existent parameter \'BAD_PARAM\'')
        with self.assertRaises(ServerError):
            self._ia_client.get_resource(['BAD_PARAM'])

        # Set the polling_interval to a new value, then get it to make sure it set properly
        self._ia_client.set_resource({'POLLING_INTERVAL': 10})
        retval = self._ia_client.get_resource(['POLLING_INTERVAL'])
        log.debug('Retrieved parameters from agent: {0}'.format(retval))
        self.assertTrue(isinstance(retval, dict))
        self.assertEqual(retval['POLLING_INTERVAL'], 10)

        # Attempt to set a parameter that doesn't exist
        log.debug('Try setting a non-existent parameter \'BAD_PARAM\'')
        with self.assertRaises(ServerError):
            self._ia_client.set_resource({'BAD_PARAM': 'bad_val'})

        # Attempt to set one parameter that does exist, and one that doesn't
        with self.assertRaises(ServerError):
            self._ia_client.set_resource({'POLLING_INTERVAL': 20, 'BAD_PARAM': 'bad_val'})

        retval = self._ia_client.get_resource(['POLLING_INTERVAL'])
        log.debug('Retrieved parameters from agent: {0}'.format(retval))
        self.assertTrue(isinstance(retval, dict))
        self.assertEqual(retval['POLLING_INTERVAL'], 20)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_initialize(self):
        # Test agent initialize command. This causes creation of driver process and transition to inactive.

        # We start in uninitialized state.
        # In this state there is no driver process.
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Initialize the agent.
        # The agent is spawned with a driver config, but you can pass one in
        # optinally with the initialize command. This validates the driver
        # config, launches a driver process and connects to it via messaging.
        # If successful, we switch to the inactive state.
        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        # Reset the agent. This causes the driver messaging to be stopped,
        # the driver process to end and switches us back to uninitialized.
        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_states(self):
        # Test agent state transitions.

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.PAUSE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STOPPED)

        cmd = AgentCommand(command=ResourceAgentEvent.RESUME)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.CLEAR)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.PAUSE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STOPPED)

        cmd = AgentCommand(command=ResourceAgentEvent.CLEAR)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=DriverEvent.START_AUTOSAMPLE)
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.STREAMING)

        cmd = AgentCommand(command=DriverEvent.STOP_AUTOSAMPLE)
        self._ia_client.execute_resource(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_capabilities(self):
        """
        Test the ability to retrieve agent and resource parameter and command
        capabilities in various system states.
        """

        # Test the ability to retrieve agent and resource parameter and command capabilities.
        acmds = self._ia_client.get_capabilities(['AGT_CMD'])
        log.debug('Agent Commands: {0}'.format(acmds))
        #        acmds = [item[1] for item in acmds]
        self.assertListsEqual(acmds, AGT_CMDS.keys())
        apars = self._ia_client.get_capabilities(['AGT_PAR'])
        log.debug('Agent Parameters: {0}'.format(apars))

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        rcmds = self._ia_client.get_capabilities(['RES_CMD'])
        log.debug('Resource Commands: {0}'.format(rcmds))
        #        rcmds = [item[1] for item in rcmds]
        self.assertListsEqual(rcmds, CMDS.keys())

        rpars = self._ia_client.get_capabilities(['RES_PAR'])
        log.debug('Resource Parameters: {0}'.format(rpars))
        #        rpars = [item[1] for item in rpars]
        self.assertListsEqual(rpars, PARAMS.keys())

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

    def test_errors(self):
        # Test illegal behavior and replies.

        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)

        # Can't go active in unitialized state.
        # Status 660 is state error.
        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        with self.assertRaises(Conflict):
            self._ia_client.execute_agent(cmd)

        # Can't command driver in this state.
        cmd = AgentCommand(command=DriverEvent.ACQUIRE_SAMPLE)
        with self.assertRaises(Conflict):
            self._ia_client.execute_resource(cmd)
            #self.assertEqual(reply.status, 660)

        cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.INACTIVE)

        cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.IDLE)

        cmd = AgentCommand(command=ResourceAgentEvent.RUN)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.COMMAND)

        # 404 unknown agent command.
        cmd = AgentCommand(command='kiss_edward')
        with self.assertRaises(BadRequest):
            self._ia_client.execute_agent(cmd)

        # 670 unknown driver command.
        cmd = AgentCommand(command='acquire_sample_please')
        with self.assertRaises(ServerError):
            self._ia_client.execute_resource(cmd)

        # 630 Parameter error.
        #self.assertRaises(InstParameterError, self._ia_client.get_param, 'bogus bogus')

        cmd = AgentCommand(command=ResourceAgentEvent.RESET)
        self._ia_client.execute_agent(cmd)
        state = self._ia_client.get_agent_state()
        self.assertEqual(state, ResourceAgentState.UNINITIALIZED)
Beispiel #14
0
class TestInstrumentAgent(IonIntegrationTestCase):
    """
    Test cases for instrument agent class. Functions in this class provide
    instrument agent integration tests and provide a tutorial on use of
    the agent setup and interface.
    """ 
    def setUp(self):
        """
        Initialize test members.
        Start port agent.
        Start container and client.
        Start streams and subscribers.
        Start agent, client.
        """
                
        self._support = DriverIntegrationTestSupport(DRV_MOD,
                                                     DRV_CLS,
                                                     DEV_ADDR,
                                                     DEV_PORT,
                                                     DELIM,
                                                     WORK_DIR)
        # Start port agent, add stop to cleanup.
        self._pagent = None        
        self._start_pagent()
        self.addCleanup(self._support.stop_pagent)    
        
        # Start container.
        self._start_container()
        
        # Bring up services in a deploy file (no need to message)
        self.container.start_rel_from_url('res/deploy/r2dm.yml')

        # Start data suscribers, add stop to cleanup.
        # Define stream_config.
        self._no_samples = None
        self._async_data_result = AsyncResult()
        self._data_greenlets = []
        self._stream_config = {}
        self._samples_received = []
        self._data_subscribers = []
        self._start_data_subscribers()
        self.addCleanup(self._stop_data_subscribers)

        # Start event subscribers, add stop to cleanup.
        self._no_events = None
        self._async_event_result = AsyncResult()
        self._events_received = []
        self._event_subscribers = []
        self._start_event_subscribers()
        self.addCleanup(self._stop_event_subscribers)
                
        # Create agent config.
        agent_config = {
            'driver_config' : DVR_CONFIG,
            'stream_config' : self._stream_config,
            'agent'         : {'resource_id': IA_RESOURCE_ID},
            'test_mode' : True
        }
        
        # Start instrument agent.
        self._ia_pid = None
        log.debug("TestInstrumentAgent.setup(): starting IA.")
        container_client = ContainerAgentClient(node=self.container.node,
                                                name=self.container.name)
        self._ia_pid = container_client.spawn_process(name=IA_NAME,
                                                      module=IA_MOD, 
                                                      cls=IA_CLS, 
                                                      config=agent_config)      
        log.info('Agent pid=%s.', str(self._ia_pid))
        
        # Start a resource agent client to talk with the instrument agent.
        self._ia_client = None
        self._ia_client = ResourceAgentClient(IA_RESOURCE_ID, process=FakeProcess())
        log.info('Got ia client %s.', str(self._ia_client))        
        
    def _start_pagent(self):
        """
        Construct and start the port agent.
        """
        port = self._support.start_pagent()
        
        # Configure driver to use port agent port number.
        DVR_CONFIG['comms_config'] = {
            'addr' : 'localhost',
            'port' : port
        }
        
    def _start_data_subscribers(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)

        # A callback for processing subscribed-to data.
        def consume_data(message, headers):
            log.info('Subscriber received data message: %s.', str(message))
            self._samples_received.append(message)
            if self._no_samples and self._no_samples == len(self._samples_received):
                self._async_data_result.set()
                
        # Create a stream subscriber registrar to create subscribers.
        subscriber_registrar = StreamSubscriberRegistrar(process=self.container,
                                                node=self.container.node)

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}
        self._data_subscribers = []
        for (stream_name, val) in PACKET_CONFIG.iteritems():
            stream_def = ctd_stream_definition(stream_id=None)
            stream_def_id = pubsub_client.create_stream_definition(
                                                    container=stream_def)        
            stream_id = pubsub_client.create_stream(
                        name=stream_name,
                        stream_definition_id=stream_def_id,
                        original=True,
                        encoding='ION R2')
            self._stream_config[stream_name] = stream_id
            
            # Create subscriptions for each stream.
            exchange_name = '%s_queue' % stream_name
            sub = subscriber_registrar.create_subscriber(exchange_name=exchange_name,
                                                         callback=consume_data)
            self._listen(sub)
            self._data_subscribers.append(sub)
            query = StreamQuery(stream_ids=[stream_id])
            sub_id = pubsub_client.create_subscription(\
                                query=query, exchange_name=exchange_name)
            pubsub_client.activate_subscription(sub_id)
            
    def _listen(self, sub):
        """
        Pass in a subscriber here, this will make it listen in a background greenlet.
        """
        gl = spawn(sub.listen)
        self._data_greenlets.append(gl)
        sub._ready_event.wait(timeout=5)
        return gl
                                 
    def _stop_data_subscribers(self):
        """
        Stop the data subscribers on cleanup.
        """
        for sub in self._data_subscribers:
            sub.stop()
        for gl in self._data_greenlets:
            gl.kill()
            
    def _start_event_subscribers(self):
        """
        Create subscribers for agent and driver events.
        """
        def consume_event(*args, **kwargs):
            log.info('Test recieved ION event: args=%s, kwargs=%s, event=%s.', 
                     str(args), str(kwargs), str(args[0]))
            self._events_received.append(args[0])
            if self._no_events and self._no_events == len(self._event_received):
                self._async_event_result.set()
                
        event_sub = EventSubscriber(event_type="DeviceEvent", callback=consume_event)
        event_sub.activate()
        self._event_subscribers.append(event_sub)
        
    def _stop_event_subscribers(self):
        """
        Stop event subscribers on cleanup.
        """
        for sub in self._event_subscribers:
            sub.deactivate()
        
    def assertSampleDict(self, val):
        """
        Verify the value is a sample dictionary for the sbe37.
        """
        #{'p': [-6.945], 'c': [0.08707], 't': [20.002], 'time': [1333752198.450622]}        
        self.assertTrue(isinstance(val, dict))
        self.assertTrue(val.has_key('c'))
        self.assertTrue(val.has_key('t'))
        self.assertTrue(val.has_key('p'))
        self.assertTrue(val.has_key('time'))
        c = val['c'][0]
        t = val['t'][0]
        p = val['p'][0]
        time = val['time'][0]
    
        self.assertTrue(isinstance(c, float))
        self.assertTrue(isinstance(t, float))
        self.assertTrue(isinstance(p, float))
        self.assertTrue(isinstance(time, float))

    def assertParamDict(self, pd, all_params=False):
        """
        Verify all device parameters exist and are correct type.
        """
        if all_params:
            self.assertEqual(set(pd.keys()), set(PARAMS.keys()))
            for (key, type_val) in PARAMS.iteritems():
                if type_val == list or type_val == tuple:
                    self.assertTrue(isinstance(pd[key], (list, tuple)))
                else:
                    self.assertTrue(isinstance(pd[key], type_val))
                    
        else:
            for (key, val) in pd.iteritems():
                self.assertTrue(PARAMS.has_key(key))
                self.assertTrue(isinstance(val, PARAMS[key]))
    
    def assertParamVals(self, params, correct_params):
        """
        Verify parameters take the correct values.
        """
        self.assertEqual(set(params.keys()), set(correct_params.keys()))
        for (key, val) in params.iteritems():
            correct_val = correct_params[key]
            if isinstance(val, float):
                # Verify to 5% of the larger value.
                max_val = max(abs(val), abs(correct_val))
                self.assertAlmostEqual(val, correct_val, delta=max_val*.01)

            elif isinstance(val, (list, tuple)):
                # list of tuple.
                self.assertEqual(list(val), list(correct_val))
            
            else:
                # int, bool, str.
                self.assertEqual(val, correct_val)

    def test_initialize(self):
        """
        Test agent initialize command. This causes creation of
        driver process and transition to inactive.
        """

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)

        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
                
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
    def test_states(self):
        """
        Test agent state transitions.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)
                
        cmd = AgentCommand(command='pause')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STOPPED)
 
        cmd = AgentCommand(command='resume')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)
 
        cmd = AgentCommand(command='clear')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='pause')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STOPPED)

        cmd = AgentCommand(command='clear')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_get_set(self):
        """
        Test instrument driver get and set interface.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Retrieve all resource parameters.                
        reply = self._ia_client.get_param(SBE37Parameter.ALL)
        self.assertParamDict(reply, True)
        orig_config = reply
        
        # Retrieve a subset of resource parameters.
        params = [
            SBE37Parameter.TA0,
            SBE37Parameter.INTERVAL,
            SBE37Parameter.STORETIME
        ]
        reply = self._ia_client.get_param(params)
        self.assertParamDict(reply)
        orig_params = reply

        # Set a subset of resource parameters.
        new_params = {
            SBE37Parameter.TA0 : (orig_params[SBE37Parameter.TA0] * 2),
            SBE37Parameter.INTERVAL : (orig_params[SBE37Parameter.INTERVAL] + 1),
            SBE37Parameter.STORETIME : (not orig_params[SBE37Parameter.STORETIME])
        }
        self._ia_client.set_param(new_params)
        check_new_params = self._ia_client.get_param(params)
        self.assertParamVals(check_new_params, new_params)
        
        # Reset the parameters back to their original values.
        self._ia_client.set_param(orig_params)
        reply = self._ia_client.get_param(SBE37Parameter.ALL)
        reply.pop(SBE37Parameter.SAMPLENUM)
        orig_config.pop(SBE37Parameter.SAMPLENUM)
        self.assertParamVals(reply, orig_config)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_poll(self):
        """
        Test observatory polling function.
        """
        
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)
        
        # Lets get 3 samples.
        self._no_samples = 3
        
        # Poll for a few samples.
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)

        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)        

        # Assert we got 3 samples.
        self._async_data_result.get(timeout=10)
        self.assertTrue(len(self._samples_received)==3)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)        
        
    def test_autosample(self):
        """
        Test instrument driver execute interface to start and stop streaming
        mode.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Make sure the sampling rate and transmission are sane.                
        params = {
            SBE37Parameter.NAVG : 1,
            SBE37Parameter.INTERVAL : 5,
            SBE37Parameter.TXREALTIME : True
        }
        self._ia_client.set_param(params)

        self._no_samples = 2

        # Begin streaming.                
        cmd = AgentCommand(command='go_streaming')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.STREAMING)
 
        # Wait for some samples to roll in.
        gevent.sleep(15)
 
        # Halt streaming.
        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # Assert we got some samples.
        self._async_data_result.get(timeout=10)
        self.assertTrue(len(self._samples_received)>=2)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

    def test_capabilities(self):
        """
        Test the ability to retrieve agent and resource parameter and command
        capabilities.
        """
        acmds = self._ia_client.get_capabilities(['AGT_CMD'])
        acmds = [item[1] for item in acmds]
        self.assertEqual(acmds, AGT_CMDS)
        apars = self._ia_client.get_capabilities(['AGT_PAR'])
        apars = [item[1] for item in apars]
        
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)
        
        rcmds = self._ia_client.get_capabilities(['RES_CMD'])
        rcmds = [item[1] for item in rcmds]
        self.assertEqual(rcmds, CMDS)
        
        rpars = self._ia_client.get_capabilities(['RES_PAR'])
        rpars = [item[1] for item in rpars]
        self.assertEqual(rpars, SBE37Parameter.list())
                
        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
    @unittest.skip('Never written')
    def test_errors(self):
        """
        Test illegal behavior and replies.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)

        # Can't go active in unitialized state.
        # Status 660 is state error.
        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        log.info('GO ACTIVE CMD %s',str(retval))
        self.assertEquals(retval.status, 660)
        
        # Can't command driver in this state.
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertEqual(reply.status, 660)
        
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        # OK, I can do this now.        
        cmd = AgentCommand(command='acquire_sample')
        reply = self._ia_client.execute(cmd)
        self.assertSampleDict(reply.result)

        # 404 unknown agent command.
        cmd = AgentCommand(command='kiss_edward')
        retval = self._ia_client.execute_agent(cmd)
        self.assertEquals(retval.status, 404)
        
        # 670 unknown driver command.
        cmd = AgentCommand(command='acquire_sample_please')
        retval = self._ia_client.execute(cmd)
        self.assertEqual(retval.status, 670)

        # 630 Parameter error.
        with self.assertRaises(InstParameterError):
            reply = self._ia_client.get_param('bogus bogus')

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
        
        
    @unittest.skip('Direct access test to be finished by adding the telnet client, manual for now.')
    def test_direct_access(self):
        """
        Test agent direct_access command. This causes creation of
        driver process and transition to direct access.
        """
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
    
        cmd = AgentCommand(command='initialize')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.INACTIVE)

        cmd = AgentCommand(command='go_active')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.IDLE)
        
        cmd = AgentCommand(command='run')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='go_direct_access', 
                           #kwargs={'session_type':DirectAccessTypes.telnet,
                           kwargs={'session_type':DirectAccessTypes.vsp,
                                   'session_timeout':600,
                                   'inactivity_timeout':600})
        retval = self._ia_client.execute_agent(cmd)
        print("go_direct_access retval=" + str(retval))       
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.DIRECT_ACCESS)

        # sleep to let tester run telnet client manually
        print "test sleeping to run telnet client"
        time.sleep(60)

        # Halt DA.
        cmd = AgentCommand(command='go_observatory')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.OBSERVATORY)

        cmd = AgentCommand(command='reset')
        retval = self._ia_client.execute_agent(cmd)
        cmd = AgentCommand(command='get_current_state')
        retval = self._ia_client.execute_agent(cmd)
        state = retval.result
        self.assertEqual(state, InstrumentAgentState.UNINITIALIZED)
class TestIntExternalObservatoryAgent(IonIntegrationTestCase):

    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url(rel_url='res/deploy/r2eoi.yml')

        self.dams_cli = DataAcquisitionManagementServiceClient()
        self.dpms_cli = DataProductManagementServiceClient()

        eda = ExternalDatasetAgent()
        self.eda_id = self.dams_cli.create_external_dataset_agent(eda)

        eda_inst = ExternalDatasetAgentInstance()
        self.eda_inst_id = self.dams_cli.create_external_dataset_agent_instance(eda_inst, external_dataset_agent_id=self.eda_id)


        self._setup_ncom()
        proc_name = self.ncom_ds_id+'_worker'
        config = {}
        config['process']={'name':proc_name,'type':'agent'}
        config['process']['eoa']={'dataset_id':self.ncom_ds_id}
        pid = self.container.spawn_process(name=proc_name, module='ion.agents.eoi.external_observatory_agent', cls='ExternalObservatoryAgent', config=config)

        queue_id = "%s.%s" % (self.container.id, pid)

        log.debug("Spawned worker process ==> proc_name: %s\tproc_id: %s\tqueue_id: %s" % (proc_name, pid, queue_id))

        self._agent_cli = ResourceAgentClient(self.ncom_ds_id, name=pid, process=FakeProcess())
        log.debug("Got a ResourceAgentClient: res_id=%s" % self._agent_cli.resource_id)

    def _setup_ncom(self):
        # TODO: some or all of this (or some variation) should move to DAMS

        # Create and register the necessary resources/objects

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(), contact=ContactInformation())
        #        dprov.institution.name = "OOI CGSN"
        dprov.contact.name = "Robert Weller"
        dprov.contact.email = "*****@*****.**"

        # Create DataSource
        dsrc = DataSource(protocol_type="DAP", institution=Institution(), contact=ContactInformation())
        #        dsrc.connection_params["base_data_url"] = "http://ooi.whoi.edu/thredds/dodsC/"
        dsrc.connection_params["base_data_url"] = ""
        dsrc.contact.name="Rich Signell"
        dsrc.contact.email = "*****@*****.**"

        # Create ExternalDataset
        dset = ExternalDataset(name="test", dataset_description=DatasetDescription(), update_description=UpdateDescription(), contact=ContactInformation())

        #        dset.dataset_description.parameters["dataset_path"] = "ooi/AS02CPSM_R_M.nc"
        dset.dataset_description.parameters["dataset_path"] = "test_data/ncom.nc"
        dset.dataset_description.parameters["temporal_dimension"] = "time"
        dset.dataset_description.parameters["zonal_dimension"] = "lon"
        dset.dataset_description.parameters["meridional_dimension"] = "lat"

        # Create DataSourceModel
        dsrc_model = DataSourceModel(name="dap_model")
        dsrc_model.model = "DAP"
        dsrc_model.data_handler_module = "ion.agents.eoi.handler.dap_external_data_handler"
        dsrc_model.data_handler_class = "DapExternalDataHandler"

        ## Run everything through DAMS
        ds_id = self.ncom_ds_id = self.dams_cli.create_external_dataset(external_dataset=dset)
        ext_dprov_id = self.dams_cli.create_external_data_provider(external_data_provider=dprov)
        ext_dsrc_id = self.dams_cli.create_data_source(data_source=dsrc)
        ext_dsrc_model_id = self.dams_cli.create_data_source_model(dsrc_model)

        # Register the ExternalDataset
        dproducer_id = self.dams_cli.register_external_data_set(external_dataset_id=ds_id)

        ## Associate everything
        # Convenience method
#        self.dams_cli.assign_eoi_resources(external_data_provider_id=ext_dprov_id, data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id, external_dataset_id=ds_id, external_data_agent_id=self.eda_id, agent_instance_id=self.eda_inst_id)

        # Or using each method
        self.dams_cli.assign_data_source_to_external_data_provider(data_source_id=ext_dsrc_id, external_data_provider_id=ext_dprov_id)
        self.dams_cli.assign_data_source_to_data_model(data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id)
        self.dams_cli.assign_external_dataset_to_data_source(external_dataset_id=ds_id, data_source_id=ext_dsrc_id)
        self.dams_cli.assign_external_dataset_to_agent_instance(external_dataset_id=ds_id, agent_instance_id=self.eda_inst_id)
#        self.dams_cli.assign_external_data_agent_to_agent_instance(external_data_agent_id=self.eda_id, agent_instance_id=self.eda_inst_id)

        # Generate the data product and associate it to the ExternalDataset
        dprod = DataProduct(name='ncom_product', description='raw ncom product')
        dproduct_id = self.dpms_cli.create_data_product(data_product=dprod)

        self.dams_cli.assign_data_product(input_resource_id=ds_id, data_product_id=dproduct_id, create_stream=True)


########## Tests ##########

#    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_get_capabilities(self):
        # Get all the capabilities
        caps = self._agent_cli.get_capabilities()
        log.debug("all capabilities: %s" % caps)
        lst=[['RES_CMD', 'acquire_data'], ['RES_CMD', 'acquire_data_by_request'],
            ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'], ['RES_CMD', 'compare'],
            ['RES_CMD', 'get_attributes'], ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
            ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._agent_cli.get_capabilities(capability_types=['RES_CMD'])
        log.debug("resource commands: %s" % caps)
        lst=[['RES_CMD', 'acquire_data'], ['RES_CMD', 'acquire_data_by_request'],
            ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'], ['RES_CMD', 'compare'],
            ['RES_CMD', 'get_attributes'], ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
            ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._agent_cli.get_capabilities(capability_types=['RES_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._agent_cli.get_capabilities(capability_types=['AGT_CMD'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._agent_cli.get_capabilities(capability_types=['AGT_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_get_attrs(self):
        cmd = AgentCommand(command_id="111", command="get_attributes")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._agent_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)
class RunInstrument(MiIntTestCase):
    """
    Main class for communicating with an instrument
    """

    def __init__(self, monitor=False, subscriber=False):
        self.driver_make = None
        self.driver_model = None
        self.driver_name = None
        self.driver_class = DRIVER_CLASS
        self.ip_address = None
        self.data_port = None
        self.command_port = None
        self.driver_version = None
        self._pagent = None
        self.monitor_window = monitor
        self.subcriber_window = subscriber
        self.stream_config = {}
        
        self._cleanups = []

    def _initialize(self):
        """
        Start port agent, add port agent cleanup.
        Start container.
        Start deploy services.
        Define agent config, start agent.
        Start agent client.
        """
        
        try:

            """
            Get the information for the driver.  This can be read from the yml
            files; the user can run switch_driver to change the current driver.
            """ 
            self.fetch_metadata()
            self.fetch_driver_class()
            self.fetch_comm_config()
            
            if not exists(PIPE_PATH):
                mkfifo(PIPE_PATH)
    
            if not exists(self.metadata.driver_dir()):
                raise DriverDoesNotExist( "%s/%s/$%s" % (self.metadata.driver_make,
                                                         self.metadata.driver_model,
                                                         self.driver_name))        
            
            driver_module = DRIVER_MODULE_ROOT + self.metadata.driver_make + '.' + self.metadata.driver_model + '.' + self.metadata.driver_name + DRIVER_MODULE_LEAF
            
            log.info('driver module: %s', driver_module)
            log.info('driver class: %s', self.driver_class)
            log.info('device address: %s', self.ip_address)
            log.info('device data port: %s', self.data_port)
            log.info('device command port: %s', self.command_port)
            log.info('log delimiter: %s', DELIM)
            log.info('work dir: %s', WORK_DIR)
    
            DVR_CONFIG.update({'dvr_mod' : driver_module, 'dvr_cls' : self.driver_class})
    
            """
            self._support = DriverIntegrationTestSupport(driver_module,
                                                         self.driver_class,
                                                         self.ip_address,
                                                         self.data_port,
                                                         DELIM,
                                                         WORK_DIR)
            """
            
            # Start port agent, add stop to cleanup (not sure if that's
            # necessary yet).
            print( "------------------>>>> Starting Port Agent <<<<------------------" )
            self.start_pagent()
    
            # Start a monitor window if specified.
            if self.monitor_window:
                self.monitor_file = self._pagent.port_agent.logfname
                strXterm = "xterm -T InstrumentMonitor -sb -rightbar"
                #pOpenString = "xterm -T InstrumentMonitor -e tail -f " + self.monitor_file
                pOpenString = strXterm + " -e tail -f " + self.monitor_file
                
                x = subprocess.Popen(pOpenString, shell=True)        
    
            """
            DHE: Added self._cleanups to make base classes happy
            """
            self.addCleanup(self.stop_pagent)    
            
            # Start container.
            print( "------------------>>>> Starting Capability Container <<<<------------------" )
            self._start_container()
            
            # Bring up services in a deploy file (no need to message)
            print( "------------------>>>> Starting Deploy Services <<<<------------------" )
            self.container.start_rel_from_url('res/deploy/r2deploy.yml')
    
            # Setup stream config.
            self._build_stream_config()
            
            # Create agent config.
            agent_config = {
                'driver_config' : DVR_CONFIG,
                'stream_config' : self._stream_config,
                'agent'         : {'resource_id': IA_RESOURCE_ID},
                'test_mode' : True
            }
    
            # Start instrument agent.
            self._ia_pid = None
            print( "------------------>>>> Starting Instrument Agent <<<<------------------" )
            container_client = ContainerAgentClient(node=self.container.node,
                                                    name=self.container.name)
            self._ia_pid = container_client.spawn_process(name=IA_NAME,
                                                          module=IA_MOD, 
                                                          cls=IA_CLS, 
                                                          config=agent_config)      
            log.info('Agent pid=%s.', str(self._ia_pid))
            
            # Start a resource agent client to talk with the instrument agent.
            self._ia_client = None
            self._ia_client = ResourceAgentClient(IA_RESOURCE_ID,
                                                  process=FakeProcess())
            log.info('Got ia client %s.', str(self._ia_client))
            
            if self.subcriber_window:
                self._start_data_subscribers(6)
                #self.addCleanup(self._stop_data_subscribers)
                
        except:
            log.error("initialize(): Exception occurred; shutting down.", exc_info=True)
            return False
        
        else:
            return True

    ###############################################################################
    # Port agent helpers.
    ###############################################################################
        
    def start_pagent(self):
        """
        Construct and start the port agent.
        @retval port Port that was used for connection to agent
        """
        # Create port agent object.
        comm_config = self.comm_config

        config = {
            'device_addr' : comm_config.device_addr,
            'device_port' : comm_config.device_port,

            'command_port': comm_config.command_port,
            'data_port': comm_config.data_port,

            'process_type': PortAgentProcessType.UNIX,
            'log_level': 5,
        }

        self._pagent = PortAgentProcess.launch_process(config, timeout = 60, test_mode = True)
        pid = self._pagent.get_pid()
        port = self._pagent.get_data_port()

        log.info('Started port agent pid %d listening at port %d', pid, port)

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

        return port

    def stop_pagent(self):
        """
        Stop the port agent.
        """
        if self._pagent:
            pid = self._pagent.get_pid()
            if pid:
                log.info('Stopping pagent pid %i', pid)
                self._pagent.stop()
            else:
                log.info('No port agent running.')
       
    ###############################################################################
    # Data stream helpers.
    ###############################################################################

    def _build_stream_config(self):
        """
        """
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
        dataset_management = DatasetManagementServiceClient() 
           
        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}

        streams = {
            'parsed' : 'ctd_parsed_param_dict',
            'raw' : 'ctd_raw_param_dict'
        }

        for (stream_name, param_dict_name) in streams.iteritems():
            pd_id = dataset_management.read_parameter_dictionary_by_name(DEFAULT_PARAM_DICT, id_only=True)
            if (not pd_id):
                log.error("No pd_id found for param_dict '%s'" % DEFAULT_PARAM_DICT)

            stream_def_id = pubsub_client.create_stream_definition(name=stream_name,
                                                                   parameter_dictionary_id=pd_id)
            pd = None
            stream_id, stream_route = pubsub_client.create_stream(name=stream_name,
                                                exchange_point='science_data',
                                                stream_definition_id=stream_def_id)

            stream_config = dict(stream_route=stream_route,
                                 routing_key=stream_route.routing_key,
                                 exchange_point=stream_route.exchange_point,
                                 stream_id=stream_id,
                                 stream_definition_ref=stream_def_id,
                                 parameter_dictionary=pd)

            self.stream_config[stream_name] = stream_config    

    def _start_data_subscribers(self, count):
        """
        """        
        # Create a pubsub client to create streams.
        pubsub_client = PubsubManagementServiceClient(node=self.container.node)
                
        # Create streams and subscriptions for each stream named in driver.
        self._data_subscribers = []
        self._samples_received = []
        #self._async_data_result = AsyncResult()

        strXterm = "xterm -T InstrumentScienceData -sb -rightbar "
        pOpenString = strXterm + " -e tail -f " + PIPE_PATH
        subprocess.Popen(['xterm', '-T', 'InstrumentScienceData', '-e', 'tail', '-f', PIPE_PATH])        
        #subprocess.Popen(pOpenString)
                
        #self.pipeData = open(PIPE_PATH, "w", 1) 

        # A callback for processing subscribed-to data.
        def recv_data(message, stream_route, stream_id):
            print 'Received message on ' + str(stream_id) + ' (' + str(stream_route.exchange_point) + ',' + str(stream_route.routing_key) + ')'
            log.info('Received message on %s (%s,%s)', stream_id, stream_route.exchange_point, stream_route.routing_key)
             
            self.pipeData = open(PIPE_PATH, "w", 1) 
            self.pipeData.write(str(message))
            self.pipeData.flush()
            self.pipeData.close()      

            self._samples_received.append(message)
            #if len(self._samples_received) == count:
                #self._async_data_result.set()

        for (stream_name, stream_config) in self._stream_config.iteritems():
            
            stream_id = stream_config['stream_id']
            
            # Create subscriptions for each stream.

            exchange_name = '%s_queue' % stream_name
            self._purge_queue(exchange_name)
            sub = StandaloneStreamSubscriber(exchange_name, recv_data)
            sub.start()
            self._data_subscribers.append(sub)
            print 'stream_id: %s' % stream_id
            sub_id = pubsub_client.create_subscription(name=exchange_name, stream_ids=[stream_id])
            pubsub_client.activate_subscription(sub_id)
            sub.subscription_id = sub_id # Bind the subscription to the standalone subscriber (easier cleanup, not good in real practice)

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()
 
    def _stop_data_subscribers(self):
        for subscriber in self._data_subscribers:
            pubsub_client = PubsubManagementServiceClient()
            if hasattr(subscriber,'subscription_id'):
                try:
                    pubsub_client.deactivate_subscription(subscriber.subscription_id)
                except:
                    pass
                pubsub_client.delete_subscription(subscriber.subscription_id)
            subscriber.stop()

    def bring_instrument_active(self):
        """
        @brief Bring the agent up to COMMAND state, 
        """

        """
        DHE: Don't have an event subscriber yet

        # Set up a subscriber to collect error events.
        #self._start_event_subscriber('ResourceAgentResourceStateEvent', 6)
        #self.addCleanup(self._stop_event_subscriber)
        """    

        try:
            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)
        
            cmd = AgentCommand(command=ResourceAgentEvent.INITIALIZE)
            retval = self._ia_client.execute_agent(cmd)

            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)
            
            res_state = self._ia_client.get_resource_state()
            print "DriverState: " + str(res_state)
    
            cmd = AgentCommand(command=ResourceAgentEvent.GO_ACTIVE)
            retval = self._ia_client.execute_agent(cmd)

            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)
    
            res_state = self._ia_client.get_resource_state()
            print "DriverState: " + str(res_state)

            """
            If the agent is in STREAMING state, it will not accept the run
            command.
            """
            if state != ResourceAgentState.STREAMING:    
                cmd = AgentCommand(command=ResourceAgentEvent.RUN)
                retval = self._ia_client.execute_agent(cmd)

            state = self._ia_client.get_agent_state()
            print "AgentState: " + str(state)
            
            res_state = self._ia_client.get_resource_state()
            print "DriverState: " + str(res_state)
            
        except:
            log.error("bring_instrument_active(): Exception occurred; shutting down.", exc_info=True)
            return False
        
        else:
            return True

        """
        DHE: Don't have an event subscriber yet so we've received no events.
        self._async_event_result.get(timeout=2)
        print "Received: " + str(len(self._events_received)) + " events."
        """
                
    ###############################################################################
    # RunInstrument helpers.
    ###############################################################################
    def get_capabilities(self):
        """
        @brief Get exposed capabilities in current state.
        """
        
        retval = self._ia_client.get_capabilities()
        
        # Validate capabilities for state UNINITIALIZED.
        self.agt_cmds = [x.name for x in retval if x.cap_type==CapabilityType.AGT_CMD]
        self.agt_pars = [x.name for x in retval if x.cap_type==CapabilityType.AGT_PAR]
        self.res_cmds = [x.name for x in retval if x.cap_type==CapabilityType.RES_CMD]
        self.res_pars = [x.name for x in retval if x.cap_type==CapabilityType.RES_PAR]
        
        print "\n------------------>>>> Current Capabilities <<<<------------------"
        print "Agent Commands: " + str(self.agt_cmds)
        #print "Agent Parameters: " + str(self.agt_pars)
        print "Resource Commands: " + str(self.res_cmds)
        #print "Resource Parameters: " + str(self.res_pars)

    def send_agent_command(self, command):
        """
        @brief Send a command to the agent. 
        """
        
        DA_WAIT_PERIOD = 60
        waiting = False
        print "Input command: " + str(command)
        if command == 'RESOURCE_AGENT_EVENT_GO_DIRECT_ACCESS':
            cmd = AgentCommand(command = command, 
                               kwargs={'session_type': DirectAccessTypes.telnet,
                                       'session_timeout':600,
                                       'inactivity_timeout':600})
            waiting = True
        else:
            cmd = AgentCommand(command = command)
            
        retval = self._ia_client.execute_agent(cmd)
        print "Results of command: " + str(retval)
        while waiting:
            print "Waiting " + str(DA_WAIT_PERIOD) + " seconds for you to test direct access."
            gevent.sleep(DA_WAIT_PERIOD)
            still_waiting = prompt.text('Still waiting? (y/n)')
            if still_waiting is 'n':
                waiting = False

    def send_driver_command(self, command):
        """
        @brief Send a command to the instrument through the instrument agent.
        First determine whether it's a get or set, which are handled separately.
        """

        if command == DriverEvent.GET:
            self._get_param()
        elif command == DriverEvent.SET:
            self._set_param()
        else:
            print "Input command: " + str(command)
            cmd = AgentCommand(command = command)
            retval = self._ia_client.execute_resource(cmd)
            print "Results of command: " + str(retval)

    def _get_param(self):
        """
        @brief Get a single parameter from the instrument (will be updated to get 
        multiple later).
        """
        
        _all_params = self._ia_client.get_resource('DRIVER_PARAMETER_ALL')
        print "Parameters you can get are: " + str(_all_params)
        _param_valid = False
        while _param_valid is False:
            _param = prompt.text('\nEnter a single parameter')
            if _param in _all_params:
                _param_valid = True
            else:
                print 'Invalid parameter: ' + _param 
                
        reply = self._ia_client.get_resource([_param])
        print 'Reply is :' + str(reply)
                                                                    
    def _set_param(self):
        """
        @brief Set a single parameter
        """
        
        _all_params = self._ia_client.get_resource('DRIVER_PARAMETER_ALL')
        print "Parameters you can set are: " + str(_all_params)
        _param_valid = False
        while _param_valid is False:
            _param = prompt.text('\nEnter a single parameter')
            if _param in _all_params:
                _param_valid = True
            else:
                print 'Invalid parameter: ' + _param 

        _value = prompt.text('Enter value')
        _value = _value.lower()

        """
        DHE: Need to convert to native types here; can't be string; this is a 
        problem for the UI because we need a way to get the metadata about 
        each param to the UI.
        """        
        if _value == 'true':
            _value = True
        elif _value == 'false':
            _value = False

        param_dict = {_param: _value}
        self._ia_client.set_resource(param_dict)
                                                                            
    def fetch_metadata(self):
        """
        @brief collect metadata from the user
        """

        self.metadata = Metadata()
        self.driver_make = self.metadata.driver_make
        self.driver_model = self.metadata.driver_model
        self.driver_name = self.metadata.driver_name
        
        if not (self.driver_make and self.driver_model and self.driver_name):
            self.driver_make = prompt.text( 'Driver Make', self.driver_make )
            self.driver_model = prompt.text( 'Driver Model', self.driver_model )
            self.driver_name = prompt.text( 'Driver Name', self.driver_name )
            
        if not (self.driver_class):
            self.driver_class = prompt.text( 'Driver Class', self.driver_class )
            
        self.metadata = Metadata(self.driver_make, self.driver_model, self.driver_name)

    def fetch_comm_config(self):
        """
        @brief collect connection information for the logger from the user
        """

        config_path = "%s/%s" % (self.metadata.driver_dir(), CommConfig.config_filename())
        self.comm_config = CommConfig.get_config_from_console(config_path)
        self.comm_config.display_config()
        #self.comm_config.get_from_console()
        self.ip_address = self.comm_config.device_addr
        self.data_port = self.comm_config.data_port
        self.command_port = self.comm_config.command_port
        
        if not (self.ip_address):
            self.ip_address = prompt.text( 'Instrument IP Address', self.ip_address )
            
        if not (self.data_port):
            continuing = True
            while continuing:
                sport = prompt.text( 'Instrument Port', self.data_port )
                try:
                    self.data_port = int(sport)
                    continuing = False
                except ValueError as e:
                    print "Error converting port to number: " + str(e)
                    print "Please enter a valid port number.\n"

    def fetch_driver_class(self):
        self.driver_class = prompt.text( 'Driver Class', self.driver_class )
            
    def get_user_command(self, text='Enter command'):

        command = prompt.text(text)
        return command
            
    def run(self):
        """
        @brief Run it.
        """
        
        print( "------------------>>>> Starting RunInstrument <<<<------------------" )

        """
        initialize; returns True if successful, else False.
        """
        continuing = self._initialize()

        """
        bring_instrument_active; returns True if successful, else False
        """
        if (continuing):
            continuing = self.bring_instrument_active()
            
        PROMPT = 'Enter command (\'quit\' to exit)'
        text = PROMPT
        while continuing:
            try:
                """
                Get a list of the currently available capabilities
                """
                self.get_capabilities()
                command = self.get_user_command(text)
                text = PROMPT
                if command == 'quit':
                    continuing = False
                elif command in self.agt_cmds:
                    self.send_agent_command(command)
                elif command in self.res_cmds:
                    self.send_driver_command(command)
                else:
                    text = 'Invalid Command: ' + command + '\n' + PROMPT 
            except:
                log.error("run(): Exception occurred; shutting down.", exc_info=True)
                continuing = False
                
        self.stop_pagent()
        print( "------------------>>>> Stopping RunInstrument <<<<------------------" )
class TestIntExternalObservatoryAgentService(IonIntegrationTestCase):
    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url(rel_url='res/deploy/r2eoi.yml')

        #        self.eoas_cli = ExternalObservatoryAgentServiceClient()
        #        self.rr_cli = ResourceRegistryServiceClient()
        self.dams_cli = DataAcquisitionManagementServiceClient()
        self.dpms_cli = DataProductManagementServiceClient()

        self._setup_ncom()
        self._setup_hfr()

        #        eoas_proc = self.container.proc_manager.procs_by_name['external_data_agent_management']
        #        log.debug("Got EOAS Process: %s" % eoas_proc)
        self._ncom_agt_cli = ResourceAgentClient(
            resource_id=self.ncom_ds_id,
            name='external_observatory_agent',
            process=FakeProcess())
        log.debug("Got a ResourceAgentClient: res_id=%s" %
                  self._ncom_agt_cli.resource_id)

        self._hfr_agt_cli = ResourceAgentClient(
            resource_id=self.hfr_ds_id,
            name='external_observatory_agent',
            process=FakeProcess())
        log.debug("Got a ResourceAgentClient: res_id=%s" %
                  self._hfr_agt_cli.resource_id)

    def _setup_ncom(self):
        # TODO: some or all of this (or some variation) should move to DAMS

        # Create and register the necessary resources/objects

        eda = ExternalDatasetAgent()
        eda_id = self.dams_cli.create_external_dataset_agent(eda)

        eda_inst = ExternalDatasetAgentInstance()
        eda_inst_id = self.dams_cli.create_external_dataset_agent_instance(
            eda_inst, external_dataset_agent_id=eda_id)

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(),
                                     contact=ContactInformation())
        #        dprov.institution.name = "OOI CGSN"
        dprov.contact.name = "Robert Weller"
        dprov.contact.email = "*****@*****.**"

        # Create DataSource
        dsrc = DataSource(protocol_type="DAP",
                          institution=Institution(),
                          contact=ContactInformation())
        #        dsrc.connection_params["base_data_url"] = "http://ooi.whoi.edu/thredds/dodsC/"
        dsrc.connection_params["base_data_url"] = ""
        dsrc.contact.name = "Rich Signell"
        dsrc.contact.email = "*****@*****.**"

        # Create ExternalDataset
        dset = ExternalDataset(name="test",
                               dataset_description=DatasetDescription(),
                               update_description=UpdateDescription(),
                               contact=ContactInformation())

        #        dset.dataset_description.parameters["dataset_path"] = "ooi/AS02CPSM_R_M.nc"
        dset.dataset_description.parameters[
            "dataset_path"] = "test_data/ncom.nc"
        dset.dataset_description.parameters["temporal_dimension"] = "time"
        dset.dataset_description.parameters["zonal_dimension"] = "lon"
        dset.dataset_description.parameters["meridional_dimension"] = "lat"

        # Create DataSourceModel
        dsrc_model = DataSourceModel(name="dap_model")
        dsrc_model.model = "DAP"
        dsrc_model.data_handler_module = "eoi.agent.handler.dap_external_data_handler"
        dsrc_model.data_handler_class = "DapExternalDataHandler"

        ## Run everything through DAMS
        ds_id = self.ncom_ds_id = self.dams_cli.create_external_dataset(
            external_dataset=dset)
        ext_dprov_id = self.dams_cli.create_external_data_provider(
            external_data_provider=dprov)
        ext_dsrc_id = self.dams_cli.create_data_source(data_source=dsrc)
        ext_dsrc_model_id = self.dams_cli.create_data_source_model(dsrc_model)

        # Register the ExternalDataset
        dproducer_id = self.dams_cli.register_external_data_set(
            external_dataset_id=ds_id)

        ## Associate everything
        # Convenience method
        #        self.dams_cli.assign_eoi_resources(external_data_provider_id=ext_dprov_id, data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id, external_dataset_id=ds_id, external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Or using each method
        self.dams_cli.assign_data_source_to_external_data_provider(
            data_source_id=ext_dsrc_id, external_data_provider_id=ext_dprov_id)
        self.dams_cli.assign_data_source_to_data_model(
            data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id)
        self.dams_cli.assign_external_dataset_to_data_source(
            external_dataset_id=ds_id, data_source_id=ext_dsrc_id)
        self.dams_cli.assign_external_dataset_to_agent_instance(
            external_dataset_id=ds_id, agent_instance_id=eda_inst_id)
        #        self.dams_cli.assign_external_dataset_agent_to_data_model(external_data_agent_id=eda_id, data_source_model_id=ext_dsrc_model_id)
        #        self.dams_cli.assign_external_data_agent_to_agent_instance(external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Generate the data product and associate it to the ExternalDataset
        dprod = DataProduct(name='ncom_product',
                            description='raw ncom product')
        dproduct_id = self.dpms_cli.create_data_product(data_product=dprod)

        self.dams_cli.assign_data_product(input_resource_id=ds_id,
                                          data_product_id=dproduct_id,
                                          create_stream=True)

    def _setup_hfr(self):
        # TODO: some or all of this (or some variation) should move to DAMS

        # Create and register the necessary resources/objects

        eda = ExternalDatasetAgent()
        eda_id = self.dams_cli.create_external_dataset_agent(eda)

        eda_inst = ExternalDatasetAgentInstance()
        eda_inst_id = self.dams_cli.create_external_dataset_agent_instance(
            eda_inst, external_dataset_agent_id=eda_id)

        # Create DataProvider
        dprov = ExternalDataProvider(institution=Institution(),
                                     contact=ContactInformation())
        #        dprov.institution.name = "HFR UCSD"

        # Create DataSource
        dsrc = DataSource(protocol_type="DAP",
                          institution=Institution(),
                          contact=ContactInformation())
        dsrc.connection_params[
            "base_data_url"] = "http://hfrnet.ucsd.edu:8080/thredds/dodsC/"

        # Create ExternalDataset
        dset = ExternalDataset(name="UCSD HFR",
                               dataset_description=DatasetDescription(),
                               update_description=UpdateDescription(),
                               contact=ContactInformation())
        dset.dataset_description.parameters[
            "dataset_path"] = "HFRNet/USEGC/6km/hourly/RTV"
        #        dset.dataset_description.parameters["dataset_path"] = "test_data/hfr.nc"
        dset.dataset_description.parameters["temporal_dimension"] = "time"
        dset.dataset_description.parameters["zonal_dimension"] = "lon"
        dset.dataset_description.parameters["meridional_dimension"] = "lat"

        # Create DataSourceModel
        dsrc_model = DataSourceModel(name="dap_model")
        dsrc_model.model = "DAP"
        dsrc_model.data_handler_module = "eoi.agent.handler.dap_external_data_handler"
        dsrc_model.data_handler_class = "DapExternalDataHandler"

        ## Run everything through DAMS
        ds_id = self.hfr_ds_id = self.dams_cli.create_external_dataset(
            external_dataset=dset)
        ext_dprov_id = self.dams_cli.create_external_data_provider(
            external_data_provider=dprov)
        ext_dsrc_id = self.dams_cli.create_data_source(data_source=dsrc)
        ext_dsrc_model_id = self.dams_cli.create_data_source_model(dsrc_model)

        # Register the ExternalDataset
        dproducer_id = self.dams_cli.register_external_data_set(
            external_dataset_id=ds_id)

        ## Associate everything
        # Convenience method
        #        self.dams_cli.assign_eoi_resources(external_data_provider_id=ext_dprov_id, data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id, external_dataset_id=ds_id, external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Or using each method
        self.dams_cli.assign_data_source_to_external_data_provider(
            data_source_id=ext_dsrc_id, external_data_provider_id=ext_dprov_id)
        self.dams_cli.assign_data_source_to_data_model(
            data_source_id=ext_dsrc_id, data_source_model_id=ext_dsrc_model_id)
        self.dams_cli.assign_external_dataset_to_data_source(
            external_dataset_id=ds_id, data_source_id=ext_dsrc_id)
        self.dams_cli.assign_external_dataset_to_agent_instance(
            external_dataset_id=ds_id, agent_instance_id=eda_inst_id)
        #        self.dams_cli.assign_external_dataset_agent_to_data_model(external_data_agent_id=eda_id, data_source_model_id=ext_dsrc_model_id)
        #        self.dams_cli.assign_external_data_agent_to_agent_instance(external_data_agent_id=eda_id, agent_instance_id=eda_inst_id)

        # Generate the data product and associate it to the ExternalDataset
        dprod = DataProduct(name='hfr_product', description='raw hfr product')
        dproduct_id = self.dpms_cli.create_data_product(data_product=dprod)

        self.dams_cli.assign_data_product(input_resource_id=ds_id,
                                          data_product_id=dproduct_id,
                                          create_stream=True)

########## Tests ##########

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_get_capabilities(self):
        # Get all the capabilities
        caps = self._ncom_agt_cli.get_capabilities()
        log.debug("all capabilities: %s" % caps)
        lst = [['RES_CMD', 'acquire_data'],
               ['RES_CMD', 'acquire_data_by_request'],
               ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'],
               ['RES_CMD', 'compare'], ['RES_CMD', 'get_attributes'],
               ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
               ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._ncom_agt_cli.get_capabilities(
            capability_types=['RES_CMD'])
        log.debug("resource commands: %s" % caps)
        lst = [['RES_CMD', 'acquire_data'],
               ['RES_CMD', 'acquire_data_by_request'],
               ['RES_CMD', 'acquire_new_data'], ['RES_CMD', 'close'],
               ['RES_CMD', 'compare'], ['RES_CMD', 'get_attributes'],
               ['RES_CMD', 'get_fingerprint'], ['RES_CMD', 'get_status'],
               ['RES_CMD', 'has_new_data']]
        self.assertEquals(caps, lst)

        caps = self._ncom_agt_cli.get_capabilities(
            capability_types=['RES_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._ncom_agt_cli.get_capabilities(
            capability_types=['AGT_CMD'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

        caps = self._ncom_agt_cli.get_capabilities(
            capability_types=['AGT_PAR'])
        log.debug("resource commands: %s" % caps)
        self.assertEqual(type(caps), list)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_get_attrs(self):
        cmd = AgentCommand(command_id="111", command="get_attributes")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_get_fingerprint(self):
        cmd = AgentCommand(command_id="111", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_single_worker(self):
        cmd = AgentCommand(command_id="111", command="get_attributes")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="112", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_multi_worker(self):
        cmd = AgentCommand(command_id="111", command="has_new_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="111", command="has_new_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._hfr_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="112", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

        cmd = AgentCommand(command_id="112", command="get_fingerprint")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._hfr_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)
        self.assertTrue(type(ret.result), dict)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_acquire_data(self):
        cmd = AgentCommand(command_id="113", command="acquire_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)

    @unittest.skip("Currently broken due to resource/agent refactorings")
    def test_execute_acquire_new_data(self):
        cmd = AgentCommand(command_id="113", command="acquire_new_data")
        log.debug("Execute AgentCommand: %s" % cmd)
        ret = self._ncom_agt_cli.execute(cmd)
        log.debug("Returned: %s" % ret)
        self.assertEquals(ret.status, 0)

    @unittest.skip("Underlying method not yet implemented")
    def test_set_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.set_param(resource_id=res_id,
                                    name="param",
                                    value="value")

    @unittest.skip("Underlying method not yet implemented")
    def test_get_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.get_param(resource_id=res_id, name="param")

    @unittest.skip("Underlying method not yet implemented")
    def test_execute_agent(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.execute_agent(resource_id=res_id)

    @unittest.skip("Underlying method not yet implemented")
    def test_set_agent_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.set_agent_param(resource_id=res_id,
                                          name="param",
                                          value="value")

    @unittest.skip("Underlying method not yet implemented")
    def test_get_agent_param(self):
        res_id = self.ncom_ds_id

        log.debug("test_get_worker with res_id: %s" % res_id)
        res = self.eoas_cli.get_worker(res_id)

        with self.assertRaises(IonException):
            self.eoas_cli.get_agent_param(resource_id=res_id, name="param")
class TestPlatformAgent(IonIntegrationTestCase, HelperTestMixin):
    @classmethod
    def setUpClass(cls):
        HelperTestMixin.setUpClass()

        # Use the network definition provided by RSN OMS directly.
        rsn_oms = CIOMSClientFactory.create_instance(DVR_CONFIG['oms_uri'])
        network_definition = RsnOmsUtil.build_network_definition(rsn_oms)
        network_definition_ser = NetworkUtil.serialize_network_definition(
            network_definition)
        if log.isEnabledFor(logging.DEBUG):
            log.debug("NetworkDefinition serialization:\n%s",
                      network_definition_ser)

        cls.PLATFORM_CONFIG = {
            'platform_id': cls.PLATFORM_ID,
            'driver_config': DVR_CONFIG,
            'network_definition': network_definition_ser
        }

        NetworkUtil._gen_open_diagram(
            network_definition.pnodes[cls.PLATFORM_ID])

    def setUp(self):
        self._start_container()
        self.container.start_rel_from_url('res/deploy/r2deploy.yml')

        self._pubsub_client = PubsubManagementServiceClient(
            node=self.container.node)

        # Start data subscribers, add stop to cleanup.
        # Define stream_config.
        self._async_data_result = AsyncResult()
        self._data_greenlets = []
        self._stream_config = {}
        self._samples_received = []
        self._data_subscribers = []
        self._start_data_subscribers()
        self.addCleanup(self._stop_data_subscribers)

        # start event subscriber:
        self._async_event_result = AsyncResult()
        self._event_subscribers = []
        self._events_received = []
        self.addCleanup(self._stop_event_subscribers)
        self._start_event_subscriber()

        self._agent_config = {
            'agent': {
                'resource_id': PA_RESOURCE_ID
            },
            'stream_config': self._stream_config,

            # pass platform config here
            'platform_config': self.PLATFORM_CONFIG
        }

        if log.isEnabledFor(logging.TRACE):
            log.trace("launching with agent_config=%s" %
                      str(self._agent_config))

        self._launcher = LauncherFactory.createLauncher()
        self._pid = self._launcher.launch(self.PLATFORM_ID, self._agent_config)

        log.debug("LAUNCHED PLATFORM_ID=%r", self.PLATFORM_ID)

        # Start a resource agent client to talk with the agent.
        self._pa_client = ResourceAgentClient(PA_RESOURCE_ID,
                                              process=FakeProcess())
        log.info('Got pa client %s.' % str(self._pa_client))

    def tearDown(self):
        try:
            self._launcher.cancel_process(self._pid)
        finally:
            super(TestPlatformAgent, self).tearDown()

    def _start_data_subscribers(self):
        """
        """

        # Create streams and subscriptions for each stream named in driver.
        self._stream_config = {}
        self._data_subscribers = []

        #
        # TODO retrieve appropriate stream definitions; for the moment, using
        # adhoc_get_stream_names
        #

        # A callback for processing subscribed-to data.
        def consume_data(message, stream_route, stream_id):
            log.info('Subscriber received data message: %s.' % str(message))
            self._samples_received.append(message)
            self._async_data_result.set()

        for stream_name in adhoc_get_stream_names():
            log.info('creating stream %r ...', stream_name)

            # TODO use appropriate exchange_point
            stream_id, stream_route = self._pubsub_client.create_stream(
                name=stream_name, exchange_point='science_data')

            log.info('create_stream(%r): stream_id=%r, stream_route=%s',
                     stream_name, stream_id, str(stream_route))

            pdict = adhoc_get_parameter_dictionary(stream_name)
            stream_config = dict(stream_route=stream_route.routing_key,
                                 stream_id=stream_id,
                                 parameter_dictionary=pdict.dump())

            self._stream_config[stream_name] = stream_config
            log.info('_stream_config[%r]= %r', stream_name, stream_config)

            # Create subscriptions for each stream.
            exchange_name = '%s_queue' % stream_name
            self._purge_queue(exchange_name)
            sub = StandaloneStreamSubscriber(exchange_name, consume_data)
            sub.start()
            self._data_subscribers.append(sub)
            sub_id = self._pubsub_client.create_subscription(
                name=exchange_name, stream_ids=[stream_id])
            self._pubsub_client.activate_subscription(sub_id)
            sub.subscription_id = sub_id

    def _purge_queue(self, queue):
        xn = self.container.ex_manager.create_xn_queue(queue)
        xn.purge()

    def _stop_data_subscribers(self):
        """
        Stop the data subscribers on cleanup.
        """
        for sub in self._data_subscribers:
            if hasattr(sub, 'subscription_id'):
                try:
                    self._pubsub_client.deactivate_subscription(
                        sub.subscription_id)
                except:
                    pass
                self._pubsub_client.delete_subscription(sub.subscription_id)
            sub.stop()

    def _start_event_subscriber(self,
                                event_type="DeviceEvent",
                                sub_type="platform_event"):
        """
        Starts event subscriber for events of given event_type ("DeviceEvent"
        by default) and given sub_type ("platform_event" by default).
        """
        def consume_event(evt, *args, **kwargs):
            # A callback for consuming events.
            log.info('Event subscriber received evt: %s.', str(evt))
            self._events_received.append(evt)
            self._async_event_result.set(evt)

        sub = EventSubscriber(event_type=event_type,
                              sub_type=sub_type,
                              callback=consume_event)

        sub.start()
        log.info("registered event subscriber for event_type=%r, sub_type=%r",
                 event_type, sub_type)

        self._event_subscribers.append(sub)
        sub._ready_event.wait(timeout=EVENT_TIMEOUT)

    def _stop_event_subscribers(self):
        """
        Stops the event subscribers on cleanup.
        """
        try:
            for sub in self._event_subscribers:
                if hasattr(sub, 'subscription_id'):
                    try:
                        self.pubsubcli.deactivate_subscription(
                            sub.subscription_id)
                    except:
                        pass
                    self.pubsubcli.delete_subscription(sub.subscription_id)
                sub.stop()
        finally:
            self._event_subscribers = []

    def _get_state(self):
        state = self._pa_client.get_agent_state()
        return state

    def _assert_state(self, state):
        self.assertEquals(self._get_state(), state)

    #def _execute_agent(self, cmd, timeout=TIMEOUT):
    def _execute_agent(self, cmd):
        log.info("_execute_agent: cmd=%r kwargs=%r ...", cmd.command,
                 cmd.kwargs)
        time_start = time.time()
        #retval = self._pa_client.execute_agent(cmd, timeout=timeout)
        retval = self._pa_client.execute_agent(cmd)
        elapsed_time = time.time() - time_start
        log.info("_execute_agent: cmd=%r elapsed_time=%s, retval = %s",
                 cmd.command, elapsed_time, str(retval))
        return retval

    def _reset(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RESET)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.UNINITIALIZED)

    def _ping_agent(self):
        retval = self._pa_client.ping_agent()
        self.assertIsInstance(retval, str)

    def _ping_resource(self):
        cmd = AgentCommand(command=PlatformAgentEvent.PING_RESOURCE)
        if self._get_state() == PlatformAgentState.UNINITIALIZED:
            # should get ServerError: "Command not handled in current state"
            with self.assertRaises(ServerError):
                #self._pa_client.execute_agent(cmd, timeout=TIMEOUT)
                self._pa_client.execute_agent(cmd)
        else:
            # In all other states the command should be accepted:
            retval = self._execute_agent(cmd)
            self.assertEquals("PONG", retval.result)

    def _get_metadata(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_METADATA)
        retval = self._execute_agent(cmd)
        md = retval.result
        self.assertIsInstance(md, dict)
        # TODO verify possible subset of required entries in the dict.
        log.info("GET_METADATA = %s", md)

    def _get_ports(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_PORTS)
        retval = self._execute_agent(cmd)
        md = retval.result
        self.assertIsInstance(md, dict)
        # TODO verify possible subset of required entries in the dict.
        log.info("GET_PORTS = %s", md)

    def _connect_instrument(self):
        #
        # TODO more realistic settings for the connection
        #
        port_id = self.PORT_ID
        instrument_id = self.INSTRUMENT_ID
        instrument_attributes = self.INSTRUMENT_ATTRIBUTES_AND_VALUES

        kwargs = dict(port_id=port_id,
                      instrument_id=instrument_id,
                      attributes=instrument_attributes)
        cmd = AgentCommand(command=PlatformAgentEvent.CONNECT_INSTRUMENT,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("CONNECT_INSTRUMENT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], dict)
        returned_attrs = self._verify_valid_instrument_id(
            instrument_id, result[port_id])
        if isinstance(returned_attrs, dict):
            for attrName in instrument_attributes:
                self.assertTrue(attrName in returned_attrs)

    def _get_connected_instruments(self):
        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id, )
        cmd = AgentCommand(
            command=PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("GET_CONNECTED_INSTRUMENTS = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], dict)
        instrument_id = self.INSTRUMENT_ID
        self.assertTrue(instrument_id in result[port_id])

    def _disconnect_instrument(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID
        instrument_id = self.INSTRUMENT_ID

        kwargs = dict(port_id=port_id, instrument_id=instrument_id)
        cmd = AgentCommand(command=PlatformAgentEvent.DISCONNECT_INSTRUMENT,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("DISCONNECT_INSTRUMENT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertIsInstance(result[port_id], dict)
        self.assertTrue(instrument_id in result[port_id])
        self._verify_instrument_disconnected(instrument_id,
                                             result[port_id][instrument_id])

    def _turn_on_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.TURN_ON_PORT,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("TURN_ON_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertEquals(result[port_id], NormalResponse.PORT_TURNED_ON)

    def _turn_off_port(self):
        # TODO real settings and corresp verification

        port_id = self.PORT_ID

        kwargs = dict(port_id=port_id)
        cmd = AgentCommand(command=PlatformAgentEvent.TURN_OFF_PORT,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        result = retval.result
        log.info("TURN_OFF_PORT = %s", result)
        self.assertIsInstance(result, dict)
        self.assertTrue(port_id in result)
        self.assertEquals(result[port_id], NormalResponse.PORT_TURNED_OFF)

    def _get_resource(self):
        attrNames = self.ATTR_NAMES
        #
        # OOIION-631: use get_ion_ts() as a basis for using system time, which is
        # a string.
        #
        cur_time = get_ion_ts()
        from_time = str(int(cur_time) - 50000)  # a 50-sec time window
        kwargs = dict(attr_names=attrNames, from_time=from_time)
        cmd = AgentCommand(command=PlatformAgentEvent.GET_RESOURCE,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attr_name in attrNames:
            self._verify_valid_attribute_id(attr_name, attr_values)

    def _set_resource(self):
        attrNames = self.ATTR_NAMES
        writ_attrNames = self.WRITABLE_ATTR_NAMES

        # do valid settings:

        # TODO more realistic value depending on attribute's type
        attrs = [(attrName, self.VALID_ATTR_VALUE) for attrName in attrNames]
        log.info("%r: setting attributes=%s", self.PLATFORM_ID, attrs)
        kwargs = dict(attrs=attrs)
        cmd = AgentCommand(command=PlatformAgentEvent.SET_RESOURCE,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attrName in attrNames:
            if attrName in writ_attrNames:
                self._verify_valid_attribute_id(attrName, attr_values)
            else:
                self._verify_not_writable_attribute_id(attrName, attr_values)

        # try invalid settings:

        # set invalid values to writable attributes:
        attrs = [(attrName, self.INVALID_ATTR_VALUE)
                 for attrName in writ_attrNames]
        log.info("%r: setting attributes=%s", self.PLATFORM_ID, attrs)
        kwargs = dict(attrs=attrs)
        cmd = AgentCommand(command=PlatformAgentEvent.SET_RESOURCE,
                           kwargs=kwargs)
        retval = self._execute_agent(cmd)
        attr_values = retval.result
        self.assertIsInstance(attr_values, dict)
        for attrName in writ_attrNames:
            self._verify_attribute_value_out_of_range(attrName, attr_values)

    def _initialize(self):
        self._assert_state(PlatformAgentState.UNINITIALIZED)
        cmd = AgentCommand(command=PlatformAgentEvent.INITIALIZE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.INACTIVE)

    def _go_active(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GO_ACTIVE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.IDLE)

    def _run(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RUN)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _pause(self):
        cmd = AgentCommand(command=PlatformAgentEvent.PAUSE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.STOPPED)

    def _resume(self):
        cmd = AgentCommand(command=PlatformAgentEvent.RESUME)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _start_resource_monitoring(self):
        cmd = AgentCommand(command=PlatformAgentEvent.START_MONITORING)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.MONITORING)

    def _wait_for_a_data_sample(self):
        log.info("waiting for reception of a data sample...")
        # just wait for at least one -- see consume_data
        self._async_data_result.get(timeout=DATA_TIMEOUT)
        self.assertTrue(len(self._samples_received) >= 1)
        log.info("Received samples: %s", len(self._samples_received))

    def _stop_resource_monitoring(self):
        cmd = AgentCommand(command=PlatformAgentEvent.STOP_MONITORING)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.COMMAND)

    def _go_inactive(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GO_INACTIVE)
        retval = self._execute_agent(cmd)
        self._assert_state(PlatformAgentState.INACTIVE)

    def _get_subplatform_ids(self):
        cmd = AgentCommand(command=PlatformAgentEvent.GET_SUBPLATFORM_IDS)
        retval = self._execute_agent(cmd)
        self.assertIsInstance(retval.result, list)
        self.assertTrue(x in retval.result for x in self.SUBPLATFORM_IDS)
        return retval.result

    def _wait_for_external_event(self):
        log.info("waiting for reception of an external event...")
        # just wait for at least one -- see consume_event
        self._async_event_result.get(timeout=EVENT_TIMEOUT)
        self.assertTrue(len(self._events_received) >= 1)
        log.info("Received events: %s", len(self._events_received))

    def _check_sync(self):
        cmd = AgentCommand(command=PlatformAgentEvent.CHECK_SYNC)
        retval = self._execute_agent(cmd)
        log.info("CHECK_SYNC result: %s", retval.result)
        self.assertTrue(retval.result is not None)
        self.assertEquals(retval.result[0:3], "OK:")
        return retval.result

    def test_capabilities(self):

        agt_cmds_all = [
            PlatformAgentEvent.INITIALIZE,
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GO_ACTIVE,
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RUN,
            PlatformAgentEvent.CLEAR,
            PlatformAgentEvent.PAUSE,
            PlatformAgentEvent.RESUME,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.CONNECT_INSTRUMENT,
            PlatformAgentEvent.DISCONNECT_INSTRUMENT,
            PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.START_MONITORING,
            PlatformAgentEvent.STOP_MONITORING,
            PlatformAgentEvent.CHECK_SYNC,
        ]

        def sort_caps(caps):
            agt_cmds = []
            agt_pars = []
            res_cmds = []
            res_pars = []

            if len(caps) > 0 and isinstance(caps[0], AgentCapability):
                agt_cmds = [
                    x.name for x in caps
                    if x.cap_type == CapabilityType.AGT_CMD
                ]
                agt_pars = [
                    x.name for x in caps
                    if x.cap_type == CapabilityType.AGT_PAR
                ]
                res_cmds = [
                    x.name for x in caps
                    if x.cap_type == CapabilityType.RES_CMD
                ]
                res_pars = [
                    x.name for x in caps
                    if x.cap_type == CapabilityType.RES_PAR
                ]

            elif len(caps) > 0 and isinstance(caps[0], dict):
                agt_cmds = [
                    x['name'] for x in caps
                    if x['cap_type'] == CapabilityType.AGT_CMD
                ]
                agt_pars = [
                    x['name'] for x in caps
                    if x['cap_type'] == CapabilityType.AGT_PAR
                ]
                res_cmds = [
                    x['name'] for x in caps
                    if x['cap_type'] == CapabilityType.RES_CMD
                ]
                res_pars = [
                    x['name'] for x in caps
                    if x['cap_type'] == CapabilityType.RES_PAR
                ]

            return agt_cmds, agt_pars, res_cmds, res_pars

        agt_pars_all = ['example'
                        ]  # 'cause ResourceAgent defines aparam_example
        res_pars_all = []
        res_cmds_all = []

        ##################################################################
        # UNINITIALIZED
        ##################################################################

        self._assert_state(PlatformAgentState.UNINITIALIZED)

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state UNINITIALIZED.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_uninitialized = [
            PlatformAgentEvent.INITIALIZE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]
        self.assertItemsEqual(agt_cmds, agt_cmds_uninitialized)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states.
        retval = self._pa_client.get_capabilities(current_state=False)

        # Validate all capabilities as read from state UNINITIALIZED.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        ##################################################################
        # INACTIVE
        ##################################################################
        self._initialize()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state INACTIVE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_inactive = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GO_ACTIVE,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        self.assertItemsEqual(agt_cmds, agt_cmds_inactive)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state INACTIVE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        ##################################################################
        # IDLE
        ##################################################################
        self._go_active()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities for state IDLE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_idle = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RUN,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        self.assertItemsEqual(agt_cmds, agt_cmds_idle)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        # Get exposed capabilities in all states as read from IDLE.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state IDLE.
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, [])
        self.assertItemsEqual(res_pars, [])

        ##################################################################
        # COMMAND
        ##################################################################
        self._run()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state COMMAND
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_command = [
            PlatformAgentEvent.GO_INACTIVE,
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.PAUSE,
            PlatformAgentEvent.CLEAR,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.CONNECT_INSTRUMENT,
            PlatformAgentEvent.DISCONNECT_INSTRUMENT,
            PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.START_MONITORING,
            PlatformAgentEvent.CHECK_SYNC,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_command)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        ##################################################################
        # STOPPED
        ##################################################################
        self._pause()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state STOPPED
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_stopped = [
            PlatformAgentEvent.RESUME,
            PlatformAgentEvent.CLEAR,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_stopped)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        # back to COMMAND:
        self._resume()

        ##################################################################
        # MONITORING
        ##################################################################
        self._start_resource_monitoring()

        # Get exposed capabilities in current state.
        retval = self._pa_client.get_capabilities()

        # Validate capabilities of state MONITORING
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        agt_cmds_monitoring = [
            PlatformAgentEvent.RESET,
            PlatformAgentEvent.GET_METADATA,
            PlatformAgentEvent.GET_PORTS,
            PlatformAgentEvent.CONNECT_INSTRUMENT,
            PlatformAgentEvent.DISCONNECT_INSTRUMENT,
            PlatformAgentEvent.GET_CONNECTED_INSTRUMENTS,
            PlatformAgentEvent.TURN_ON_PORT,
            PlatformAgentEvent.TURN_OFF_PORT,
            PlatformAgentEvent.GET_SUBPLATFORM_IDS,
            PlatformAgentEvent.GET_RESOURCE_CAPABILITIES,
            PlatformAgentEvent.PING_RESOURCE,
            PlatformAgentEvent.GET_RESOURCE,
            PlatformAgentEvent.SET_RESOURCE,
            PlatformAgentEvent.STOP_MONITORING,
            PlatformAgentEvent.CHECK_SYNC,
        ]

        res_cmds_command = []

        self.assertItemsEqual(agt_cmds, agt_cmds_monitoring)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_command)
        self.assertItemsEqual(res_pars, res_pars_all)

        # return to COMMAND state:
        self._stop_resource_monitoring()

        ###################
        # ALL CAPABILITIES
        ###################

        # Get exposed capabilities in all states as read from state COMMAND.
        retval = self._pa_client.get_capabilities(False)

        # Validate all capabilities as read from state COMMAND
        agt_cmds, agt_pars, res_cmds, res_pars = sort_caps(retval)

        self.assertItemsEqual(agt_cmds, agt_cmds_all)
        self.assertItemsEqual(agt_pars, agt_pars_all)
        self.assertItemsEqual(res_cmds, res_cmds_all)
        self.assertItemsEqual(res_pars, res_pars_all)

        self._go_inactive()
        self._reset()

    def test_some_state_transitions(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._initialize()  # -> INACTIVE
        self._reset()  # -> UNINITIALIZED
        self._initialize()  # -> INACTIVE
        self._go_active()  # -> IDLE
        self._reset()  # -> UNINITIALIZED
        self._initialize()  # -> INACTIVE
        self._go_active()  # -> IDLE
        self._run()  # -> COMMAND
        self._pause()  # -> STOPPED
        self._resume()  # -> COMMAND

        self._reset()  # -> UNINITIALIZED

    def test_get_set_resources(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._get_resource()
        self._set_resource()

        self._go_inactive()
        self._reset()

    def test_some_commands(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._ping_agent()
        self._ping_resource()

        self._get_metadata()
        self._get_ports()
        self._get_subplatform_ids()

        self._go_inactive()
        self._reset()

    def test_resource_monitoring(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._start_resource_monitoring()
        self._wait_for_a_data_sample()
        self._stop_resource_monitoring()

        self._go_inactive()
        self._reset()

    def test_external_event_dispatch(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._wait_for_external_event()

        self._go_inactive()
        self._reset()

    def test_connect_disconnect_instrument(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._connect_instrument()
        self._turn_on_port()

        self._get_connected_instruments()

        self._turn_off_port()
        self._disconnect_instrument()

        self._go_inactive()
        self._reset()

    def test_check_sync(self):

        self._assert_state(PlatformAgentState.UNINITIALIZED)
        self._ping_agent()

        self._initialize()
        self._go_active()
        self._run()

        self._check_sync()

        self._connect_instrument()
        self._check_sync()

        self._disconnect_instrument()
        self._check_sync()

        self._go_inactive()
        self._reset()