behavior_action_server.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 import rospy
3 import actionlib
4 
5 from flexbe_msgs.msg import *
6 from rospkg import RosPack
7 from flexbe_core import BehaviorLibrary
8 
9 from std_msgs.msg import String, Empty
10 
11 import zlib
12 import difflib
13 import os
14 import yaml
15 import xml.etree.ElementTree as ET
16 
17 
18 class BehaviorActionServer(object):
19 
20  def __init__(self):
21  self._behavior_started = False
22  self._preempt_requested = False
23  self._current_state = None
24  self._active_behavior_id = None
25 
26  self._pub = rospy.Publisher('flexbe/start_behavior', BehaviorSelection, queue_size=100)
27  self._preempt_pub = rospy.Publisher('flexbe/command/preempt', Empty, queue_size=100)
28  self._status_sub = rospy.Subscriber('flexbe/status', BEStatus, self._status_cb)
29  self._state_sub = rospy.Subscriber('flexbe/behavior_update', String, self._state_cb)
30 
31  self._as = actionlib.SimpleActionServer('flexbe/execute_behavior', BehaviorExecutionAction, None, False)
32  self._as.register_preempt_callback(self._preempt_cb)
33  self._as.register_goal_callback(self._goal_cb)
34 
35  self._rp = RosPack()
36  self._behavior_lib = BehaviorLibrary()
37 
38  # start action server after all member variables have been initialized
39  self._as.start()
40 
41  rospy.loginfo("%d behaviors available, ready for start request." % self._behavior_lib.count_behaviors())
42 
43 
44  def _goal_cb(self):
45  if self._as.is_active() or not self._as.is_new_goal_available():
46  return
47  goal = self._as.accept_new_goal()
48  rospy.loginfo('Received a new request to start behavior: %s' % goal.behavior_name)
49  be_id, behavior = self._behavior_lib.find_behavior(goal.behavior_name)
50  if be_id is None:
51  rospy.logerr("Deny goal: Did not find behavior with requested name %s" % goal.behavior_name)
52  self._as.set_preempted()
53  return
54 
55  be_selection = BehaviorSelection()
56  be_selection.behavior_id = be_id
57  be_selection.autonomy_level = 255
58  try:
59  for k, v in zip(goal.arg_keys, goal.arg_values):
60  if v.startswith('file://'):
61  v = v.replace('file://', '', 1)
62  path = v.split(':')[0]
63  if len(v.split(':')) > 1:
64  ns = v.split(':')[1]
65  else:
66  ns = ''
67  if path.startswith('~') or path.startswith('/'):
68  filepath = os.path.expanduser(path)
69  else:
70  filepath = os.path.join(self._rp.get_path(path.split('/')[0]), '/'.join(path.split('/')[1:]))
71  with open(filepath, 'r') as f:
72  content = f.read()
73  if ns != '':
74  content = getattr(yaml, 'full_load', yaml.load)(content)
75  if ns in content:
76  content = content[ns]
77  content = yaml.dump(content)
78  be_selection.arg_keys.append(k)
79  be_selection.arg_values.append(content)
80  else:
81  be_selection.arg_keys.append(k)
82  be_selection.arg_values.append(v)
83  except Exception as e:
84  rospy.logwarn('Failed to parse and substitute behavior arguments, will use direct input.\n%s' % str(e))
85  be_selection.arg_keys = goal.arg_keys
86  be_selection.arg_values = goal.arg_values
87  be_selection.input_keys = goal.input_keys
88  be_selection.input_values = goal.input_values
89 
90  # check for local modifications of the behavior to send them to the onboard behavior
91  be_filepath_new = self._behavior_lib.get_sourcecode_filepath(be_id)
92  with open(be_filepath_new, "r") as f:
93  be_content_new = f.read()
94 
95  be_filepath_old = self._behavior_lib.get_sourcecode_filepath(be_id, add_tmp=True)
96  if not os.path.isfile(be_filepath_old):
97  be_selection.behavior_checksum = zlib.adler32(be_content_new.encode()) & 0x7fffffff
98  else:
99  with open(be_filepath_old, "r") as f:
100  be_content_old = f.read()
101 
102  sqm = difflib.SequenceMatcher(a=be_content_old, b=be_content_new)
103  diffs = [x[1] for x in sqm.get_grouped_opcodes(0)]
104  for opcode, a0, a1, b0, b1 in diffs:
105  content = be_content_new[b0:b1]
106  be_selection.modifications.append(BehaviorModification(a0, a1, content))
107 
108  be_selection.behavior_checksum = zlib.adler32(be_content_new.encode()) & 0x7fffffff
109 
110  # reset state before starting new behavior
111  self._current_state = None
112  self._behavior_started = False
113  self._preempt_requested = False
114 
115  # start new behavior
116  self._pub.publish(be_selection)
117 
118 
119  def _preempt_cb(self):
120  self._preempt_requested = True
121  if not self._behavior_started:
122  return
123  self._preempt_pub.publish()
124  rospy.loginfo('Behavior execution preempt requested!')
125 
126 
127  def _status_cb(self, msg):
128  if msg.code == BEStatus.ERROR:
129  rospy.logerr('Failed to run behavior! Check onboard terminal for further infos.')
130  self._as.set_aborted('')
131  # Call goal cb in case there is a queued goal available
132  self._goal_cb()
133  return
134  if not self._behavior_started and msg.code == BEStatus.STARTED:
135  self._behavior_started = True
136  self._active_behavior_id = msg.behavior_id
137  rospy.loginfo('Behavior execution has started!')
138  # Preempt if the goal was asked to preempt before the behavior started
139  if self._preempt_requested:
140  self._preempt_cb()
141  # Ignore status until behavior start was received
142  if not self._behavior_started:
143  return
144 
145  if msg.behavior_id != self._active_behavior_id:
146  rospy.logwarn('Ignored status because behavior id differed ({} vs {})!'.format(msg.behavior_id, self._active_behavior_id))
147  return
148  elif msg.code == BEStatus.FINISHED:
149  result = msg.args[0] if len(msg.args) >= 1 else ''
150  rospy.loginfo('Finished behavior execution with result "%s"!' % result)
151  self._as.set_succeeded(BehaviorExecutionResult(outcome=result))
152  # Call goal cb in case there is a queued goal available
153  self._goal_cb()
154  elif msg.code == BEStatus.FAILED:
155  rospy.logerr('Behavior execution failed in state %s!' % str(self._current_state))
156  self._as.set_aborted('')
157  # Call goal cb in case there is a queued goal available
158  self._goal_cb()
159 
160 
161  def _state_cb(self, msg):
162  self._current_state = msg.data
163  if self._as.is_active():
164  self._as.publish_feedback(BehaviorExecutionFeedback(self._current_state))
165  rospy.loginfo('Current state: %s' % self._current_state)


flexbe_widget
Author(s): Philipp Schillinger
autogenerated on Sun Dec 13 2020 04:01:47