From a409bbd6a4e5b9e7a3643a4016a4fdcff0374660 Mon Sep 17 00:00:00 2001 From: Daniel Stonier Date: Sun, 10 Nov 2019 11:34:36 -0500 Subject: [PATCH] [blackboard] distinguish primitives from nested for read activity (#255) --- CHANGELOG.rst | 2 +- py_trees/blackboard.py | 6 +++++- py_trees/demos/blackboard.py | 41 +++++++++++++++++++++++++++++++++++- py_trees/utilities.py | 15 +++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d6fe531d..124f5e70 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,7 +3,7 @@ Release Notes Forthcoming ----------- -* ... +* [blackboards] distinguish primitives vs nested for refined read activity detection, `#255 `_ 1.4.x (2019-11-07) ------------------ diff --git a/py_trees/blackboard.py b/py_trees/blackboard.py index 3c17fd96..01ba0047 100755 --- a/py_trees/blackboard.py +++ b/py_trees/blackboard.py @@ -694,10 +694,14 @@ def __getattr__(self, name: str): try: if local_name in super().__getattribute__("write"): if Blackboard.activity_stream is not None: + if utilities.is_primitive(Blackboard.storage[name]): + activity_type = ActivityType.READ + else: # could be a nested class object being accessed to write an attribute + activity_type = ActivityType.ACCESSED Blackboard.activity_stream.push( self._generate_activity_item( key=name, - activity_type=ActivityType.ACCESSED, + activity_type=activity_type, current_value=Blackboard.storage[name], ) ) diff --git a/py_trees/demos/blackboard.py b/py_trees/demos/blackboard.py index 5b632089..1b4b4277 100644 --- a/py_trees/demos/blackboard.py +++ b/py_trees/demos/blackboard.py @@ -126,6 +126,39 @@ def update(self): return py_trees.common.Status.SUCCESS +class ParamsAndState(py_trees.behaviour.Behaviour): + """ + A more esotoric use of multiple blackboards in a behaviour to represent + storage of parameters and state. + """ + def __init__(self, name="ParamsAndState"): + super().__init__(name=name) + # namespaces can include the separator or may leave it out + # they can also be nested, e.g. /agent/state, /agent/parameters + self.parameters = self.attach_blackboard_client("Params", "parameters_") + self.state = self.attach_blackboard_client("State", "state_") + self.parameters.register_key( + key="default_speed", + access=py_trees.common.Access.WRITE + ) + self.state.register_key( + key="current_speed", + access=py_trees.common.Access.WRITE + ) + if not self.parameters.exists("default_speed"): + self.parameters.default_speed = 30.0 + + def initialise(self): + self.state.current_speed = self.parameters.default_speed + + def update(self): + if self.state.current_speed > 40.0: + return py_trees.common.Status.SUCCESS + else: + self.state.current_speed += 1.0 + return py_trees.common.Status.RUNNING + + def create_root(): root = py_trees.composites.Sequence("Blackboard Demo") set_blackboard_variable = py_trees.behaviours.SetBlackboardVariable( @@ -135,7 +168,13 @@ def create_root(): check_blackboard_variable = py_trees.behaviours.CheckBlackboardVariableValue( name="Check Nested Foo", variable_name="nested.foo", expected_value="bar" ) - root.add_children([set_blackboard_variable, write_blackboard_variable, check_blackboard_variable]) + params_and_state = ParamsAndState() + root.add_children([ + set_blackboard_variable, + write_blackboard_variable, + check_blackboard_variable, + params_and_state + ]) return root ############################################################################## diff --git a/py_trees/utilities.py b/py_trees/utilities.py index 62265f9b..41d30826 100644 --- a/py_trees/utilities.py +++ b/py_trees/utilities.py @@ -19,6 +19,7 @@ import os import re import traceback +import typing ############################################################################## # Python Helpers @@ -44,6 +45,20 @@ def decorate(func): return decorate +@static_variables(primitives={bool, str, int, float}) +def is_primitive(incoming: typing.Any) -> bool: + """ + Check if an incoming argument is a primitive type with no esoteric + accessors (e.g. class attributes or container [] accessors. + + Args: + incoming: the instance to check + Returns: + True or false, depending on the check against the reserved primitives + """ + return type(incoming) in is_primitive.primitives + + def truncate(original: str, length: int) -> str: """ Provide an elided version of the string for which the last three