00001
00002
00003
00004
00005 import numpy
00006 import random
00007
00008 import roslib
00009 import roslib.message
00010 import rospy
00011 from opcua import ua, uamethod
00012
00013 import ros_actions
00014 import ros_server
00015 import rostopic
00016
00017
00018 class OpcUaROSTopic:
00019 def __init__(self, server, parent, idx, topic_name, topic_type):
00020 self.server = server
00021 self.parent = self.recursive_create_objects(topic_name, idx, parent)
00022 self.type_name = topic_type
00023 self.name = topic_name
00024 self._nodes = {}
00025 self.idx = idx
00026
00027 self.message_class = None
00028 try:
00029 self.message_class = roslib.message.get_message_class(topic_type)
00030 self.message_instance = self.message_class()
00031
00032 except rospy.ROSException:
00033 self.message_class = None
00034 rospy.logfatal("Couldn't find message class for type " + topic_type)
00035
00036 self._recursive_create_items(self.parent, idx, topic_name, topic_type, self.message_instance, True)
00037
00038 self._subscriber = rospy.Subscriber(self.name, self.message_class, self.message_callback)
00039 self._publisher = rospy.Publisher(self.name, self.message_class, queue_size=1)
00040 rospy.loginfo("Created ROS Topic with name: " + str(self.name))
00041
00042 def _recursive_create_items(self, parent, idx, topic_name, type_name, message, top_level=False):
00043 topic_text = topic_name.split('/')[-1]
00044 if '[' in topic_text:
00045 topic_text = topic_text[topic_text.index('['):]
00046
00047
00048 if hasattr(message, '__slots__') and hasattr(message, '_slot_types'):
00049 complex_type = True
00050 new_node = parent.add_object(ua.NodeId(topic_name, parent.nodeid.NamespaceIndex, ua.NodeIdType.String),
00051 ua.QualifiedName(topic_name, parent.nodeid.NamespaceIndex))
00052 new_node.add_property(ua.NodeId(topic_name + ".Type", idx),
00053 ua.QualifiedName("Type", parent.nodeid.NamespaceIndex), type_name)
00054 if top_level:
00055 new_node.add_method(ua.NodeId(topic_name + ".Update", parent.nodeid.NamespaceIndex),
00056 ua.QualifiedName("Update", parent.nodeid.NamespaceIndex),
00057 self.opcua_update_callback, [], [])
00058 for slot_name, type_name_child in zip(message.__slots__, message._slot_types):
00059 self._recursive_create_items(new_node, idx, topic_name + '/' + slot_name, type_name_child,
00060 getattr(message, slot_name))
00061 self._nodes[topic_name] = new_node
00062
00063 else:
00064
00065 base_type_str, array_size = _extract_array_info(type_name)
00066 try:
00067 base_instance = roslib.message.get_message_class(base_type_str)()
00068 except (ValueError, TypeError):
00069 base_instance = None
00070
00071 if array_size is not None and hasattr(base_instance, '__slots__'):
00072 for index in range(array_size):
00073 self._recursive_create_items(parent, idx, topic_name + '[%d]' % index, base_type_str, base_instance)
00074 else:
00075 new_node = _create_node_with_type(parent, idx, topic_name, topic_text, type_name, array_size)
00076 self._nodes[topic_name] = new_node
00077
00078 if topic_name in self._nodes and self._nodes[topic_name].get_node_class() == ua.NodeClass.Variable:
00079 self._nodes[topic_name].set_writable(True)
00080 return
00081
00082 def message_callback(self, message):
00083 self.update_value(self.name, message)
00084
00085 @uamethod
00086 def opcua_update_callback(self, parent):
00087 try:
00088 for nodeName in self._nodes:
00089 child = self._nodes[nodeName]
00090 name = child.get_display_name().Text
00091 if hasattr(self.message_instance, name):
00092 if child.get_node_class() == ua.NodeClass.Variable:
00093 setattr(self.message_instance, name,
00094 correct_type(child, type(getattr(self.message_instance, name))))
00095 elif child.get_node_class == ua.NodeClass.Object:
00096 setattr(self.message_instance, name, self.create_message_instance(child))
00097 self._publisher.publish(self.message_instance)
00098 except rospy.ROSException as e:
00099 rospy.logerr("Error when updating node " + self.name, e)
00100 self.server.server.delete_nodes([self.parent])
00101
00102 def update_value(self, topic_name, message):
00103 if hasattr(message, '__slots__') and hasattr(message, '_slot_types'):
00104 for slot_name in message.__slots__:
00105 self.update_value(topic_name + '/' + slot_name, getattr(message, slot_name))
00106
00107 elif type(message) in (list, tuple):
00108 if (len(message) > 0) and hasattr(message[0], '__slots__'):
00109 for index, slot in enumerate(message):
00110 if topic_name + '[%d]' % index in self._nodes:
00111 self.update_value(topic_name + '[%d]' % index, slot)
00112 else:
00113 if topic_name in self._nodes:
00114 base_type_str, _ = _extract_array_info(
00115 self._nodes[topic_name].text(self.type_name))
00116 self._recursive_create_items(self._nodes[topic_name], topic_name + '[%d]' % index,
00117 base_type_str,
00118 slot, None)
00119
00120 if topic_name in self._nodes:
00121 if len(message) < len(self._nodes[topic_name].get_children()):
00122 for i in range(len(message), self._nodes[topic_name].childCount()):
00123 item_topic_name = topic_name + '[%d]' % i
00124 self.recursive_delete_items(self._nodes[item_topic_name])
00125 del self._nodes[item_topic_name]
00126 else:
00127 if topic_name in self._nodes and self._nodes[topic_name] is not None:
00128 self._nodes[topic_name].set_value(repr(message))
00129
00130 def recursive_delete_items(self, item):
00131 self._publisher.unregister()
00132 self._subscriber.unregister()
00133 for child in item.get_children():
00134 self.recursive_delete_items(child)
00135 if child in self._nodes:
00136 del self._nodes[child]
00137 self.server.server.delete_nodes([child])
00138 self.server.server.delete_nodes([item])
00139 if len(self.parent.get_children()) == 0:
00140 self.server.server.delete_nodes([self.parent])
00141
00142 def create_message_instance(self, node):
00143 for child in node.get_children():
00144 name = child.get_display_name().Text
00145 if hasattr(self.message_instance, name):
00146 if child.get_node_class() == ua.NodeClass.Variable:
00147 setattr(self.message_instance, name,
00148 correct_type(child, type(getattr(self.message_instance, name))))
00149 elif child.get_node_class == ua.NodeClass.Object:
00150 setattr(self.message_instance, name, self.create_message_instance(child))
00151 return self.message_instance
00152
00153 def recursive_create_objects(self, topic_name, idx, parent):
00154 hierachy = topic_name.split('/')
00155 if len(hierachy) == 0 or len(hierachy) == 1:
00156 return parent
00157 for name in hierachy:
00158 if name != '':
00159 try:
00160 nodewithsamename = self.server.find_topics_node_with_same_name(name, idx)
00161 if nodewithsamename is not None:
00162 return self.recursive_create_objects(ros_server.nextname(hierachy, hierachy.index(name)), idx,
00163 nodewithsamename)
00164 else:
00165
00166
00167 newparent = parent.add_object(
00168 ua.NodeId(name, parent.nodeid.NamespaceIndex, ua.NodeIdType.String),
00169 ua.QualifiedName(name, parent.nodeid.NamespaceIndex))
00170 return self.recursive_create_objects(ros_server.nextname(hierachy, hierachy.index(name)), idx,
00171 newparent)
00172
00173 except IndexError, common.UaError:
00174 newparent = parent.add_object(
00175 ua.NodeId(name + str(random.randint(0, 10000)), parent.nodeid.NamespaceIndex,
00176 ua.NodeIdType.String),
00177 ua.QualifiedName(name, parent.nodeid.NamespaceIndex))
00178 return self.recursive_create_objects(ros_server.nextname(hierachy, hierachy.index(name)), idx,
00179 newparent)
00180
00181 return parent
00182
00183
00184
00185
00186 def correct_type(node, typemessage):
00187 data_value = node.get_data_value()
00188 result = node.get_value()
00189 if isinstance(data_value, ua.DataValue):
00190 if typemessage.__name__ == "float":
00191 result = numpy.float(result)
00192 if typemessage.__name__ == "double":
00193 result = numpy.double(result)
00194 if typemessage.__name__ == "int":
00195 result = int(result) & 0xff
00196 else:
00197 rospy.logerr("can't convert: " + str(node.get_data_value.Value))
00198 return None
00199 return result
00200
00201
00202 def _extract_array_info(type_str):
00203 array_size = None
00204 if '[' in type_str and type_str[-1] == ']':
00205 type_str, array_size_str = type_str.split('[', 1)
00206 array_size_str = array_size_str[:-1]
00207 if len(array_size_str) > 0:
00208 array_size = int(array_size_str)
00209 else:
00210 array_size = 0
00211
00212 return type_str, array_size
00213
00214
00215 def _create_node_with_type(parent, idx, topic_name, topic_text, type_name, array_size):
00216 if '[' in type_name:
00217 type_name = type_name[:type_name.index('[')]
00218
00219 if type_name == 'bool':
00220 dv = ua.Variant(False, ua.VariantType.Boolean)
00221 elif type_name == 'byte':
00222 dv = ua.Variant(0, ua.VariantType.Byte)
00223 elif type_name == 'int':
00224 dv = ua.Variant(0, ua.VariantType.Int32)
00225 elif type_name == 'int8':
00226 dv = ua.Variant(0, ua.VariantType.SByte)
00227 elif type_name == 'uint8':
00228 dv = ua.Variant(0, ua.VariantType.Byte)
00229 elif type_name == 'int16':
00230 dv = ua.Variant(0, ua.VariantType.Int16)
00231 elif type_name == 'uint16':
00232 dv = ua.Variant(0, ua.VariantType.UInt16)
00233 elif type_name == 'int32':
00234 dv = ua.Variant(0, ua.VariantType.Int32)
00235 elif type_name == 'uint32':
00236 dv = ua.Variant(0, ua.VariantType.UInt32)
00237 elif type_name == 'int64':
00238 dv = ua.Variant(0, ua.VariantType.Int64)
00239 elif type_name == 'uint64':
00240 dv = ua.Variant(0, ua.VariantType.UInt64)
00241 elif type_name == 'float' or type_name == 'float32' or type_name == 'float64':
00242 dv = ua.Variant(0.0, ua.VariantType.Float)
00243 elif type_name == 'double':
00244 dv = ua.Variant(0.0, ua.VariantType.Double)
00245 elif type_name == 'string':
00246 dv = ua.Variant('', ua.VariantType.String)
00247 else:
00248 rospy.logerr("can't create node with type" + str(type_name))
00249 return None
00250
00251 if array_size is not None:
00252 value = []
00253 for i in range(array_size):
00254 value.append(i)
00255 return parent.add_variable(ua.NodeId(topic_name, parent.nodeid.NamespaceIndex),
00256 ua.QualifiedName(topic_text, parent.nodeid.NamespaceIndex), dv.Value)
00257
00258
00259
00260 def numberofsubscribers(nametolookfor, topicsDict):
00261
00262 if nametolookfor != "/rosout":
00263 ret = topicsDict[nametolookfor]._subscriber.get_num_connections()
00264 else:
00265 ret = 2
00266 return ret
00267
00268
00269 def refresh_topics_and_actions(namespace_ros, server, topicsdict, actionsdict, idx_topics, idx_actions, topics,
00270 actions):
00271 ros_topics = rospy.get_published_topics(namespace_ros)
00272 rospy.logdebug(str(ros_topics))
00273 rospy.logdebug(str(rospy.get_published_topics('/move_base_simple')))
00274 for topic_name, topic_type in ros_topics:
00275 if topic_name not in topicsdict or topicsdict[topic_name] is None:
00276 if "cancel" in topic_name or "result" in topic_name or "feedback" in topic_name or "goal" in topic_name or "status" in topic_name:
00277 rospy.logdebug("Found an action: " + str(topic_name))
00278 correct_name = ros_actions.get_correct_name(topic_name)
00279 if correct_name not in actionsdict:
00280 rospy.loginfo("Creating Action with name: " + correct_name)
00281 try:
00282 actionsdict[correct_name] = ros_actions.OpcUaROSAction(server, actions, idx_actions,
00283 correct_name,
00284 get_goal_type(correct_name),
00285 get_feedback_type(
00286 correct_name))
00287 except (ValueError, TypeError, AttributeError) as e:
00288 print(e)
00289 rospy.logerr("Error while creating Action Objects for Action " + topic_name)
00290
00291 else:
00292
00293 topic = OpcUaROSTopic(server, topics, idx_topics, topic_name, topic_type)
00294 topicsdict[topic_name] = topic
00295 elif numberofsubscribers(topic_name, topicsdict) <= 1 and "rosout" not in topic_name:
00296 topicsdict[topic_name].recursive_delete_items(server.server.get_node(ua.NodeId(topic_name, idx_topics)))
00297 del topicsdict[topic_name]
00298 ros_server.own_rosnode_cleanup()
00299
00300 ros_topics = rospy.get_published_topics(namespace_ros)
00301
00302 tobedeleted = []
00303 for topic_nameOPC in topicsdict:
00304 found = False
00305 for topicROS, topic_type in ros_topics:
00306 if topic_nameOPC == topicROS:
00307 found = True
00308 if not found:
00309 topicsdict[topic_nameOPC].recursive_delete_items(server.get_node(ua.NodeId(topic_nameOPC, idx_topics)))
00310 tobedeleted.append(topic_nameOPC)
00311 for name in tobedeleted:
00312 del topicsdict[name]
00313 ros_actions.refresh_dict(namespace_ros, actionsdict, topicsdict, server, idx_actions)
00314
00315
00316 def get_feedback_type(action_name):
00317 try:
00318 type, name, fn = rostopic.get_topic_type(action_name + "/feedback")
00319 return type
00320 except rospy.ROSException as e:
00321 try:
00322 type, name, fn = rostopic.get_topic_type(action_name + "/Feedback", e)
00323 return type
00324 except rospy.ROSException as e2:
00325 rospy.logerr("Couldnt find feedback type for action " + action_name, e2)
00326 return None
00327
00328
00329 def get_goal_type(action_name):
00330 try:
00331 type, name, fn = rostopic.get_topic_type(action_name + "/goal")
00332 return type
00333 except rospy.ROSException as e:
00334 try:
00335 type, name, fn = rostopic.get_topic_type(action_name + "/Goal", e)
00336 return type
00337 except rospy.ROSException as e2:
00338 rospy.logerr("Couldnt find feedback type for action " + action_name, e2)
00339 return None