00001
00002
00003 import random
00004 from pydoc import locate
00005
00006 import actionlib
00007 import roslib
00008 import rospy
00009 from opcua import ua, common
00010 from opcua import uamethod
00011 from opcua.ua.uaerrors import UaError
00012 from roslib import message
00013
00014 import ros_server
00015 import ros_services
00016 import ros_topics
00017
00018
00019 class OpcUaROSAction:
00020 def __init__(self, server, parent, idx, name, action_type, feedback_type):
00021 self.server = server
00022 self.idx = idx
00023 self.name = name
00024 self.type = action_type.split("/")[0]
00025 self.feedback_type = feedback_type
00026 self._feedback_nodes = {}
00027 goal_name = "_" + action_type.split("/")[-1]
00028 msg_name = goal_name.replace("Goal", "")
00029 class_name = msg_name.replace("_", "", 1)
00030 rospy.logdebug("Trying to find module with name: " + self.type + ".msg." + goal_name.replace("Goal", ""))
00031 actionspec = locate(self.type + ".msg." + msg_name)
00032 rospy.logdebug("We are creating action: " + self.name)
00033 rospy.logdebug("We have type: " + self.type)
00034 rospy.logdebug("We have msg name: " + msg_name)
00035 rospy.logdebug("We have class name: " + class_name)
00036 rospy.logdebug("We have goal name: " + goal_name)
00037 rospy.logdebug("We have goal class name: " + goal_name.replace("_", "", 1))
00038
00039 goalspec = locate(self.type + ".msg." + goal_name)
00040 rospy.logdebug("found goalspec")
00041 self.goal_class = getattr(goalspec, goal_name.replace("_", "", 1))
00042
00043 if 'move_base_simple' in self.name:
00044 self.goal_instance = self.goal_class()
00045 else:
00046 self.goal_instance = self.goal_class().goal
00047 rospy.logdebug("found goal_instance " + str(self.goal_instance))
00048 try:
00049 self.client = actionlib.SimpleActionClient(self.get_ns_name(), getattr(actionspec, class_name))
00050 rospy.logdebug("We have created a SimpleActionClient for action " + self.name)
00051 except actionlib.ActionException as e:
00052 rospy.logerr("Error when creating ActionClient for action " + self.name, e)
00053 rospy.logdebug("Creating parent objects for action " + str(self.name))
00054 self.parent = self.recursive_create_objects(name, idx, parent)
00055 rospy.logdebug("Found parent for action: " + str(self.parent))
00056 rospy.logdebug("Creating main node with name " + self.name.split("/")[-1])
00057
00058 if self.name.split("/")[-1] == self.parent.nodeid.Identifier:
00059 self.main_node = self.parent
00060 else:
00061 self.main_node = self.parent.add_object(
00062 ua.NodeId(self.name.split("/")[-1], self.parent.nodeid.NamespaceIndex, ua.NodeIdType.String),
00063 ua.QualifiedName(self.name.split("/")[-1], self.parent.nodeid.NamespaceIndex))
00064 rospy.logdebug("Created Main Node under parent!")
00065 self.result = self.main_node.add_object(
00066 ua.NodeId(self.name + "_result", self.main_node.nodeid.NamespaceIndex, ua.NodeIdType.String),
00067 ua.QualifiedName("result", self.main_node.nodeid.NamespaceIndex))
00068 self.result_node = ros_topics._create_node_with_type(self.result, self.idx, self.name + "_result_value",
00069 self.name + "_result_value",
00070 "string", -1)
00071
00072 self.result_node.set_value("No goal completed yet")
00073 rospy.logdebug("Created result node")
00074 self.goal = self.main_node.add_object(
00075 ua.NodeId(self.name + "_goal", self.main_node.nodeid.NamespaceIndex, ua.NodeIdType.String),
00076 ua.QualifiedName("goal", parent.nodeid.NamespaceIndex))
00077 self.goal_node = self.goal.add_method(idx, self.name + "_send_goal", self.send_goal,
00078 getargarray(self.goal_instance), [])
00079
00080 self.goal_cancel = self.goal.add_method(idx, self.name + "_cancel_goal", self.cancel_goal, [], [])
00081
00082 rospy.logdebug("Created goal node")
00083
00084 self.feedback = self.main_node.add_object(
00085 ua.NodeId(self.name + "feedback", self.main_node.nodeid.NamespaceIndex, ua.NodeIdType.String),
00086 ua.QualifiedName("feedback", self.main_node.nodeid.NamespaceIndex))
00087 rospy.logdebug("Created feedback node")
00088 if self.feedback_type is not None:
00089 try:
00090 rospy.logdebug("We are trying to create Feedback for feedback type: " + self.feedback_type)
00091 self.feedback_message_class = roslib.message.get_message_class(self.feedback_type)
00092 self.feedback_message_instance = self.feedback_message_class()
00093 rospy.logdebug("Created feedback message instance")
00094
00095 except rospy.ROSException:
00096 self.message_class = None
00097 rospy.logerror("Didn't find feedback message class for type " + self.feedback_type)
00098
00099 self._recursive_create_feedback_items(self.feedback, self.name + "/feedback", self.feedback_type,
00100 getattr(self.feedback_message_instance, "feedback"))
00101
00102 self.status = self.main_node.add_object(
00103 ua.NodeId(self.name + "status", self.main_node.nodeid.NamespaceIndex, ua.NodeIdType.String),
00104 ua.QualifiedName("status", self.main_node.nodeid.NamespaceIndex))
00105 self.status_node = ros_topics._create_node_with_type(self.status, self.idx, self.name + "_status",
00106 self.name + "_status",
00107 "string", -1)
00108 self.status_node.set_value("No goal sent yet")
00109 rospy.loginfo("Created ROS Action with name: %s", self.name)
00110
00111 def message_callback(self, message):
00112 self.update_value(self.name + "/feedback", message)
00113
00114 def update_value(self, topic_name, message):
00115 if hasattr(message, '__slots__') and hasattr(message, '_slot_types'):
00116 for slot_name in message.__slots__:
00117 self.update_value(topic_name + '/' + slot_name, getattr(message, slot_name))
00118
00119 elif type(message) in (list, tuple):
00120 if (len(message) > 0) and hasattr(message[0], '__slots__'):
00121 for index, slot in enumerate(message):
00122 if topic_name + '[%d]' % index in self._feedback_nodes:
00123 self.update_value(topic_name + '[%d]' % index, slot)
00124 else:
00125 if topic_name in self._feedback_nodes:
00126 base_type_str, _ = ros_topics._extract_array_info(
00127 self._feedback_nodes[topic_name].text(self.feedback_type))
00128 self._recursive_create_items(self._feedback_nodes[topic_name], topic_name + '[%d]' % index,
00129 base_type_str,
00130 slot, None)
00131
00132 if topic_name in self._feedback_nodes:
00133 if len(message) < len(self._feedback_nodes[topic_name].get_children()):
00134 for i in range(len(message), self._feedback_nodes[topic_name].childCount()):
00135 item_topic_name = topic_name + '[%d]' % i
00136 self.recursive_delete_items(self._feedback_nodes[item_topic_name])
00137 del self._feedback_nodes[item_topic_name]
00138 else:
00139 if topic_name in self._feedback_nodes and self._feedback_nodes[topic_name] is not None:
00140 self._feedback_nodes[topic_name].set_value(repr(message))
00141
00142 @uamethod
00143 def cancel_goal(self, parent, *inputs):
00144
00145 try:
00146 self.client.cancel_all_goals()
00147 self.update_state()
00148 except (rospy.ROSException, UaError) as e:
00149 rospy.logerr("Error when cancelling a goal for " + self.name, e)
00150
00151 def recursive_create_objects(self, topic_name, idx, parent):
00152 rospy.logdebug("reached parent object creation! current parent: " + str(parent))
00153 hierachy = topic_name.split('/')
00154 rospy.logdebug("Current hierachy: " + str(hierachy))
00155 if len(hierachy) == 0 or len(hierachy) == 1:
00156 return parent
00157 for name in hierachy:
00158 rospy.logdebug("current name: " + str(name))
00159 if name != '':
00160 try:
00161 nodewithsamename = self.server.find_action_node_with_same_name(name, idx)
00162 if nodewithsamename is not None:
00163 rospy.logdebug("Found node with same name, is now new parent")
00164 return self.recursive_create_objects(ros_server.nextname(hierachy, hierachy.index(name)), idx,
00165 nodewithsamename)
00166 else:
00167
00168
00169 newparent = parent.add_object(
00170 ua.NodeId(name, parent.nodeid.NamespaceIndex, ua.NodeIdType.String),
00171 ua.QualifiedName(name, parent.nodeid.NamespaceIndex))
00172 return self.recursive_create_objects(ros_server.nextname(hierachy, hierachy.index(name)), idx,
00173 newparent)
00174
00175 except IndexError, UaError:
00176 newparent = parent.add_object(
00177 ua.NodeId(name + str(random.randint(0, 10000)), parent.nodeid.NamespaceIndex,
00178 ua.NodeIdType.String),
00179 ua.QualifiedName(name, parent.nodeid.NamespaceIndex))
00180 return self.recursive_create_objects(ros_server.nextname(hierachy, hierachy.index(name)), idx,
00181 newparent)
00182
00183 return parent
00184
00185 def _recursive_create_feedback_items(self, parent, feedback_topic_name, type_name, feedback_message):
00186 idx = self.idx
00187 topic_text = feedback_topic_name.split('/')[-1]
00188 if '[' in topic_text:
00189 topic_text = topic_text[topic_text.index('['):]
00190
00191
00192 if hasattr(feedback_message, '__slots__') and hasattr(feedback_message, '_slot_types'):
00193 complex_type = True
00194 new_node = parent.add_object(
00195 ua.NodeId(feedback_topic_name, parent.nodeid.NamespaceIndex, ua.NodeIdType.String),
00196 ua.QualifiedName(feedback_topic_name, parent.nodeid.NamespaceIndex))
00197
00198 for slot_name, type_name_child in zip(feedback_message.__slots__, feedback_message._slot_types):
00199 self._recursive_create_feedback_items(new_node, feedback_topic_name + '/' + slot_name, type_name_child,
00200 getattr(feedback_message, slot_name))
00201 self._feedback_nodes[feedback_topic_name] = new_node
00202
00203 else:
00204
00205 base_type_str, array_size = ros_topics._extract_array_info(type_name)
00206 try:
00207 base_instance = roslib.message.get_message_class(base_type_str)()
00208 except (ValueError, TypeError):
00209 base_instance = None
00210
00211 if array_size is not None and hasattr(base_instance, '__slots__'):
00212 for index in range(array_size):
00213 self._recursive_create_feedback_items(parent, feedback_topic_name + '[%d]' % index, base_type_str,
00214 base_instance)
00215 else:
00216 new_node = ros_topics._create_node_with_type(parent, idx, feedback_topic_name, topic_text, type_name,
00217 array_size)
00218 new_node.set_writable(True)
00219 self._feedback_nodes[feedback_topic_name] = new_node
00220 return
00221
00222
00223 def get_ns_name(self):
00224 splits = self.name.split("/")
00225 ns = splits[1:]
00226 res = ""
00227 for split in ns:
00228 res += split + "/"
00229 rospy.logdebug("Created ns name: " + res[:-1])
00230 return str(res[:-1])
00231
00232 @uamethod
00233 def send_goal(self, parent, *inputs):
00234 rospy.loginfo("Sending Goal for " + self.name)
00235 try:
00236 goal_msg = self.create_message_instance(inputs, self.goal_instance)
00237 if 'move_base' in self.name:
00238 rospy.loginfo("setting frame_id for move_base malformation")
00239 try:
00240 target_pose = getattr(goal_msg, "target_pose")
00241 header = getattr(target_pose, "header")
00242 setattr(header, "frame_id", "map")
00243 setattr(target_pose, "header", header)
00244 except AttributeError as e:
00245 rospy.logerr("Error occured when setting frame_id", e)
00246 rospy.loginfo("Created Message Instance for goal-send: " + str(goal_msg))
00247 self.client.send_goal(goal_msg, done_cb=self.update_result, feedback_cb=self.update_feedback,
00248 active_cb=self.update_state)
00249 return
00250 except Exception as e:
00251 rospy.logerr("Error occured during goal sending for Action " + str(self.name))
00252 print(e)
00253
00254 def create_message_instance(self, inputs, sample):
00255 rospy.logdebug("Creating message for goal call")
00256 already_set = []
00257 if isinstance(inputs, tuple):
00258 arg_counter = 0
00259 object_counter = 0
00260 while arg_counter < len(inputs) and object_counter < len(sample.__slots__):
00261 cur_arg = inputs[arg_counter]
00262 cur_slot = sample.__slots__[object_counter]
00263
00264 while cur_slot == 'header':
00265 rospy.logdebug("ignoring header")
00266 object_counter += 1
00267 if object_counter < len(sample.__slots__):
00268 cur_slot = sample.__slots__[object_counter]
00269 real_slot = getattr(sample, cur_slot)
00270 rospy.lodebug(
00271 "cur_arg: " + str(cur_arg) + " cur_slot_name: " + str(cur_slot) + " real slot content: " + str(
00272 real_slot))
00273 if hasattr(real_slot, '_type'):
00274 rospy.logdebug("We found an object with name " + str(cur_slot) + ", creating it recursively")
00275 arg_counter_before = arg_counter
00276 already_set, arg_counter = self.create_object_instance(already_set, real_slot, cur_slot,
00277 arg_counter, inputs, sample)
00278 if arg_counter != arg_counter_before:
00279 object_counter += 1
00280 rospy.logdebug("completed object, object counter: " + str(object_counter) + " len(object): " + str(
00281 len(sample.__slots__)))
00282 else:
00283 already_set.append(cur_slot)
00284
00285 setattr(sample, cur_slot, cur_arg)
00286 arg_counter += 1
00287 object_counter += 1
00288
00289 return sample
00290
00291 def create_object_instance(self, already_set, object, name, counter, inputs, parent):
00292 rospy.logdebug("Create Object Instance Notify")
00293 object_counter = 0
00294 while object_counter < len(object.__slots__) and counter < len(inputs):
00295 cur_arg = inputs[counter]
00296 cur_slot = object.__slots__[object_counter]
00297
00298 while cur_slot == 'header':
00299 rospy.logdebug("ignoring header")
00300 object_counter += 1
00301 if object_counter < len(object.__slots__):
00302 cur_slot = object.__slots__[object_counter]
00303 else:
00304 return already_set, counter
00305 real_slot = getattr(object, cur_slot)
00306 rospy.logdebug(
00307 "cur_arg: " + str(cur_arg) + " cur_slot_name: " + str(cur_slot) + " real slot content: " + str(
00308 real_slot))
00309 if hasattr(real_slot, '_type'):
00310 rospy.logdebug("Recursive Object found in request/response of service call")
00311 already_set, counter = self.create_object_instance(already_set, real_slot, cur_slot, counter, inputs,
00312 object)
00313 object_counter += 1
00314 else:
00315 already_set.append(cur_slot)
00316 setattr(object, cur_slot, cur_arg)
00317 object_counter += 1
00318 counter += 1
00319
00320 setattr(parent, name, object)
00321 return already_set, counter
00322
00323 def recursive_delete_items(self, item):
00324 self.client.cancel_all_goals()
00325 for child in item.get_children():
00326 self.recursive_delete_items(child)
00327 self.server.server.delete_nodes([child])
00328 self.server.server.delete_nodes([self.result, self.result_node, self.goal_node, self.goal, self.parent])
00329 ros_server.own_rosnode_cleanup()
00330
00331 def update_result(self, state, result):
00332 rospy.logdebug("updated result cb reached")
00333 self.status_node.set_value(map_status_to_string(state))
00334 self.result_node.set_value(repr(result))
00335
00336 def update_state(self):
00337 rospy.logdebug("updated state cb reached")
00338 self.status_node.set_value(repr(self.client.get_goal_status_text()))
00339
00340 def update_feedback(self, feedback):
00341 rospy.logdebug("updated feedback cb reached")
00342 self.message_callback(feedback)
00343
00344
00345 def get_correct_name(topic_name):
00346 rospy.logdebug("getting correct name for: " + str(topic_name))
00347 splits = topic_name.split("/")
00348 counter = 0
00349 counter2 = 0
00350 result = ""
00351 while counter < len(splits):
00352 if splits[-1] == splits[counter] and not counter == 1:
00353 while counter2 <= counter - 1:
00354 if counter2 != counter - 1:
00355 result += splits[counter2] + '/'
00356 else:
00357 result += splits[counter2]
00358 counter2 += 1
00359 return result
00360 counter += 1
00361
00362
00363 def getargarray(goal_class):
00364 array = []
00365 for slot_name in goal_class.__slots__:
00366 if slot_name != 'header':
00367 slot = getattr(goal_class, slot_name)
00368 if hasattr(slot, '_type'):
00369 array_to_merge = getargarray(slot)
00370 array.extend(array_to_merge)
00371 else:
00372 if isinstance(slot, list):
00373 rospy.logdebug("Found an Array Argument!")
00374 arg = ua.Argument()
00375 arg.Name = slot_name
00376 arg.DataType = ua.NodeId(ros_services.getobjectidfromtype("array"))
00377 arg.ValueRank = -1
00378 arg.ArrayDimensions = []
00379 arg.Description = ua.LocalizedText("Array")
00380 else:
00381 arg = ua.Argument()
00382 if hasattr(goal_class, "__name__"):
00383 arg.Name = goal_class.__name__ + slot_name
00384 else:
00385 arg.Name = slot_name
00386 arg.DataType = ua.NodeId(ros_services.getobjectidfromtype(type(slot).__name__))
00387 arg.ValueRank = -1
00388 arg.ArrayDimensions = []
00389 arg.Description = ua.LocalizedText(slot_name)
00390 array.append(arg)
00391
00392 return array
00393
00394
00395 def map_status_to_string(param):
00396 if param == 9:
00397 return "Goal LOST"
00398 elif param == 8:
00399 return "Goal RECALLED"
00400 elif param == 7:
00401 return "Goal RECALLING"
00402 elif param == 6:
00403 return "Goal PREEMPTING"
00404 elif param == 5:
00405 return "Goal REJECTED"
00406 elif param == 4:
00407 return "Goal ABORTED"
00408 elif param == 3:
00409 return "Goal SUCEEDED"
00410 elif param == 2:
00411 return "Goal PREEMPTED"
00412 elif param == 1:
00413 return "Goal ACTIVE"
00414 elif param == 0:
00415 return "Goal PENDING"
00416
00417
00418 def refresh_dict(namespace_ros, actionsdict, topicsdict, server, idx_actions):
00419 topics = rospy.get_published_topics(namespace_ros)
00420 tobedeleted = []
00421 for actionNameOPC in actionsdict:
00422 found = False
00423 for topicROS, topic_type in topics:
00424 ros_server.own_rosnode_cleanup()
00425 if actionNameOPC in topicROS:
00426 found = True
00427 if not found:
00428 actionsdict[actionNameOPC].recursive_delete_items(actionsdict[actionNameOPC].parent)
00429 tobedeleted.append(actionNameOPC)
00430 rospy.logdebug("deleting action: " + actionNameOPC)
00431 ros_server.own_rosnode_cleanup()
00432 for name in tobedeleted:
00433 del actionsdict[name]