ros_actions.py
Go to the documentation of this file.
00001 # !/usr/bin/python
00002 # thanks to https://github.com/ros-visualization/rqt_common_plugins/blob/groovy-devel/rqt_action/src/rqt_action/action_plugin.py
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         # malformed move_base_simple Action hack
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         # parent is our main node, this means our parent in log message above was actionsObject
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             # remove obsolete children
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         # rospy.logdebug("cancelling goal " + self.name)
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                         # if for some reason 2 services with exactly same name are created use hack>: add random int, prob to hit two
00168                         # same ints 1/10000, should be sufficient
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                 # thrown when node with parent name is not existent in server
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         # This here are 'complex data'
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             # This are arrays
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     # namespace
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                 # ignore header for malformed move_base_goal, as header shouldnt be in sent message
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                     # set the attribute in the request
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             # ignore header for malformed move_base_goal, as header shouldnt be in sent message
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                 # sets the object as an attribute in the request were trying to build
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]


ros_opcua_impl_python_opcua
Author(s): Denis Štogl , Daniel Draper
autogenerated on Sat Jun 8 2019 18:26:23