Example #1
0
class KnowledgeUpdateBehaviour(BehaviourBase):
    '''
    Behaviour that updates knowledge after it is activated
    '''
    def __init__(self,
                 name,
                 pattern,
                 new_tuple,
                 knowledge_base_name=KnowledgeBase.DEFAULT_NAME,
                 **kwargs):
        """
        :param name: behaviour name
        :param pattern: pattern match that will be updated
        :param new_tuple: new tuple value that will replaced the matched tuples
        :param knowledge_base_name: name of the knowledge base that is used
        :param kwargs: further general BehaviourBase arguments
        """

        super(KnowledgeUpdateBehaviour, self) \
            .__init__(name=name, **kwargs)

        self.pattern = pattern
        self.new_tuple = new_tuple

        self._kb_client = KnowledgeBaseClient(
            knowledge_base_name=knowledge_base_name)

    def start(self):
        rhbplog.logdebug("Updating knowledge '%s' to '%s'", self.pattern,
                         self.new_tuple)

        self._kb_client.update(self.pattern, self.new_tuple)
Example #2
0
    def __init__(self, pattern, knowledge_base_name=KnowledgeBase.DEFAULT_NAME, timeout=None):
        """
        :param pattern: tuple of strings. The update handler caches all facts, matching the pattern.
        :param knowledge_base_name:
        :param timeout: Timeout for all kb service calls in seconds, default is blocking/endless
        """

        self.__knowledge_base_name = knowledge_base_name
        self.__service_timeout = timeout
        self.__pattern = pattern
        self.__initialized = False
        self.__contained_facts = []
        self.__example_service_name = knowledge_base_name + KnowledgeBase.UPDATE_SERVICE_NAME_POSTFIX
        self.__client = KnowledgeBaseClient(knowledge_base_name)
        self.__value_lock = threading.Lock()
        self.__update_listeners = []  # functions to call on update
        self.__update_time = None
        self.__last_updated_fact = tuple()

        try:
            initial_timeout = timeout if timeout else 5 # use here also provided timeout for initial non crucial waiting
            rospy.wait_for_service(self.__example_service_name, initial_timeout)
            self.__register_for_updates()
        except rospy.ROSException:
            rhbplog.logwarn(
                'The following knowledge base node is currently not present. Connection will be established later: '
                + knowledge_base_name)
Example #3
0
    def setUp(self):

        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME
        # prevent influence of previous tests
        self.__message_prefix = 'UpdateHandlerTestSuite' + str(time.time())

        # start KnowledgeBase
        self.start_kb_node()

        rospy.init_node('UpdateHandlerTestSuite', log_level=rospy.DEBUG)

        self.__client = KnowledgeBaseClient(self.__knowledge_base_address)
Example #4
0
    def __init__(self, *args, **kwargs):
        super(TestKnowledgeBaseSensor, self).__init__(*args, **kwargs)

        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME + "KnowledgeBaseSensorTestSuite"

        # prevent influence of previous tests
        self.__message_prefix = 'TestKnowledgeBaseSensor' + str(time.time())

        self.start_kb_node()

        rospy.init_node('knowledge_sensor_test_node', log_level=rospy.DEBUG)
        self.__client = KnowledgeBaseClient(
            knowledge_base_name=self.__knowledge_base_address)
Example #5
0
    def __init__(self, pattern, knowledge_base_name=KnowledgeBase.DEFAULT_NAME):
        """
        :param pattern: tuple of strings. The update handler caches all facts, matching the pattern.
        :param knowledge_base_name:
        """

        self.__knowledge_base_name = knowledge_base_name
        self.__pattern = pattern
        self.__initialized = False
        self.__contained_facts = []
        self.__example_service_name = knowledge_base_name + KnowledgeBase.UPDATE_SERVICE_NAME_POSTFIX
        self.__client = KnowledgeBaseClient(knowledge_base_name)
        self.__value_lock = allocate_lock()

        try:
            rospy.wait_for_service(self.__example_service_name, timeout=10)
            self.__register_for_updates()
        except rospy.ROSException:
            rhbplog.loginfo(
                'The following knowledge base node is currently not present. Connection will be established later: ' + knowledge_base_name)
    def setUp(self):

        # prevent influence of previous tests
        self.__message_prefix = 'TupleSpaceTestSuite' + str(time.time())
        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME

        #start KnowledgeBase
        package = 'knowledge_base'
        executable = 'knowledge_base_node.py'
        node = roslaunch.core.Node(package=package,
                                   node_type=executable,
                                   name=self.__knowledge_base_address)
        launch = roslaunch.scriptapi.ROSLaunch()
        launch.start()

        self._kb_process = launch.launch(node)

        rospy.init_node('TupleSpaceTestSuite', log_level=rospy.DEBUG)

        self.__client = KnowledgeBaseClient(self.__knowledge_base_address)
Example #7
0
    def __init__(self,
                 name,
                 pattern,
                 new_tuple,
                 knowledge_base_name=KnowledgeBase.DEFAULT_NAME,
                 **kwargs):
        """
        :param name: behaviour name
        :param pattern: pattern match that will be updated
        :param new_tuple: new tuple value that will replaced the matched tuples
        :param knowledge_base_name: name of the knowledge base that is used
        :param kwargs: further general BehaviourBase arguments
        """

        super(KnowledgeUpdateBehaviour, self) \
            .__init__(name=name, **kwargs)

        self.pattern = pattern
        self.new_tuple = new_tuple

        self._kb_client = KnowledgeBaseClient(
            knowledge_base_name=knowledge_base_name)
Example #8
0
class TestKnowledgeBaseSensor(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(TestKnowledgeBaseSensor, self).__init__(*args, **kwargs)
        # prevent influence of previous tests
        self.__message_prefix = 'TestKnowledgeBaseSensor' + str(time.time())
        rospy.init_node('knowledge_sensor_test_node', log_level=rospy.DEBUG)
        self.__client = KnowledgeBaseClient()

    def test_basic(self):
        """
        Tests sensor output, if the fact is added at runtime (and did not exist before)
        """
        sensor = KnowledgeSensor(pattern=((self.__message_prefix, 'test_basic',
                                           'pos', '*', '*')))
        sensor.sync()
        self.assertFalse(sensor.value)

        self.__client.push(
            (self.__message_prefix, 'test_basic', 'pos', '42', '0'))
        rospy.sleep(0.1)

        sensor.sync()
        self.assertTrue(sensor.value)

    def test_remove(self):
        """
        Tests sensor output , if the fact is removed
        """
        test_tuple = (self.__message_prefix, 'test_remove', 'pos', '42', '0')
        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        sensor = KnowledgeSensor(pattern=(self.__message_prefix, 'test_remove',
                                          'pos', '*', '*'))
        sensor.sync()
        self.assertTrue(sensor.value)

        rospy.sleep(0.1)

        self.__client.pop(test_tuple)

        rospy.sleep(0.1)

        sensor.sync()

        self.assertFalse(sensor.value)
class UpdateHandlerTestSuite(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(UpdateHandlerTestSuite, self).__init__(*args, **kwargs)

    def setUp(self):

        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME + "UpdateHandlerTestSuite"
        # prevent influence of previous tests
        self.__message_prefix = 'UpdateHandlerTestSuite' + str(time.time())

        # start KnowledgeBase
        self.start_kb_node()

        rospy.init_node('UpdateHandlerTestSuite', log_level=rospy.DEBUG)

        self.__client = KnowledgeBaseClient(self.__knowledge_base_address)

    def start_kb_node(self):
        """
        start the knowledge base node
        """
        package = 'knowledge_base'
        executable = 'knowledge_base_node.py'
        node = roslaunch.core.Node(package=package,
                                   node_type=executable,
                                   name=self.__knowledge_base_address,
                                   output='screen')
        launch = roslaunch.scriptapi.ROSLaunch()
        launch.start()
        self._kb_process = launch.launch(node)

    def tearDown(self):
        self._kb_process.stop()

    def test_simple_adding(self):
        """
        Tests basic case of adding a tuple
        """
        test_tuple = (self.__message_prefix, 'test_simple_adding', '0', '0')

        cache = KnowledgeBaseFactCache(
            pattern=test_tuple,
            knowledge_base_name=self.__knowledge_base_address)
        self.assertFalse(cache.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')

        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        self.assertTrue(cache.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')

    def test_remove(self):
        test_tuple = (self.__message_prefix, 'test_remove', '0', '0')

        cache = KnowledgeBaseFactCache(
            pattern=test_tuple,
            knowledge_base_name=self.__knowledge_base_address)
        self.assertFalse(cache.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')

        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        self.assertTrue(cache.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')

        self.__client.pop(test_tuple)

        rospy.sleep(0.1)
        self.assertFalse(
            cache.does_fact_exists(),
            'Tuple was removed, but is still indicated as present')

    def test_multiple_caches_for_same_pattern(self):
        test_tuple = (self.__message_prefix,
                      'test_multiple_caches_for_same_pattern', '0', '0')

        cache1 = KnowledgeBaseFactCache(
            pattern=test_tuple,
            knowledge_base_name=self.__knowledge_base_address)
        cache2 = KnowledgeBaseFactCache(
            pattern=test_tuple,
            knowledge_base_name=self.__knowledge_base_address)

        self.assertFalse(cache1.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')
        self.assertFalse(cache2.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')

        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        self.assertTrue(cache1.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')
        self.assertTrue(cache2.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')

    def test_middle_placeholder(self):
        """
        Test updating an existing fact
        """
        prefix = self.__message_prefix + '_test_middle_placeholder'

        first_fact = (prefix, 'updated', 'a', '1')

        second_fact = (prefix, 'updated', 'b', '1')

        self.__client.push(first_fact)
        self.__client.push(second_fact)
        pattern = (prefix, 'updated', '*', '1')
        cache = KnowledgeBaseFactCache(
            pattern=pattern, knowledge_base_name=self.__knowledge_base_address)

        rospy.sleep(0.1)

        current = cache.get_all_matching_facts()

        self.assertEqual(2, len(current))
        self.assertTrue(first_fact in current, 'fact not found')
        self.assertTrue(second_fact in current, 'fact not found')

    def test_update_existing(self):
        """
        Test updating a fact that is already stored.
        """
        prefix = self.__message_prefix + '_test_update_existing'

        updated_old = (prefix, 'updated', '1')

        self.assertTrue(self.__client.push(updated_old))

        rospy.sleep(0.1)

        cache = KnowledgeBaseFactCache(
            pattern=(prefix, '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)
        rospy.sleep(0.1)
        updated_new = (prefix, 'updated', '1')
        self.assertTrue(self.__client.update((prefix, '*', '*'), updated_new))

        rospy.sleep(
            0.5
        )  # don' t wait for updates because we will not get one, as nothing changed

        current = cache.get_all_matching_facts()

        self.assertEqual(1, len(current))
        self.assertTrue(updated_new in current, 'Update corrupted')

    def test_update(self):
        prefix = self.__message_prefix + '_test_update'
        updated_old = (prefix, 'updated', '1')
        not_influenced = (prefix, 'not_influenced', '1')
        self.__client.push(updated_old)
        self.__client.push(not_influenced)

        cache = KnowledgeBaseFactCache(
            pattern=(prefix, '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        update_stamp = cache.update_time

        updated_new = (prefix, 'updated', '0')

        self.__client.update(updated_old, updated_new)
        while update_stamp == cache.update_time:
            rospy.sleep(0.5)

        current = cache.get_all_matching_facts()
        self.assertEqual(2, len(current))
        self.assertTrue(updated_new in current, 'Update not noticed')
        self.assertTrue(not_influenced in current,
                        'NotInfluenced was influenced')

    def test_update_non_existing(self):
        prefix = self.__message_prefix + 'test_update_non_existing'
        updated_not_existing = (prefix, 'updated', '1')

        cache = KnowledgeBaseFactCache(
            pattern=(prefix, '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        update_stamp = cache.update_time

        updated_new = (prefix, 'updated', '0')

        self.__client.update(updated_not_existing, updated_new)
        while update_stamp == cache.update_time:
            rospy.sleep(0.5)

        current = cache.get_all_matching_facts()
        self.assertEqual(1, len(current))
        self.assertTrue(updated_new in current, 'Update not noticed')

    def test_multiple_updates(self):
        prefix = self.__message_prefix + '_test_multiple_updates'
        updated_old_1 = (prefix, 'fact_1', '1')
        updated_old_2 = (prefix, 'fact_2', '2')
        self.__client.push(updated_old_1)
        self.__client.push(updated_old_2)

        cache = KnowledgeBaseFactCache(
            pattern=(prefix, '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        updated_new_1 = (prefix, 'fact_1', '1*')
        self.__client.update(updated_old_1, updated_new_1)

        updated_new_2 = (prefix, 'fact_2', '2*')

        update_stamp = cache.update_time
        self.__client.update(updated_old_2, updated_new_2)

        rospy.sleep(0.5)
        while update_stamp == cache.update_time:
            rospy.sleep(0.5)

        current = cache.get_all_matching_facts()

        self.assertEqual(2, len(current))
        self.assertTrue(updated_new_1 in current,
                        'Update of updated_new_1 not noticed')
        self.assertTrue(updated_new_2 in current,
                        'Update of updated_new_2 not noticed')

    def test_update_with_partly_matching_patterns(self):
        """
        test an update that replaces several existing facts with a single new one or adds a new fact as a result of an
        update
        """
        prefix = self.__message_prefix + 'test_update_reduction'
        updated_old_1 = (prefix, 'fact_1', '1')
        updated_old_2 = (prefix, 'fact_2', '1')
        self.__client.push(updated_old_1)
        self.__client.push(updated_old_2)

        cache_all = KnowledgeBaseFactCache(
            pattern=(prefix, '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        cache_specific_1 = KnowledgeBaseFactCache(
            pattern=(prefix, 'fact_1', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        cache_specific_2 = KnowledgeBaseFactCache(
            pattern=(prefix, 'fact_2', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        cache_specific_3 = KnowledgeBaseFactCache(
            pattern=(prefix, 'fact_3', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        current_all = cache_all.get_all_matching_facts()
        current_specific_1 = cache_specific_1.get_all_matching_facts()
        current_specific_2 = cache_specific_2.get_all_matching_facts()
        current_specific_3 = cache_specific_3.get_all_matching_facts()

        self.assertEqual(2, len(current_all))
        self.assertEqual(1, len(current_specific_1))
        self.assertEqual(1, len(current_specific_2))
        self.assertEqual(0, len(current_specific_3))

        updated_new = (prefix, 'fact_3', '1')
        update_stamp1 = cache_all.update_time
        update_stamp2 = cache_specific_1.update_time
        update_stamp3 = cache_specific_2.update_time
        update_stamp4 = cache_specific_3.update_time
        self.__client.update((prefix, '*', '1'), updated_new)

        # wait until the caches are updated
        rospy.sleep(0.5)
        while update_stamp1 == cache_all.update_time or \
                update_stamp2 == cache_specific_1.update_time or update_stamp3 == cache_specific_2.update_time \
                or update_stamp4 == cache_specific_3.update_time:
            rospy.sleep(0.1)

        current_all = cache_all.get_all_matching_facts()
        current_specific_1 = cache_specific_1.get_all_matching_facts()
        current_specific_2 = cache_specific_2.get_all_matching_facts()
        current_specific_3 = cache_specific_3.get_all_matching_facts()

        self.assertEqual(1, len(current_all))
        self.assertEqual(0, len(current_specific_1))
        self.assertEqual(0, len(current_specific_2))
        self.assertEqual(1, len(current_specific_3))
        self.assertTrue(updated_new in current_all,
                        'Update general cache of not noticed')
        self.assertTrue(updated_new not in current_specific_1,
                        'Delete update in specific cache 1 not noticed')
        self.assertTrue(updated_new not in current_specific_2,
                        'Delete update in specific cache 2 not noticed')
        self.assertTrue(updated_new in current_specific_3,
                        'Add update in specific cache 3 not noticed')

    def test_all_pattern_with_update(self):
        prefix = self.__message_prefix + '_test_multiple_updates'
        updated_old_1 = (prefix, 'fact_1', '1')
        self.__client.push(updated_old_1)

        all_pattern = ()
        cache = KnowledgeBaseFactCache(
            pattern=all_pattern,
            knowledge_base_name=self.__knowledge_base_address)
        update_stamp = cache.update_time

        updated_new_1 = (prefix, 'fact_1', '3')
        self.__client.update(updated_old_1, updated_new_1)

        while update_stamp == cache.update_time:
            rospy.sleep(0.1)

        current = cache.get_all_matching_facts()
        self.assertEqual(1, len(current))
        self.assertTrue(updated_new_1 in current, 'Update not noticed')

    def __check_content(self, fact_cache, *expected_facts):

        content = fact_cache.get_all_matching_facts()
        error_message = 'Excepted "{0}", but is {1}'.format(
            str(expected_facts), str(content))
        self.assertEqual(len(content), len(expected_facts), error_message)
        for expected_fact in expected_facts:
            self.assertTrue(expected_fact in content, error_message)
        for existing_fact in content:
            self.assertTrue(existing_fact in expected_facts, error_message)

    def test_update_replacing_several_facts(self):

        prefix = self.__message_prefix + 'test_update_replacing_several_facts'
        updated_old_1 = (prefix, 'toUpdate', '1')
        updated_old_2 = (prefix, 'toUpdate', '2')
        not_influenced = (prefix, 'not_influenced', '1')

        cache = KnowledgeBaseFactCache(
            pattern=(prefix, '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)

        rospy.sleep(0.1)

        self.__client.push(updated_old_1)
        self.__client.push(updated_old_2)
        self.__client.push(not_influenced)

        rospy.sleep(0.1)

        self.__check_content(cache, updated_old_1, updated_old_2,
                             not_influenced)

        new_fact = (prefix, 'updated', '3')

        self.__client.update((prefix, 'toUpdate', '*'), new_fact)

        rospy.sleep(0.1)

        self.__check_content(cache, not_influenced, new_fact)

    def test_mass_updates(self):
        """
        Here we test many updates in a short time period
        """

        prefix = self.__message_prefix + 'test_mass_updates'

        number_of_entries = 500

        # fill KB
        for i in range(number_of_entries):

            updated_old_1 = (prefix, 'fact', str(i))
            self.__client.push(updated_old_1)

        cache = KnowledgeBaseFactCache(
            pattern=(prefix, 'fact_new', '*'),
            knowledge_base_name=self.__knowledge_base_address)
        current = cache.get_all_matching_facts()
        self.assertEqual(0, len(current))

        rospy.sleep(0.5)

        for i in range(number_of_entries):
            updated_old_1 = (prefix, 'fact', str(i))
            updated_new_1 = (prefix, 'fact_new', str(i))
            last_fact = updated_new_1
            self.__client.update(updated_old_1, updated_new_1)

        rospy.sleep(0.5)
        while last_fact != cache.last_updated_fact:
            rospy.sleep(0.5)

        current = cache.get_all_matching_facts()

        # test if we received all tuples
        for i in range(number_of_entries):
            updated_new_1 = (prefix, 'fact_new', str(i))
            self.assertTrue(
                updated_new_1 in current,
                'Update of "' + str(updated_new_1) + '" not noticed')

    def test_sub_fact_matching(self):
        prefix = self.__message_prefix + '_test_multiple_updates'
        fact_1 = (prefix, 'fact_1', '1', 'a')
        self.__client.push(fact_1)
        fact_2 = (prefix, 'fact_2', '2', 'a')
        self.__client.push(fact_2)
        fact_3 = (prefix, 'fact_3', '3', 'b')
        self.__client.push(fact_3)

        all_pattern = ()
        cache = KnowledgeBaseFactCache(
            pattern=all_pattern,
            knowledge_base_name=self.__knowledge_base_address)

        # initial check to see if we have a correct setup
        current = cache.get_all_matching_facts()
        self.assertEqual(3, len(current))

        sub_fact_pattern = (prefix, 'fact_2', '2', 'a')
        does_exist = cache.does_sub_fact_exist(pattern=sub_fact_pattern)

        self.assertTrue(does_exist)

        first_sub_fact = cache.get_matching_sub_fact(pattern=sub_fact_pattern)

        self.assertEqual(first_sub_fact, fact_2)

        # test with sub placeholders
        sub_fact_pattern = (prefix, '*', '*', 'a')
        does_exist = cache.does_sub_fact_exist(pattern=sub_fact_pattern)

        self.assertTrue(does_exist)

        first_sub_fact = cache.get_matching_sub_fact(pattern=sub_fact_pattern)

        self.assertEqual(first_sub_fact, fact_1)

        all_sub_facts = cache.get_all_matching_sub_facts(
            pattern=sub_fact_pattern)
        self.assertEqual(2, len(all_sub_facts))
        self.assertTrue(fact_1 in all_sub_facts,
                        "fact_1 not found in sub facts")
        self.assertTrue(fact_2 in all_sub_facts,
                        "fact_2 not found in sub facts")

        # invalid pattern
        sub_fact_pattern = (prefix, 'fact_unknown', '2', 'a')
        does_exist = cache.does_sub_fact_exist(pattern=sub_fact_pattern)

        self.assertFalse(does_exist)
Example #10
0
class KnowledgeBaseFactCache(object):
    """
    Adapter for using update mechanism of knowledge base for caching the value.
    If the knowledge base does not exists at initialization, than the subscribe is done at first using of the cache.
    """

    def __init__(self, pattern, knowledge_base_name=KnowledgeBase.DEFAULT_NAME):
        """
        :param pattern: tuple of strings. The update handler caches all facts, matching the pattern.
        :param knowledge_base_name:
        """

        self.__knowledge_base_name = knowledge_base_name
        self.__pattern = pattern
        self.__initialized = False
        self.__contained_facts = []
        self.__example_service_name = knowledge_base_name + KnowledgeBase.UPDATE_SERVICE_NAME_POSTFIX
        self.__client = KnowledgeBaseClient(knowledge_base_name)
        self.__value_lock = allocate_lock()

        try:
            rospy.wait_for_service(self.__example_service_name, timeout=10)
            self.__register_for_updates()
        except rospy.ROSException:
            rhbplog.loginfo(
                'The following knowledge base node is currently not present. Connection will be established later: ' + knowledge_base_name)

    def __register_for_updates(self):
        """
        registers at knowledge base for updates of facts, which match the pattern of this instance
        """
        added_topic_name, update_topic_name, removed_topic_name = self.__client.subscribe_for_updates(self.__pattern)
        rospy.Subscriber(added_topic_name, Fact, self.__handle_add_update)
        rospy.Subscriber(removed_topic_name, FactRemoved, self.__handle_remove_update)
        rospy.Subscriber(update_topic_name, FactUpdated, self.__handle_fact_update)
        self.update_state_manually()
        self.__initialized = True
        rhbplog.logdebug('Connected to knowledge base: ' + self.__knowledge_base_name)

    def __handle_add_update(self, fact_added):
        """
        handles message, that a matching fact was added
        :param fact_added: empty message
        """
        with(self.__value_lock):
            if (not fact_added.content in self.__contained_facts):
                self.__contained_facts.append(tuple(fact_added.content))

    def __handle_remove_update(self, fact_removed):
        """
        handles message, that a matching fact was removed
        :param fact_removed: FactRemoved, as defined ROS message
        """
        with(self.__value_lock):
            try:
                self.__contained_facts.remove(tuple(fact_removed.fact))
            except ValueError:
                pass

    def __handle_fact_update(self, fact_updated):
        with(self.__value_lock):
            if (not fact_updated.new in self.__contained_facts):
                self.__contained_facts.append(tuple(fact_updated.new))

            for removed_fact in fact_updated.removed:
                try:
                    self.__contained_facts.remove(tuple(removed_fact.content))
                except ValueError:
                    pass

    def update_state_manually(self):
        """
        requests in knowledge base, whether a matching state exists
        :return: whether matching fact exists
        """
        new_content = self.__client.all(self.__pattern)
        with (self.__value_lock):
            self.__contained_facts = new_content
            return not (len(self.__contained_facts) == 0)

    def __ensure_initialization(self):
        if not self.__initialized:
            rhbplog.loginfo('Wait for knowledge base service: ' + self.__example_service_name)
            rospy.wait_for_service(self.__example_service_name)
            self.__register_for_updates()

    def does_fact_exists(self):
        """
        :return: current cached value
        """
        self.__ensure_initialization()
        with (self.__value_lock):
            return not (len(self.__contained_facts) == 0)

    def get_all_matching_facts(self):
        self.__ensure_initialization()
        with (self.__value_lock):
            #TODO better return copy here? Maybe use deepcopy?!
            return self.__contained_facts
Example #11
0
 def __init__(self, *args, **kwargs):
     super(TestKnowledgeBaseSensor, self).__init__(*args, **kwargs)
     # prevent influence of previous tests
     self.__message_prefix = 'TestKnowledgeBaseSensor' + str(time.time())
     rospy.init_node('knowledge_sensor_test_node', log_level=rospy.DEBUG)
     self.__client = KnowledgeBaseClient()
Example #12
0
class TestKnowledgeBaseSensor(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(TestKnowledgeBaseSensor, self).__init__(*args, **kwargs)

        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME + "KnowledgeBaseSensorTestSuite"

        # prevent influence of previous tests
        self.__message_prefix = 'TestKnowledgeBaseSensor' + str(time.time())

        self.start_kb_node()

        rospy.init_node('knowledge_sensor_test_node', log_level=rospy.DEBUG)
        self.__client = KnowledgeBaseClient(
            knowledge_base_name=self.__knowledge_base_address)

    def start_kb_node(self):
        """
        start the knowledge base node
        """
        package = 'knowledge_base'
        executable = 'knowledge_base_node.py'
        node = roslaunch.core.Node(package=package,
                                   node_type=executable,
                                   name=self.__knowledge_base_address,
                                   output='screen')
        launch = roslaunch.scriptapi.ROSLaunch()
        launch.start()
        self._kb_process = launch.launch(node)
        rospy.sleep(1)

    def test_basic(self):
        """
        Tests sensor output, if the fact is added at runtime (and did not exist before)
        """
        sensor = KnowledgeSensor(
            pattern=(self.__message_prefix, 'test_basic', 'pos', '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)
        rospy.sleep(0.1)
        sensor.sync()
        self.assertFalse(sensor.value)

        update_stamp = sensor._value_cache.update_time
        self.__client.push(
            (self.__message_prefix, 'test_basic', 'pos', '42', '0'))
        rospy.sleep(0.1)
        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)

        sensor.sync()
        self.assertTrue(sensor.value)

    def test_remove(self):
        """
        Tests sensor output , if the fact is removed
        """
        test_tuple = (self.__message_prefix, 'test_remove', 'pos', '42', '0')

        sensor = KnowledgeSensor(
            pattern=(self.__message_prefix, 'test_remove', 'pos', '*', '*'),
            knowledge_base_name=self.__knowledge_base_address)
        rospy.sleep(0.1)
        update_stamp = sensor._value_cache.update_time
        self.__client.push(test_tuple)
        rospy.sleep(0.1)
        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)
        sensor.sync()
        self.assertTrue(sensor.value)

        update_stamp = sensor._value_cache.update_time

        removed_facts = self.__client.pop(test_tuple)

        self.assertIsNotNone(removed_facts, "No fact removed")

        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)

        sensor.sync()

        self.assertFalse(sensor.value)

    def test_knowledge_fact_int_sensor(self):
        """
        Test KnowledgeFactIntSensor
        """

        initial_value = 1337
        sensor_pattern = (self.__message_prefix,
                          'test_knowledge_fact_int_sensor', 'number', '*')
        sensor = KnowledgeFactNumberSensor(
            pattern=sensor_pattern,
            initial_value=initial_value,
            knowledge_base_name=self.__knowledge_base_address)
        rospy.sleep(0.1)

        sensor.sync()
        self.assertEquals(sensor.value, initial_value)

        test_value = 42

        update_stamp = sensor._value_cache.update_time
        # regular operation
        self.__client.push(
            (self.__message_prefix, 'test_knowledge_fact_int_sensor', 'number',
             str(test_value)))
        rospy.sleep(0.1)
        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)

        sensor.sync()
        self.assertEquals(sensor.value, test_value)

        update_stamp = sensor._value_cache.update_time
        # illegal operation with non integer value
        new_tuple = (self.__message_prefix, 'test_knowledge_fact_int_sensor',
                     'number', "NO_NUMBER")
        self.assertEquals(
            self.__client.update(pattern=sensor_pattern, new=new_tuple), True)
        rospy.sleep(0.1)
        while update_stamp > sensor.value_update_time:
            rospy.sleep(0.5)

        sensor.sync()
        rospy.loginfo(sensor.value)
        self.assertEquals(sensor.value, initial_value)

    def test_knowledge_fact_count_sensor(self):
        """
        Test KnowledgeFactCountSensor
        """

        initial_value = 0
        sensor_pattern = (self.__message_prefix,
                          'test_knowledge_fact_count_sensor', 'test', '*')
        sensor = KnowledgeFactCountSensor(
            pattern=sensor_pattern,
            knowledge_base_name=self.__knowledge_base_address,
            initial_value=initial_value)
        rospy.sleep(0.1)

        sensor.sync()
        self.assertEquals(sensor.value, initial_value)

        update_stamp = sensor._value_cache.update_time
        # regular operation
        self.__client.push((self.__message_prefix,
                            'test_knowledge_fact_count_sensor', 'test', 'a'))
        rospy.sleep(0.1)
        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)

        sensor.sync()
        self.assertEquals(sensor.value, 1)

        update_stamp = sensor._value_cache.update_time
        # regular operation
        self.__client.push((self.__message_prefix,
                            'test_knowledge_fact_count_sensor', 'test', 'b'))
        rospy.sleep(0.1)
        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)

        sensor.sync()
        self.assertEquals(sensor.value, 2)

        update_stamp = sensor._value_cache.update_time
        # regular operation
        self.__client.pop((self.__message_prefix,
                           'test_knowledge_fact_count_sensor', 'test', 'b'))
        rospy.sleep(0.1)
        while update_stamp == sensor._value_cache.update_time:
            rospy.sleep(0.1)

        sensor.sync()
        self.assertEquals(sensor.value, 1)
Example #13
0
class KnowledgeBaseFactCache(object):
    """
    Adapter for using update mechanism of knowledge base for caching the value.
    If the knowledge base does not exists at initialization, than the subscribe is done at first using of the cache.
    """

    def __init__(self, pattern, knowledge_base_name=KnowledgeBase.DEFAULT_NAME, timeout=None):
        """
        :param pattern: tuple of strings. The update handler caches all facts, matching the pattern.
        :param knowledge_base_name:
        :param timeout: Timeout for all kb service calls in seconds, default is blocking/endless
        """

        self.__knowledge_base_name = knowledge_base_name
        self.__service_timeout = timeout
        self.__pattern = pattern
        self.__initialized = False
        self.__contained_facts = []
        self.__example_service_name = knowledge_base_name + KnowledgeBase.UPDATE_SERVICE_NAME_POSTFIX
        self.__client = KnowledgeBaseClient(knowledge_base_name)
        self.__value_lock = threading.Lock()
        self.__update_listeners = []  # functions to call on update
        self.__update_time = None
        self.__last_updated_fact = tuple()

        try:
            initial_timeout = timeout if timeout else 5 # use here also provided timeout for initial non crucial waiting
            rospy.wait_for_service(self.__example_service_name, initial_timeout)
            self.__register_for_updates()
        except rospy.ROSException:
            rhbplog.logwarn(
                'The following knowledge base node is currently not present. Connection will be established later: '
                + knowledge_base_name)

    def __register_for_updates(self):
        """
        registers at knowledge base for updates of facts, which match the pattern of this instance
        """
        added_topic_name, update_topic_name, removed_topic_name = self.__client.subscribe_for_updates(self.__pattern)
        rospy.Subscriber(added_topic_name, Fact, self.__handle_add_update)
        rospy.Subscriber(removed_topic_name, FactRemoved, self.__handle_remove_update)
        rospy.Subscriber(update_topic_name, FactUpdated, self.__handle_fact_update)
        self.update_state_manually()
        rospy.sleep(0.1)  # Short sleep guarantees that we do not miss updates (triggering publisher queue processing)
        self.__initialized = True
        rhbplog.logdebug('Connected to knowledge base: ' + self.__knowledge_base_name)

    def __handle_add_update(self, fact_added):
        """
        handles message, that a matching fact was added
        :param fact_added: empty message
        """
        tuple_fact = tuple(fact_added.content)
        with self.__value_lock:
            if tuple_fact not in self.__contained_facts:
                self.__contained_facts.append(tuple_fact)
                self._cache_updated(tuple_fact)
        self._notify_listeners()

    def __handle_remove_update(self, fact_removed):
        """
        handles message, that a matching fact was removed
        :param fact_removed: FactRemoved, as defined ROS message
        """
        tuple_fact = tuple(fact_removed.fact)
        with self.__value_lock:
            try:
                self.__contained_facts.remove(tuple_fact)
            except ValueError:
                pass
        self._cache_updated(tuple_fact)
        self._notify_listeners()

    def __handle_fact_update(self, fact_updated):
        tuple_fact_new = tuple(fact_updated.new)
        with self.__value_lock:

            if tuple_fact_new not in self.__contained_facts:
                self.__contained_facts.append(tuple_fact_new)

            for removed_fact in fact_updated.removed:
                tuple_fact_old = tuple(removed_fact.content)
                if tuple_fact_old != tuple_fact_new:
                    try:
                        self.__contained_facts.remove(tuple_fact_old)
                    except ValueError:
                        pass
        self._cache_updated(tuple_fact_new)
        self._notify_listeners()

    def update_state_manually(self, enable_listener_notification=True):
        """
        requests/polls from knowledge base, whether a matching state exists
        :return: whether matching fact exists
        """
        new_content = self.__client.all(self.__pattern)
        with self.__value_lock:
            self.__contained_facts = new_content
            last_fact = self.__contained_facts[-1] if len(self.__contained_facts) > 0 else tuple()
            self._cache_updated(last_fact)  # last element of all facts
            if enable_listener_notification:
                self._notify_listeners()
            return not (len(self.__contained_facts) == 0)

    def __ensure_initialization(self):
        if not self.__initialized:
            try:
                rhbplog.loginfo('Wait for knowledge base service: ' + self.__example_service_name)
                rospy.wait_for_service(self.__example_service_name, timeout=self.__service_timeout)
                self.__register_for_updates()
            except rospy.ROSException as e:
                raise rospy.ROSException("Timeout: Cannot reach service self.__example_service_name:" + str(e))

    def does_fact_exists(self):
        """
        :return: current cached value
        """
        self.__ensure_initialization()
        with self.__value_lock:
            return not (len(self.__contained_facts) == 0)

    def does_sub_fact_exist(self, pattern):
        """
        Returns whether a fact contained in this KnowledgeBaseFactCache and additionally matching the
        given pattern exists.
        :param pattern: the pattern to look for
        :return: True if a cached fact matches the given pattern, else False
        """
        self.__ensure_initialization()
        with self.__value_lock:
            for f in self.__contained_facts:
                for i, pattern_elem in enumerate(pattern):
                    if pattern_elem != KnowledgeBase.PLACEHOLDER and not pattern_elem == f[i]:
                        break
                else:
                    return True
        return False

    def get_all_matching_facts(self):
        self.__ensure_initialization()
        with self.__value_lock:
            return copy.deepcopy(self.__contained_facts)

    def get_matching_sub_fact(self, pattern):
        """
        Returns the first fact that matches the given pattern, which should be a subset (i.e. more specific)
        of the pattern used during initialization of this KnowledgeBaseFactCache.
        :param pattern: the pattern to look for
        :return: the first contained fact matching the given pattern
        """
        self.__ensure_initialization()
        with self.__value_lock:
            for f in self.__contained_facts:
                for i, pattern_elem in enumerate(pattern):
                    if pattern_elem != KnowledgeBase.PLACEHOLDER and not pattern_elem == f[i]:
                        break
                else:
                    return copy.deepcopy(f)

    def get_all_matching_sub_facts(self, pattern):
        """
        Returns all facts that match the given pattern, which should be a subset (i.e. more specific) of the pattern
        used during initialization of this KnowledgeBaseFactCache.
        :param pattern: the pattern that facts should match
        :return: a list of contained facts matching the pattern
        """
        self.__ensure_initialization()
        with self.__value_lock:
            result = []
            for f in self.__contained_facts:
                for i, pattern_elem in enumerate(pattern):
                    if pattern_elem != KnowledgeBase.PLACEHOLDER and not pattern_elem == f[i]:
                        break
                else:
                    result.append(copy.deepcopy(f))
            return result

    def _notify_listeners(self):
        """
        Notify listeners about new fact update
        """
        for func in self.__update_listeners:
            func()

    def add_update_listener(self, func):
        """
        Add a function that is going to be called/notified on a fact update
        :param func: any python function without required parameters
        """
        if func not in self.__update_listeners:
            self.__update_listeners.append(func)

    def remove_update_listener(self, func):
        """
        Remove a function from the listener list
        :param func: function that has been registered before
        """
        if func in self.__update_listeners:
            self.__update_listeners.remove(func)

    def _cache_updated(self, fact):
        """
        function should be triggered when the cache was updated
        it stores some information about the last update
        :param fact: last updated fact
        """
        self.__update_time = rospy.Time.now()
        self.__last_updated_fact = fact

    @property
    def update_time(self):
        """
        Get time of last update
        :return: ROSTime of last fact change
        """
        return self.__update_time

    @property
    def last_updated_fact(self):
        """
        Get last updated fact
        :return: fact
        """
        return self.__last_updated_fact
class TupleSpaceTestSuite(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(TupleSpaceTestSuite, self).__init__(*args, **kwargs)

    def setUp(self):

        # prevent influence of previous tests
        self.__message_prefix = 'TupleSpaceTestSuite' + str(time.time())
        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME

        #start KnowledgeBase
        package = 'knowledge_base'
        executable = 'knowledge_base_node.py'
        node = roslaunch.core.Node(package=package,
                                   node_type=executable,
                                   name=self.__knowledge_base_address)
        launch = roslaunch.scriptapi.ROSLaunch()
        launch.start()

        self._kb_process = launch.launch(node)

        rospy.init_node('TupleSpaceTestSuite', log_level=rospy.DEBUG)

        self.__client = KnowledgeBaseClient(self.__knowledge_base_address)

    def tearDown(self):
        self._kb_process.stop()

    def __wait_for_tuple(self, wait_for_it):
        """
        waits, until the requested tuple is contained in knowledge_base
        :param wait_for_it: tuple
        """
        while not self.__client.exists(wait_for_it):
            rospy.sleep(0.1)

    def __add_multiple_facts(self, *facts):
        for t in facts:
            self.__client.push(t)
        self.__wait_for_tuple(facts[len(facts) - 1])

    def __test_list_equality(self, l1, l2):
        error_message = 'Excepted "{0}", but is {1}'.format(str(l1), str(l2))
        self.assertEqual(len(l1), len(l2))
        for i1 in l1:
            self.assertTrue(i1 in l2, error_message)
        for i2 in l2:
            self.assertTrue(i2 in l1, error_message)

    def test_exists_for_non_existing(self):
        test_tuple = (self.__message_prefix, 'test_exists_for_non_existing',
                      '0', '0')
        self.assertFalse(self.__client.exists(test_tuple))

    def test_simple_adding(self):
        test_tuple = (self.__message_prefix, 'test_simple_adding', '0', '0')
        self.__client.push(test_tuple)
        self.assertTrue(self.__client.exists(test_tuple))

    def test_peek(self):
        test_tuple = (self.__message_prefix, 'test_peek', '0', '0')
        self.__client.push(test_tuple)
        self.assertEqual(test_tuple, self.__client.peek(test_tuple))

    def test_pop(self):
        test_tuple = (self.__message_prefix, 'test_pop', '0', '0')
        self.__client.push(test_tuple)
        self.assertTrue(self.__client.exists(test_tuple))

        removed = self.__client.pop(test_tuple)
        self.assertEqual(1, len(removed))
        self.assertEqual(test_tuple, removed[0])

    def test_remove_of_non_existing(self):
        tuples = []
        tuples.append(
            (self.__message_prefix, 'test_remove_of_non_existing', '0', '1'))
        tuples.append(
            (self.__message_prefix, 'test_remove_of_non_existing', '2', '5'))
        self.__add_multiple_facts(*tuples)

        self.__check_content(
            (self.__message_prefix, 'test_remove_of_non_existing', '*', '*'),
            *tuples)
        removed = self.__client.pop(
            (self.__message_prefix, 'test_remove_of_non_existing', '4', '*'))
        self.assertEqual(0, len(removed))
        self.__check_content(
            (self.__message_prefix, 'test_remove_of_non_existing', '*', '*'),
            *tuples)

    def test_multiple_pop(self):
        uninteresting_content = []
        uninteresting_content.append(
            (self.__message_prefix, 'test_multiple_pop', 'boring', '1'))
        uninteresting_content.append(
            (self.__message_prefix, 'test_multiple_pop', 'boring', '5'))
        to_remove = []
        to_remove.append(
            (self.__message_prefix, 'test_multiple_pop', 'interesting', '1'))
        to_remove.append(
            (self.__message_prefix, 'test_multiple_pop', 'interesting', '5'))
        all = []
        all.extend(uninteresting_content)
        all.extend(to_remove)
        self.__add_multiple_facts(*all)
        self.__check_content(
            (self.__message_prefix, 'test_multiple_pop', '*', '*'),
            *tuple(all))
        removed = self.__client.pop(
            (self.__message_prefix, 'test_multiple_pop', 'interesting', '*'))
        self.__test_list_equality(removed, to_remove)
        self.__check_content(
            (self.__message_prefix, 'test_multiple_pop', '*', '*'),
            *tuple(uninteresting_content))

    def test_placeholder(self):
        test_tuple = (self.__message_prefix, 'test_placeholder', '0', '0')
        pattern = (self.__message_prefix, 'test_placeholder', '*', '*')

        self.__client.push(test_tuple)
        self.assertEqual(test_tuple, self.__client.peek(pattern))

    def test_all(self):
        """
        test service for find all matching facts
        """
        tuples = []
        tuples.append((self.__message_prefix, 'test_all', 'pos', '0', '0'))
        tuples.append((self.__message_prefix, 'test_all', 'pos', '1', '0'))
        tuples.append((self.__message_prefix, 'test_all', 'pos', '1', '-4'))
        self.__add_multiple_facts(*tuples)

        self.__check_content(
            (self.__message_prefix, 'test_all', 'pos', '*', '*'),
            *tuple(tuples))

    def __check_content(self, pattern, *expected):
        """
        ensures, that exact the expected content is contained in the knowledge base
        :param pattern: filter for deciding, which contained facts of the kb should used
        :param expected: expected content of the kb
        """
        knowledge_base_content = self.__client.all(pattern)
        self.assertEqual(
            len(expected), len(knowledge_base_content), ' Expected: ' +
            str(expected) + ' but is ' + str(knowledge_base_content))
        for fact in expected:
            self.assertTrue(
                fact in knowledge_base_content, ' Expected: ' + str(expected) +
                ' but is ' + str(knowledge_base_content))

    def test_prevent_multiple_adding(self):
        """
        tests that no duplicates are contained in knowledge base
        """
        test_tuple = (self.__message_prefix, 'test_prevent_multiple_adding',
                      '0', '0')
        self.__client.push(test_tuple)
        self.__client.push(test_tuple)

        service_name = self.__knowledge_base_address + KnowledgeBase.ALL_SERVICE_NAME_POSTFIX
        rospy.wait_for_service(service_name)
        all_service = rospy.ServiceProxy(service_name, All)
        all_response = all_service(test_tuple)
        self.assertEqual(1, len(all_response.found))

    def __check_mocks_empty(self, update_mocks):
        """
        Asserts, that all given mocks has no remaining messages. All expected messages must be removed before method call
        :param update_mocks (list of UpdateSubscriberMock)
        """
        for update_mock in update_mocks:
            self.assertTrue(
                len(update_mock.add_msg) == 0,
                'Following mock has an unexpected add message received: ' +
                update_mock.name)
            self.assertTrue(
                len(update_mock.remove_msg) == 0,
                'Following mock has an unexpected remove message received: ' +
                update_mock.name)
            self.assertTrue(
                len(update_mock.update_msg) == 0,
                'Following mock has an unexpected update message received: ' +
                update_mock.name)

    def test_update_non_existing(self):
        """
        Tries to update a fact, which is not in the kb
        """
        prefix = self.__message_prefix + '_test_update_non_existing'
        new_fact = (prefix, 'new', '1')
        not_influenced = (prefix, 'not_influenced', '1')

        self.__client.push(not_influenced)

        informer_added = UpdateSubscriberMock(mock_name='added',
                                              client=self.__client,
                                              pattern=(prefix, 'new', '*'))
        informer_removed = UpdateSubscriberMock(mock_name='removed',
                                                client=self.__client,
                                                pattern=(prefix, 'unsuccess',
                                                         '*'))
        informer_updated = UpdateSubscriberMock(mock_name='updated',
                                                client=self.__client,
                                                pattern=(prefix, '*', '*'))
        informer_not_influenced = UpdateSubscriberMock(
            mock_name='not_influended',
            client=self.__client,
            pattern=(prefix, 'not_influenced', '*'))
        all_informers = [
            informer_added, informer_removed, informer_updated,
            informer_not_influenced
        ]

        self.assertFalse(
            self.__client.update((self.__message_prefix, 'unsuccess', '1'),
                                 new_fact,
                                 push_without_existing=False),
            'Old fact does not exist, but update was successfull')
        rospy.sleep(0.1)
        self.__check_mocks_empty(all_informers)
        self.__check_content((prefix, '*', '*'), not_influenced)

        self.assertTrue(
            self.__client.update((self.__message_prefix, 'not_existing', '1'),
                                 new_fact,
                                 push_without_existing=True),
            'Old fact does not exists and update was not successfull')
        rospy.sleep(0.1)

        self.assertEqual(
            1, len(informer_added.add_msg),
            'Error at receiving add message: ' +
            str(len(informer_added.add_msg)))
        self.assertEqual(new_fact, tuple(informer_added.add_msg[0].content))
        informer_added.add_msg = []

        self.assertEqual(
            1, len(informer_updated.add_msg),
            'Error at receiving add message: ' +
            str(len(informer_updated.add_msg)))
        self.assertEqual(new_fact, tuple(informer_updated.add_msg[0].content))
        informer_updated.add_msg = []

        self.__check_mocks_empty(all_informers)
        self.__check_content((prefix, '*', '*'), not_influenced, new_fact)

    def test_update_basic(self):
        """
        Tests simple update of an existing fact
        """
        prefix = self.__message_prefix + '_test_update_basic'
        old_fact = (prefix, 'old', '2')
        new_fact = (prefix, 'new', '1')
        not_influenced = (prefix, 'not_influenced', '1')

        self.__client.push(old_fact)
        self.__client.push(not_influenced)
        self.__check_content((prefix, '*', '*'), old_fact, not_influenced)

        informer_added = UpdateSubscriberMock(mock_name='added',
                                              client=self.__client,
                                              pattern=(prefix, 'new', '*'))
        informer_removed = UpdateSubscriberMock(mock_name='removed',
                                                client=self.__client,
                                                pattern=(prefix, 'old', '*'))
        informer_updated = UpdateSubscriberMock(mock_name='updated',
                                                client=self.__client,
                                                pattern=(prefix, '*', '*'))
        informer_second_target = UpdateSubscriberMock(
            mock_name='seccond',
            client=self.__client,
            pattern=(prefix, 'seccond_target', '*'))
        informer_not_influenced = UpdateSubscriberMock(
            mock_name='not_influended',
            client=self.__client,
            pattern=(prefix, 'not_influenced', '*'))
        all_informers = [
            informer_added, informer_removed, informer_updated,
            informer_second_target, informer_not_influenced
        ]

        self.assertTrue(self.__client.update(old_fact, new_fact),
                        'First update failed')
        rospy.sleep(0.1)

        self.assertEqual(
            1, len(informer_added.add_msg),
            'Error at receiving add message: ' +
            str(len(informer_added.add_msg)))
        self.assertEqual(new_fact, tuple(informer_added.add_msg[0].content))
        informer_added.add_msg = []

        self.assertEqual(
            1, len(informer_removed.remove_msg),
            'Error at receiving remove message: ' +
            str(len(informer_removed.remove_msg)))
        self.assertEqual(old_fact, tuple(informer_removed.remove_msg[0].fact))
        informer_removed.remove_msg = []

        self.assertEqual(
            1, len(informer_updated.update_msg),
            'Error at receiving update message: ' +
            str(len(informer_updated.update_msg)))
        self.assertEqual(1, len(informer_updated.update_msg[0].removed))
        self.assertEqual(
            old_fact, tuple(informer_updated.update_msg[0].removed[0].content))
        self.assertEqual(new_fact, tuple(informer_updated.update_msg[0].new))
        informer_updated.update_msg = []

        self.__check_mocks_empty(all_informers)
        self.__check_content((prefix, '*', '*'), new_fact, not_influenced)

    def test_update_existing_target(self):
        """
        Tests update of an existing fact to a fact, which is already contained in the kb
        """
        prefix = self.__message_prefix + '_test_update_existing_target'
        old_fact = (prefix, 'old', '2')
        new_fact = (prefix, 'new', '1')
        not_influenced = (prefix, 'not_influenced', '1')

        self.__add_multiple_facts(old_fact, new_fact, not_influenced)
        self.__check_content((prefix, '*', '*'), old_fact, new_fact,
                             not_influenced)

        informer_added = UpdateSubscriberMock(mock_name='added',
                                              client=self.__client,
                                              pattern=(prefix, 'new', '*'))
        informer_removed = UpdateSubscriberMock(mock_name='removed',
                                                client=self.__client,
                                                pattern=(prefix, 'old', '*'))
        informer_updated = UpdateSubscriberMock(mock_name='updated',
                                                client=self.__client,
                                                pattern=(prefix, '*', '*'))
        informer_second_target = UpdateSubscriberMock(
            mock_name='seccond',
            client=self.__client,
            pattern=(prefix, 'seccond_target', '*'))
        informer_not_influenced = UpdateSubscriberMock(
            mock_name='not_influended',
            client=self.__client,
            pattern=(prefix, 'not_influenced', '*'))
        all_informers = [
            informer_added, informer_removed, informer_updated,
            informer_second_target, informer_not_influenced
        ]

        self.assertTrue(self.__client.update(old_fact, new_fact),
                        'update failed')
        rospy.sleep(0.1)

        self.assertEqual(
            1, len(informer_removed.remove_msg),
            'Error at receiving remove message: ' +
            str(len(informer_removed.remove_msg)))
        self.assertEqual(old_fact, tuple(informer_removed.remove_msg[0].fact))
        informer_removed.remove_msg = []
        self.assertEqual(
            1, len(informer_updated.remove_msg),
            'Error at receiving second remove message: ' +
            str(len(informer_removed.remove_msg)))
        self.assertEqual(old_fact, tuple(informer_updated.remove_msg[0].fact))
        informer_updated.remove_msg = []

        self.__check_mocks_empty(all_informers)
        self.__check_content((prefix, '*', '*'), new_fact, not_influenced)

    def test_replace_several_facts(self):
        prefix = self.__message_prefix + '_test_replace_several_facts'
        old_fact_1 = (prefix, 'old', '2')
        old_fact_2 = (prefix, 'old', '3')
        not_influenced = (prefix, 'not_influenced', '1')
        self.__add_multiple_facts(old_fact_1, old_fact_2, not_influenced)
        self.__check_content((prefix, '*', '*'), old_fact_1, old_fact_2,
                             not_influenced)

        new_fact = (prefix, 'new', '1')

        informer_added = UpdateSubscriberMock(mock_name='added',
                                              client=self.__client,
                                              pattern=(prefix, 'new', '*'))
        informer_removed = UpdateSubscriberMock(mock_name='removed',
                                                client=self.__client,
                                                pattern=(prefix, 'old', '*'))
        informer_updated = UpdateSubscriberMock(mock_name='updated',
                                                client=self.__client,
                                                pattern=(prefix, '*', '*'))
        informer_not_influenced = UpdateSubscriberMock(
            mock_name='not_influended',
            client=self.__client,
            pattern=(prefix, 'not_influenced', '*'))
        all_informers = [
            informer_added, informer_removed, informer_updated,
            informer_not_influenced
        ]

        self.assertTrue(self.__client.update((prefix, 'old', '*'), new_fact),
                        'update failed')
        rospy.sleep(0.1)

        self.__check_content((prefix, '*', '*'), new_fact, not_influenced)

        self.assertEqual(
            1, len(informer_added.add_msg),
            'Error at receiving add message: ' +
            str(len(informer_added.add_msg)))
        self.assertEqual(new_fact, tuple(informer_added.add_msg[0].content))
        informer_added.add_msg = []

        self.assertEqual(
            2, len(informer_removed.remove_msg),
            'Error at receiving remove messages: ' +
            str(len(informer_removed.remove_msg)))
        removed_facts = [old_fact_1, old_fact_2]
        for remove_msg in informer_removed.remove_msg:
            self.assertTrue(
                tuple(remove_msg.fact) in removed_facts,
                str(remove_msg.fact) + ' not in ' + str(removed_facts))
            removed_facts.remove(tuple(remove_msg.fact))
        informer_removed.remove_msg = []

        self.assertEqual(
            1, len(informer_updated.update_msg),
            'Error at receiving update message: ' +
            str(len(informer_updated.update_msg)))
        self.assertEqual(tuple(informer_updated.update_msg[0].new), new_fact)
        removed_facts = [old_fact_1, old_fact_2]
        for removed_fact in informer_updated.update_msg[0].removed:
            self.assertTrue(
                tuple(removed_fact.content) in removed_facts,
                str(tuple(removed_fact.content)) + ' not in ' +
                str(removed_facts))
            removed_facts.remove(tuple(removed_fact.content))
        informer_updated.update_msg = []

        self.__check_mocks_empty(all_informers)
Example #15
0
class UpdateHandlerTestSuite(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(UpdateHandlerTestSuite, self).__init__(*args, **kwargs)

    def setUp(self):

        self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME
        # prevent influence of previous tests
        self.__message_prefix = 'UpdateHandlerTestSuite' + str(time.time())

        # start KnowledgeBase
        self.start_kb_node()

        rospy.init_node('UpdateHandlerTestSuite', log_level=rospy.DEBUG)

        self.__client = KnowledgeBaseClient(self.__knowledge_base_address)

    def start_kb_node(self):
        """
        start the knowledge base node
        """
        package = 'knowledge_base'
        executable = 'knowledge_base_node.py'
        node = roslaunch.core.Node(package=package,
                                   node_type=executable,
                                   name=self.__knowledge_base_address)
        launch = roslaunch.scriptapi.ROSLaunch()
        launch.start()
        self._kb_process = launch.launch(node)

    def tearDown(self):
        self._kb_process.stop()

    def test_simple_adding(self):
        """
        Tests basic case of adding a tuple
        """
        test_tuple = (self.__message_prefix, 'test_simple_adding', '0', '0')

        cache = KnowledgeBaseFactCache(pattern=test_tuple)
        self.assertFalse(cache.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')

        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        self.assertTrue(cache.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')

    def test_remove(self):
        test_tuple = (self.__message_prefix, 'test_remove', '0', '0')

        cache = KnowledgeBaseFactCache(pattern=test_tuple)
        self.assertFalse(cache.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')

        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        self.assertTrue(cache.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')

        self.__client.pop(test_tuple)

        rospy.sleep(0.1)
        self.assertFalse(
            cache.does_fact_exists(),
            'Tuple was removed, but is still indicated as present')

    def test_multiple_caches_for_same_pattern(self):
        test_tuple = (self.__message_prefix,
                      'test_multiple_caches_for_same_pattern', '0', '0')

        cache1 = KnowledgeBaseFactCache(pattern=test_tuple)
        cache2 = KnowledgeBaseFactCache(pattern=test_tuple)

        self.assertFalse(cache1.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')
        self.assertFalse(cache2.does_fact_exists(),
                         'Tuple is not added, but is indicated as present')

        self.__client.push(test_tuple)
        rospy.sleep(0.1)

        self.assertTrue(cache1.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')
        self.assertTrue(cache2.does_fact_exists(),
                        'Tuple was added, but is not indicated as present')

    def test_middle_placeholder(self):
        """
        Test updating an existing fact 
        """
        prefix = self.__message_prefix + '_test_middle_placeholder'

        first_fact = (prefix, 'updated', 'a', '1')

        second_fact = (prefix, 'updated', 'b', '1')

        self.__client.push(first_fact)
        self.__client.push(second_fact)

        cache = KnowledgeBaseFactCache(pattern=(prefix, 'updated', '*', '1'))

        rospy.sleep(0.1)

        current = cache.get_all_matching_facts()

        self.assertEqual(2, len(current))
        self.assertTrue(first_fact in current, 'fact not found')
        self.assertTrue(second_fact in current, 'fact not found')

    def test_update_existing(self):
        """
        Test updating an existing fact 
        """
        prefix = self.__message_prefix + '_test_update_empty'

        updated_old = (prefix, 'updated', '1')

        self.__client.push(updated_old)

        cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*'))

        updated_new = (prefix, 'updated', '1')

        self.__client.update((prefix, '*', '*'), updated_new)
        rospy.sleep(0.1)

        current = cache.get_all_matching_facts()

        self.assertEqual(1, len(current))
        self.assertTrue(updated_new in current, 'Update not noticed')

    def test_update(self):
        prefix = self.__message_prefix + '_test_update'
        updated_old = (prefix, 'updated', '1')
        not_influenced = (prefix, 'not_influenced', '1')
        self.__client.push(updated_old)
        self.__client.push(not_influenced)

        cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*'))

        updated_new = (prefix, 'updated', '0')

        self.__client.update(updated_old, updated_new)
        rospy.sleep(0.1)

        current = cache.get_all_matching_facts()
        self.assertEqual(2, len(current))
        self.assertTrue(updated_new in current, 'Update not noticed')
        self.assertTrue(not_influenced in current,
                        'NotInfluenced was influenced')

    def test_multiple_updates(self):
        prefix = self.__message_prefix + '_test_multiple_updates'
        updated_old_1 = (prefix, 'fact_1', '1')
        updated_old_2 = (prefix, 'fact_2', '2')
        self.__client.push(updated_old_1)
        self.__client.push(updated_old_2)

        cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*'))

        updated_new_1 = (prefix, 'fact_1', '3')
        self.__client.update(updated_old_1, updated_new_1)

        updated_new_2 = (prefix, 'fact_2', '4')
        self.__client.update(updated_old_2, updated_new_2)
        rospy.sleep(0.1)

        current = cache.get_all_matching_facts()
        self.assertEqual(2, len(current))
        self.assertTrue(updated_new_1 in current, 'Update not noticed')
        self.assertTrue(updated_new_2 in current, 'Update not noticed')

    def __check_content(self, fact_cache, *expected_facts):

        content = fact_cache.get_all_matching_facts()
        error_message = 'Excepted "{0}", but is {1}'.format(
            str(expected_facts), str(content))
        self.assertEqual(len(content), len(expected_facts), error_message)
        for expected_fact in expected_facts:
            self.assertTrue(expected_fact in content, error_message)
        for existing_fact in content:
            self.assertTrue(existing_fact in expected_facts, error_message)

    def test_update_replacing_several_facts(self):

        prefix = self.__message_prefix + 'test_update_replacing_several_facts'
        updated_old_1 = (prefix, 'toUpdate', '1')
        updated_old_2 = (prefix, 'toUpdate', '2')
        not_influenced = (prefix, 'not_influenced', '1')

        cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*'))

        rospy.sleep(0.1)

        self.__client.push(updated_old_1)
        self.__client.push(updated_old_2)
        self.__client.push(not_influenced)

        rospy.sleep(0.1)

        self.__check_content(cache, updated_old_1, updated_old_2,
                             not_influenced)

        new_fact = (prefix, 'updated', '3')

        self.__client.update((prefix, 'toUpdate', '*'), new_fact)

        rospy.sleep(0.1)

        self.__check_content(cache, not_influenced, new_fact)
 def __init__(self, kb_name, parent=None, *args):
     super(FactTableModel, self).__init__(parent)
     self.facts = None
     self._kb_name = kb_name
     self._kb_client = KnowledgeBaseClient(
         knowledge_base_name=self._kb_name, timeout=0.5)
class FactTableModel(QAbstractTableModel):
    """
    Simple QT table model for Knowledge Base facts
    """
    def __init__(self, kb_name, parent=None, *args):
        super(FactTableModel, self).__init__(parent)
        self.facts = None
        self._kb_name = kb_name
        self._kb_client = KnowledgeBaseClient(
            knowledge_base_name=self._kb_name, timeout=0.5)

    def update(self, new_facts):
        """
        Use the method to update the fact/data base
        :param new_facts: new list of fact tuples
        """
        if new_facts:
            new_facts.sort()
        self.layoutAboutToBeChanged.emit()
        self.facts = new_facts
        self.layoutChanged.emit()

    def clear(self):
        """
        Clear current fact base
        """
        self.update(None)

    def rowCount(self, parent=QModelIndex()):
        if self.facts:
            return len(self.facts)
        else:
            return 0

    def columnCount(self, parent=QModelIndex()):

        if self.facts:
            max_column = 0
            for fact in self.facts:
                max_column = max(max_column, len(fact))
            return max_column
        else:
            return 0

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole or role == Qt.EditRole:
            i = index.row()
            j = index.column()
            if j < len(self.facts[i]):
                return '{0}'.format(self.facts[i][j])
            else:
                return QVariant()
        else:
            return QVariant()

    def setData(self, index, value, role):
        """
        Update remote knowledge base data
        """
        try:

            pattern = copy.deepcopy(self.facts[index.row()])

            current_fact = list(pattern)

            if index.column() >= len(current_fact):
                for i in range(0, index.column() - len(current_fact) + 1):
                    current_fact.append("")
            current_fact[index.column()] = str(value)

            rospy.logdebug("Changing knowledge: \n" + str(pattern) +
                           "\n --to-- \n" + str(current_fact))

            return self._kb_client.update(pattern=pattern, new=current_fact)
        except Exception:
            rospy.logerr(traceback.format_exc())
            return False

    def flags(self, index):
        return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    @property
    def kb_name(self):
        return self._kb_name

    @kb_name.setter
    def kb_name(self, value):
        if self._kb_name != value:
            self._kb_name = value
            self._kb_client = KnowledgeBaseClient(knowledge_base_name=value,
                                                  timeout=0.5)
 def kb_name(self, value):
     if self._kb_name != value:
         self._kb_name = value
         self._kb_client = KnowledgeBaseClient(knowledge_base_name=value,
                                               timeout=0.5)