用户数据UserData类和重映射Remapper类包含在smach中的user_data.py文件中实现,该博文主要介绍其原理和例子
- UserData主要用于状态之间的数据传递,包括数据的输入input_keys和输出output_keys。用户数据做为状态的execute函数的参数来使用。而输入和输出的初始化在状态的__init__中进行初始化。数据结构可以表示为树状:
UserData
-- input_keys
--output_keys
- Rempaper主要用于用户数据中的输入input_keys和输出键output_keys的名字重映射。和消息的重映射的思路相同。
进一步关于UserData如何在状态之间传递和指定输入输出键的,请参考如下的资料:
原理:The principle of Passing User Data between States
例子:The example of Passing User Data between States
1、源码注释
具体的中文翻译解释和一些额外的注释,请看如下的实现代码:
<span>import</span>
<span> threading
</span>
<span>import</span>
<span> copy
</span>
<span>import</span>
<span> smach
</span>
<span>__all__</span> = [<span>‘</span><span>UserData</span><span>‘</span>,<span>‘</span><span>Remapper</span><span>‘</span>]<span>#</span><span>用"__all__"导出UserData,Remapper两个类</span><span>#</span><span>用户数据,用于状态之间的传递,输入或输出</span><span>class</span><span> UserData(object):
</span><span>"""</span><span>SMACH user data structure.</span><span>"""</span><span>‘‘‘</span><span>SMACH 用户数据结构</span><span>‘‘‘</span><span>def</span><span>__init__</span><span>(self):
self._data </span>=<span> {}
self._locks </span>=<span> {}
self.</span><span>__initialized</span> =<span> True
</span><span>def</span><span> update(self, other_userdata):
</span><span>"""</span><span>Combine this userdata struct with another.
This overwrites duplicate keys with values from C{other_userdata}.
</span><span>"""</span><span>‘‘‘</span><span>将外加的用户数据和原有的用户数据合并,重复关键字的会更新相应的值</span><span>‘‘‘</span><span>#</span><span> Merge data </span><span>#</span><span> 合并数据</span><span> self._data.update(other_userdata._data)
</span><span>def</span><span> extract(self, keys, remapping):
ud </span>=<span> UserData()
reverse_remapping </span>= {remapping[k]: k <span>for</span> k <span>in</span><span> remapping}
</span><span>if</span> len(reverse_remapping) !=<span> len(remapping):
smach.logerr(</span><span>"</span><span>SMACH userdata remapping is not one-to-one: </span><span>"</span> +<span> str(remapping))
</span><span>for</span> k <span>in</span><span> keys:
rmk </span>=<span> k
</span><span>if</span> k <span>in</span><span> reverse_remapping:
rmk </span>=<span> reverse_remapping[k]
ud[rmk] </span>=<span> copy.copy(self[k])
</span><span>return</span><span> ud
</span><span>def</span><span> merge(self, ud, keys, remapping):
</span><span>for</span> k <span>in</span><span> keys:
rmk </span>=<span> k
</span><span>if</span> k <span>in</span><span> remapping:
rmk </span>=<span> remapping[k]
self[rmk] </span>=<span> copy.copy(ud[k])
</span><span>def</span><span>__getitem__</span><span>(self, key):
</span><span>return</span> self.<span>__getattr__</span><span>(key)
</span><span>def</span><span>__setitem__</span><span>(self, key, item):
self._data[key] </span>=<span> item
</span><span>def</span><span> keys(self):
</span><span>return</span><span> list(self._data.keys())
</span><span>def</span><span>__contains__</span><span>(self,key):
</span><span>return</span> key <span>in</span><span> self._data
</span><span>def</span><span>__getattr__</span><span>(self, name):
</span><span>"""</span><span>Override getattr to be thread safe.</span><span>"""</span><span>‘‘‘</span><span>重写getattr是线程安全的</span><span>‘‘‘</span><span>if</span> name[0] == <span>‘</span><span>_</span><span>‘</span><span>:
</span><span>return</span> object.<span>__getattr__</span><span>(self, name)
</span><span>if</span><span>not</span> name <span>in</span><span> self._locks.keys():
self._locks[name] </span>=<span> threading.Lock()
</span><span>try</span><span>:
with self._locks[name]:
temp </span>=<span> self._data[name]
</span><span>except</span><span>:
smach.logerr(</span><span>"</span><span>Userdata key ‘%s‘ not available. Available keys are: %s</span><span>"</span> %<span> (name, str(list(self._data.keys()))))
</span><span>raise</span><span> KeyError()
</span><span>return</span><span> temp
</span><span>def</span><span>__setattr__</span><span>(self, name, value):
</span><span>"""</span><span>Override setattr to be thread safe.</span><span>"""</span><span>‘‘‘</span><span>重写setattr是线程安全的</span><span>‘‘‘</span><span>#</span><span> If we‘re still in __init__ don‘t do anything special</span><span>if</span> name[0] == <span>‘</span><span>_</span><span>‘</span><span>or</span><span>‘</span><span>_UserData__initialized</span><span>‘</span><span>not</span><span>in</span> self.<span>__dict__</span><span>:
</span><span>return</span> object.<span>__setattr__</span><span>(self, name, value)
</span><span>if</span><span>not</span> name <span>in</span><span> self._locks.keys():
self._locks[name] </span>=<span> threading.Lock()
self._locks[name].acquire()
self._data[name] </span>=<span> value
self._locks[name].release()
</span><span>#</span><span> Const wrapper</span><span>def</span><span> get_const(obj):
</span><span>"""</span><span>Get a const reference to an object if it has "user-defined" attributes.</span><span>"""</span><span>‘‘‘</span><span>如果obj有“user-defined”属性,那么返回obj的一个常引用</span><span>‘‘‘</span><span>if</span> hasattr(obj,<span>‘</span><span>__dict__</span><span>‘</span><span>):
smach.logdebug(</span><span>"</span><span>Making const ‘%s‘</span><span>"</span> %<span> str(obj))
</span><span>return</span><span> Const(obj)
</span><span>else</span><span>:
</span><span>return</span><span> obj
</span><span>#</span><span>object对象常量化</span><span>class</span><span> Const(object):
</span><span>"""</span><span>Wrapper that treats "user-defined" fields as immutable.
This wrapper class is used when user data keys are specified as input keys,
but not as output keys.
</span><span>"""</span><span>‘‘‘</span><span>一个对object的封装器,将object中的“user-defined”的域设置为不可改变,即只读
当用户数据的keys中被指定了input keys,但没有 output keys时,会用到这个封装器。
</span><span>‘‘‘</span><span>def</span><span>__init__</span><span>(self, obj):
smach.logdebug(</span><span>"</span><span>Making const ‘%s‘</span><span>"</span> %<span> str(obj))
self._obj </span>=<span> obj
self.</span><span>__initialized</span> =<span> True
</span><span>def</span><span>__getattr__</span><span>(self, name):
smach.logdebug(</span><span>"</span><span>Getting ‘%s‘ from const wrapper.</span><span>"</span> %<span> name)
attr </span>=<span> getattr(self._obj,name)
</span><span>return</span><span> get_const(attr)
</span><span>def</span><span>__getitem__</span><span>(self, name):
smach.logdebug(</span><span>"</span><span>Getting ‘%s‘ from const wrapper.</span><span>"</span> %<span> name)
attr </span>=<span> self._obj[name]
</span><span>return</span><span> get_const(attr)
</span><span>def</span><span>__setattr__</span><span>(self, name, value):
</span><span>if</span><span>‘</span><span>_const__initialized</span><span>‘</span><span>not</span><span>in</span> self.<span>__dict__</span><span>:
</span><span>return</span> object.<span>__setattr__</span><span>(self, name, value)
smach.logerr(</span><span>"</span><span>Attempting to set ‘%s‘ but this member is read-only.</span><span>"</span> %<span> name)
</span><span>raise</span><span> TypeError()
</span><span>def</span><span>__delattr__</span><span>(self, name):
smach.logerr(</span><span>"</span><span>Attempting to delete ‘%s‘ but this member is read-only.</span><span>"</span> %<span> name)
</span><span>raise</span><span> TypeError()
</span><span>#</span><span>用户数据结构的键值重映射</span><span>class</span><span> Remapper(object):
</span><span>"""</span><span>Key-remapping proxy to a SMACH userdata structure.</span><span>"""</span><span>‘‘‘</span><span>对Smach 用户数据结构的一个键值重映射代理</span><span>‘‘‘</span><span>def</span><span>__init__</span>(self, ud, input_keys=[], output_keys=[], remapping=<span>{}):
self._ud </span>=<span> ud
self._input </span>=<span> input_keys
self._output </span>=<span> output_keys
self._map </span>=<span> remapping
self.</span><span>__initialized</span> =<span> True
</span><span>def</span><span> _remap(self, key):
</span><span>"""</span><span>Return either the key or it‘s remapped value.</span><span>"""</span><span>‘‘‘</span><span>返回key‘s value或者remapped’s value</span><span>‘‘‘</span><span>if</span> key <span>in</span><span> self._map:
</span><span>return</span><span> self._map[key]
</span><span>return</span><span> key
</span><span>def</span><span> update(self, other_userdata):
self._ud.update(other_userdata)
</span><span>def</span><span>__getitem__</span>(self, key):<span>#</span><span>“__“两个下划线表示内置函数,类似inline?;“_“一个下划线表示私有函数</span><span>if</span> key <span>not</span><span>in</span><span> self._input:
</span><span>raise</span> smach.InvalidUserCodeError(<span>"</span><span>Reading from SMACH userdata key ‘%s‘ but the only keys that were declared as input to this state were: %s. This key needs to be declaread as input to this state. </span><span>"</span> %<span> (key, self._input))
</span><span>if</span> key <span>not</span><span>in</span><span> self._output:
</span><span>return</span> get_const(self._ud.<span>__getitem__</span><span>(self._remap(key)))
</span><span>return</span> self._ud.<span>__getitem__</span><span>(self._remap(key))
</span><span>def</span><span>__setitem__</span><span>(self, key, item):
</span><span>if</span> key <span>not</span><span>in</span><span> self._output:
smach.logerr(</span><span>"</span><span>Writing to SMACH userdata key ‘%s‘ but the only keys that were declared as output from this state were: %s.</span><span>"</span> %<span> (key, self._output))
</span><span>return</span><span>
self._ud.</span><span>__setitem__</span><span>(self._remap(key),item)
</span><span>def</span><span> keys(self):
</span><span>return</span> [self._remap(key) <span>for</span> key <span>in</span> self._ud.keys() <span>if</span> key <span>in</span><span> self._input]
</span><span>def</span><span>__contains__</span><span>(self,key):
</span><span>if</span> key <span>in</span><span> self._input:
</span><span>return</span> self._remap(key) <span>in</span><span> self._ud
</span><span>else</span><span>:
</span><span>return</span><span> False
</span><span>def</span><span>__getattr__</span><span>(self, name):
</span><span>if</span> name[0] == <span>‘</span><span>_</span><span>‘</span><span>:
</span><span>return</span> object.<span>__getattr__</span><span>(self, name)
</span><span>if</span> name <span>not</span><span>in</span><span> self._input:
</span><span>raise</span> smach.InvalidUserCodeError(<span>"</span><span>Reading from SMACH userdata key ‘%s‘ but the only keys that were declared as input to this state were: %s. This key needs to be declaread as input to this state. </span><span>"</span> %<span> (name, self._input))
</span><span>if</span> name <span>not</span><span>in</span><span> self._output:
</span><span>return</span><span> get_const(getattr(self._ud, self._remap(name)))
</span><span>return</span><span> getattr(self._ud, self._remap(name))
</span><span>def</span><span>__setattr__</span><span>(self, name, value):
</span><span>if</span> name[0] == <span>‘</span><span>_</span><span>‘</span><span>or</span><span>‘</span><span>_Remapper__initialized</span><span>‘</span><span>not</span><span>in</span> self.<span>__dict__</span><span>:
</span><span>return</span> object.<span>__setattr__</span><span>(self, name, value)
</span><span>if</span> name <span>not</span><span>in</span><span> self._output:
smach.logerr(</span><span>"</span><span>Writing to SMACH userdata key ‘%s‘ but the only keys that were declared as output from this state were: %s.</span><span>"</span> %<span> (name, self._output))
</span><span>return</span><span> None
setattr(self._ud, self._remap(name), value)</span>
2、例子-用户数据和键值重映射
参考ROS的smach例程,例子:The example of Passing User Data between States
<span>#</span>
<span>!/usr/bin/env python</span>
<span>import</span> roslib; roslib.load_manifest(<span>‘</span><span>smach_tutorials</span><span>‘</span><span>)
</span><span>import</span><span> rospy
</span><span>import</span><span> smach
</span><span>import</span><span> smach_ros
</span><span>#</span><span> define state Foo</span><span>class</span><span> Foo(smach.State):
</span><span>def</span><span>__init__</span><span>(self):
smach.State.</span><span>__init__</span><span>(self,
outcomes</span>=[<span>‘</span><span>outcome1</span><span>‘</span>,<span>‘</span><span>outcome2</span><span>‘</span><span>],
input_keys</span>=[<span>‘</span><span>foo_counter_in</span><span>‘</span><span>],#键初始化
output_keys</span>=[<span>‘</span><span>foo_counter_out</span><span>‘</span><span>])
</span><span>def</span><span> execute(self, userdata):#用户数据做为参数使用
rospy.loginfo(</span><span>‘</span><span>Executing state FOO</span><span>‘</span><span>)
</span><span>if</span> userdata.foo_counter_in < 3<span>:
userdata.foo_counter_out </span>= userdata.foo_counter_in + 1
<span>return</span><span>‘</span><span>outcome1</span><span>‘</span><span>else</span><span>:
</span><span>return</span><span>‘</span><span>outcome2</span><span>‘</span><span>#</span><span> define state Bar</span><span>class</span><span> Bar(smach.State):
</span><span>def</span><span>__init__</span><span>(self):
smach.State.</span><span>__init__</span><span>(self,
outcomes</span>=[<span>‘</span><span>outcome1</span><span>‘</span><span>],
input_keys</span>=[<span>‘</span><span>bar_counter_in</span><span>‘</span><span>])#键对初始化
</span><span>def</span><span> execute(self, userdata):#用户数据做为参数使用
rospy.loginfo(</span><span>‘</span><span>Executing state BAR</span><span>‘</span><span>)
rospy.loginfo(</span><span>‘</span><span>Counter = %f</span><span>‘</span>%<span>userdata.bar_counter_in)
</span><span>return</span><span>‘</span><span>outcome1</span><span>‘</span><span>def</span><span> main():
rospy.init_node(</span><span>‘</span><span>smach_example_state_machine</span><span>‘</span><span>)
</span><span>#</span><span> Create a SMACH state machine</span>
sm = smach.StateMachine(outcomes=[<span>‘</span><span>outcome4</span><span>‘</span><span>])
sm.userdata.sm_counter </span>=<span> 0
</span><span>#</span><span> Open the container</span><span> with sm:
</span><span>#</span><span> Add states to the container</span>
smach.StateMachine.add(<span>‘</span><span>FOO</span><span>‘</span><span>, Foo(),
transitions</span>={<span>‘</span><span>outcome1</span><span>‘</span>:<span>‘</span><span>BAR</span><span>‘</span><span>,
</span><span>‘</span><span>outcome2</span><span>‘</span>:<span>‘</span><span>outcome4</span><span>‘</span><span>},
remapping</span>={<span>‘</span><span>foo_counter_in</span><span>‘</span>:<span>‘</span><span>sm_counter</span><span>‘</span><span>,
</span><span>‘</span><span>foo_counter_out</span><span>‘</span>:<span>‘</span><span>sm_counter</span><span>‘</span><span>})<br />
smach.StateMachine.add(</span><span>‘</span><span>BAR</span><span>‘</span><span>, Bar(),
transitions</span>={<span>‘</span><span>outcome1</span><span>‘</span>:<span>‘</span><span>FOO</span><span>‘</span><span>},
remapping</span>={<span>‘</span><span>bar_counter_in</span><span>‘</span>:<span>‘</span><span>sm_counter</span><span>‘</span><span>})#键值重映射,重映射的名字可以根据具体的业务指定,这样更易于理解
</span><span>#</span><span> Execute SMACH plan</span>
outcome =<span> sm.execute()
</span><span>if</span><span>__name__</span> == <span>‘</span><span>__main__</span><span>‘</span><span>:
main()</span>
原文:http://www.cnblogs.com/cv-pr/p/5209072.html
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!