00001
00002
00003 import AuxFuns
00004 import roslib.message
00005 import xml.etree.ElementTree as ET
00006 import yaml
00007 import json
00008
00009 from rospy_message_converter import message_converter
00010 from AuxFuns import our_raw_input
00011 from AuxFuns import message_raw_input
00012
00013 def validator(tree):
00014 configuration = tree.getroot().find("config")
00015 if configuration == None:
00016 print "This config file lacks the config tag."
00017 return False
00018
00019 buttonKeyDict = {}
00020 idMessagesDict = {}
00021 idTopicDict = {}
00022
00023
00024 buttons = configuration.find("buttons")
00025 if buttons == None:
00026 print "This config file lacks the buttons tag."
00027 return False
00028
00029 messages = configuration.find("messages")
00030 if messages == None:
00031 print "This config file lacks the messages tag."
00032 return False
00033
00034 topics = configuration.find("topics")
00035 if topics == None:
00036 print "This config file lacks the topics tag."
00037 return False
00038
00039
00040 for message in messages:
00041 try:
00042 if not message.attrib["id"] in idMessagesDict:
00043 idMessagesDict.setdefault(message.attrib["id"], 0)
00044 else:
00045 raise ValueError('The message id "' + message.attrib["id"] + '" is repeated.')
00046 except ValueError as e:
00047 print e.message
00048 return False
00049
00050 try:
00051 if roslib.message.get_message_class(message.find("type").text) == None:
00052 raise ValueError('The type message is not a valid type')
00053 else:
00054 the_message = roslib.message.get_message_class(message.find("type").text)
00055 message_object = message_converter.convert_dictionary_to_ros_message(message.find("type").text,
00056 json.loads(
00057 message.find("content").text))
00058
00059 except ValueError as e:
00060 print e.message
00061 return False
00062 for topic in topics:
00063 try:
00064 if not topic.attrib["id"] in idTopicDict:
00065 idTopicDict.setdefault(topic.attrib["id"], 0)
00066 else:
00067 raise ValueError('The topic id "' + topic.attrib["id"] + '" is repeated.')
00068 except ValueError as e:
00069 print e.message
00070 return False
00071
00072
00073
00074 for button in buttons:
00075 try:
00076 if not button.find("key").text.upper() in buttonKeyDict:
00077 buttonKeyDict.setdefault(button.find("key").text.upper(), 0)
00078 else:
00079 raise ValueError('The "' + button.find("key").text.upper() + '" key is repeated.')
00080 except ValueError as e:
00081 print e.message
00082 return False
00083
00084 ismessage = False
00085 istopic = False
00086 try:
00087 for message in messages:
00088 if button.find("message").text == message.attrib["id"]:
00089 ismessage = True
00090 for topic in topics:
00091 if button.find("topic").text == topic.attrib["id"]:
00092 istopic = True
00093 if not topic.find("msg_type").text == message.find("type").text:
00094 raise ValueError(
00095 'The message ' + message.attrib["id"] + ' is not compatible with the topic ' +
00096 topic.attrib["id"] + '\'s type.')
00097 if not ismessage or not istopic:
00098 raise ValueError('The ' + button.find(
00099 "key").text.upper() + ' key\'s associated message type is not compatible with it\'s associated topic.')
00100 except ValueError as e:
00101 print e.message
00102 return False
00103 return True
00104
00105 def xml_validator(xml):
00106 '''
00107 :param xml: An XML file.
00108 :return Boolean: It's true if the XML file is a valid XML configuration file.
00109 :raises ValueError: It raises an exception if the XML file is not a valid configuration file.
00110 This function checks that the input XML file is a valid XML configuration file for tele-dir.
00111 If the file is not valid, then the program ends and raises an error.
00112 '''
00113
00114
00115 try:
00116 tree = ET.parse(xml)
00117 except ET.ParseError:
00118 print xml+" is not a valid XML file."
00119 return False
00120
00121
00122 return validator(tree)
00123
00124
00125 def xmlCreator(xml_dir):
00126 '''
00127 :return None:
00128 This function starts a prompt in the terminal for the user to create a custom valid XML configuration file.
00129 It asks for several inputs for the user to fill with the information required to make the configuration that
00130 the user desires.
00131 '''
00132 master = ET.ElementTree()
00133 xml = ET.Element("xml")
00134 master._setroot(xml)
00135 description = ET.Element("description")
00136 config = ET.Element("config")
00137 messages = ET.Element("messages")
00138 buttons = ET.Element("buttons")
00139 topics = ET.Element("topics")
00140 print "Initializing controller configuration."
00141 file_name = raw_input("Input file name: ")
00142 while len(file_name) < 1:
00143 file_name = raw_input("Input file name: ")
00144 name = ET.Element("name")
00145 name.text = file_name
00146 robot_name = raw_input("Input target robot name: ")
00147 robot = ET.Element("target_robot")
00148 robot.text = robot_name
00149 config_version = ET.Element("config_version")
00150 config_version.text = "1.0"
00151 description.insert(0, name)
00152 description.insert(1, robot)
00153 description.insert(2, config_version)
00154 xml.insert(0, description)
00155 print "Initializing topics configuration. "
00156 i = 1
00157 while True:
00158 topic = newTopic(i)
00159 topics.insert(i - 1, topic)
00160 i += 1
00161 end = our_raw_input("Do you wish to add another topic? (Y/N)", 'Y', 'N')
00162 if end.upper() == 'N':
00163 break
00164 i = 1
00165 print "Initializing messages configuration. "
00166 while True:
00167 message = newMessage(i)
00168 messages.insert(i - 1, message)
00169 i += 1
00170 end = our_raw_input("Do you wish to add another message? (Y/N)", 'Y', 'N')
00171 if end.upper() == 'N':
00172 break
00173 i = 1
00174 print "Initializing keyboard configuration. "
00175 while True:
00176
00177 button = newButton(i, topics, messages)
00178 buttons.insert(i - 1, button)
00179 i += 1
00180 end = our_raw_input("Do you wish to add another button? (Y/N)", 'Y', 'N')
00181 if end.upper() == 'N':
00182 break
00183 config.insert(1, messages)
00184 config.insert(2, topics)
00185 config.insert(3, buttons)
00186 xml.insert(1, config)
00187 master.write(xml_dir + file_name + ".xml")
00188 print "File Created with name :" + file_name + ".xml!"
00189
00190
00191 def delete_key_by_topic(topic, buttons):
00192 list = []
00193 for button in buttons:
00194 if button.find("topic").text == topic.attrib['id']:
00195 list.append(button)
00196 buttons.remove(button)
00197 if len(list) > 0:
00198 print "The following buttons have been deleted: ",
00199 for button in list:
00200 print button.find("key").text + " ",
00201 print ".\n"
00202 else :
00203 print "No key has been deleted due to topic removal."
00204 return list
00205
00206
00207 def delete_key_by_message(message, buttons):
00208 list = []
00209 for button in buttons:
00210 if button.find("message").text == message.attrib['id']:
00211 list.append(button)
00212 buttons.remove(button)
00213 if len(list) > 0:
00214 print "The following buttons have been deleted: ",
00215 for button in list:
00216 print button.find("key").text + " ",
00217 print ".\n"
00218 else:
00219 print "No key has been deleted due to message removal."
00220 return list
00221
00222
00223 def newMessage(i):
00224 message_description = raw_input("Input message description: ")
00225 message_type = message_raw_input("Input message type: ")
00226 message_class = roslib.message.get_message_class(message_type)
00227 message_body = message_converter.convert_ros_message_to_dictionary(eval("message_class()"))
00228 message_content = json.dumps(AuxFuns.message_param_editor(message_body), sort_keys=True)
00229 return createMessage(i, message_description, message_type, message_content)
00230
00231
00232 def createMessage(i, message_description, message_type, message_content):
00233 content = ET.Element("content")
00234 content.text = message_content
00235 message = ET.Element("message", {'id': str(i)})
00236 description = ET.Element("description")
00237 description.text = message_description
00238 type = ET.Element("type")
00239 type.text = message_type
00240 message.insert(0, description)
00241 message.insert(1, type)
00242 message.insert(2, content)
00243 return message
00244
00245 def newTopic(i):
00246 topic_name = raw_input("Input topic name: ")
00247 topic_msg = message_raw_input("Input topic msg_type: ")
00248 return createTopic(i, topic_name, topic_msg)
00249
00250 def createTopic(i, topic_name, topic_msg):
00251 topic = ET.Element("topic", {'id': str(i)})
00252 name = ET.Element("name")
00253 name.text = topic_name
00254 msg_type = ET.Element("msg_type")
00255 msg_type.text = topic_msg
00256 topic.insert(0, name)
00257 topic.insert(1, msg_type)
00258 return topic
00259
00260 def newButton(i, topics, messages):
00261 button_key = raw_input("Input only one key: ")
00262 while len(button_key) > 1:
00263 button_key = raw_input("Error Length " + str(len(button_key)) + ".Input only one key:")
00264 message_asociated = None
00265 while message_asociated == None:
00266 button_messages = raw_input("Input number of message associated: ")
00267 for message in messages:
00268 if button_messages == message.attrib['id']:
00269 message_asociated = message
00270 if message_asociated == None:
00271 print "Error, the message wasn't found"
00272 topic_associated = None
00273 while topic_associated == None:
00274 button_topics = raw_input("Input topic associated: ")
00275 if len(topics.findall("topic")) < 1:
00276 raise ValueError("Topics is empty. Can't associate with a message")
00277 for topic in topics:
00278 if button_topics == topic.attrib['id']:
00279 topic_associated = topic
00280 if topic_associated!= None:
00281 if message_asociated.find("type").text != topic_associated.find("msg_type").text:
00282 print "Error message type in the topic selected (" + topic_associated.find("msg_type").text + ") , " \
00283 "isn't the same that the message type selected (" + message_asociated.find("type").text +")."
00284 topic_associated = None
00285 else:
00286 print "Topic with number " + button_topics + " don't exist. The existing topics are :",
00287 for topic in topics:
00288 print " " + topic.attrib['id'],
00289 print "\n"
00290 key = ET.Element("key")
00291 message = ET.Element("message")
00292 topic = ET.Element("topic")
00293 key.text = button_key.upper()
00294 message.text = button_messages
00295 topic.text = button_topics
00296 button = ET.Element("button")
00297 button.insert(0, key)
00298 button.insert(1, message)
00299 button.insert(2, topic)
00300 return button
00301
00302 def xmlEditor(xmlUrl, xml):
00303 '''
00304 :param xml: A valid tele-dir XML configuration file.
00305 :return None:
00306 This function prompts the user for changes in the xml XML file. It's designed to further improve the customization options for the user.
00307 '''
00308
00309
00310 try:
00311 tree = ET.parse(xmlUrl+xml)
00312 except ET.ParseError:
00313 print xml+" is not a valid XML file."
00314 return False
00315
00316
00317 edited = False
00318 print "Welcome to the tele-dir configuration editor."
00319 descr_edit = our_raw_input("Do you wish to edit the description? (Y/N)", 'Y', 'N')
00320 if descr_edit.upper() == 'Y':
00321 description = tree.getroot().find("description")
00322 name = description.find("name")
00323 target_robot = description.find("target_robot")
00324 print "The current name for this configuration is '"+name.text+"'" \
00325 "And the target robot is '"+target_robot.text+"'."
00326 edit = our_raw_input("Input N to edit the name\n"
00327 " R to edit the target robot\n"
00328 " F to forget about editing the description and move forward\n", 'R', 'F', 'N')
00329 if edit.upper() == 'N':
00330 new_name = raw_input("Input new name: ")
00331 name.text = new_name
00332 edit = our_raw_input("Do yo wish to edit the target robot? (Y/N)", 'Y', 'N')
00333 if edit.upper() == 'R' or edit.upper() == 'Y':
00334 new_target_robot = raw_input("Input new target robot: ")
00335 target_robot.text = new_target_robot
00336
00337 conf_edit = our_raw_input("Do you wish to edit the configuration? (Y/N)", 'Y', 'N')
00338 if conf_edit.upper() == 'Y':
00339 configuration = tree.getroot().find("config")
00340 while True:
00341
00342 buttons = configuration.find("buttons")
00343 messages = configuration.find("messages")
00344 topics = configuration.find("topics")
00345 edit = our_raw_input("Input T to edit topics\n"
00346 " M to edit messages\n"
00347 " B to edit buttons\n", 'T', 'M', 'B').upper()
00348 if edit == 'T':
00349 topic_bool = our_raw_input("Do you wish yo Add or Modify topics? (A/M)", 'A', 'M').upper()
00350 if topic_bool == 'M':
00351 for topic in topics:
00352 print "The topic number "+topic.attrib['id']+" is: "
00353 print ET.dump(topic)
00354 option = our_raw_input("Input E to edit the topic \n"
00355 " D to delete the topic \n"
00356 " C to continue to the next topic.\n", 'E', 'D', 'C').upper()
00357 if option == 'E':
00358 topic.find("name").text = raw_input("Input topic name: ") or topic.find("name").text
00359 topic.find("msg_type").text = message_raw_input("Input message type: ")
00360 elif option == 'D':
00361 if our_raw_input("Are you sure? (Y/N)", 'Y', 'N').upper() == 'Y':
00362 delete_key_by_topic(topic, buttons)
00363 topics.remove(topic)
00364 elif topic_bool == 'A':
00365 while True:
00366 i = int(topics.findall('topic')[len(topics.findall('topic'))-1].attrib['id'])+1
00367 topic = newTopic(i)
00368 topics.insert(i - 1, topic)
00369 end = our_raw_input("Do you wish to add another topic? (Y/N)", 'Y', 'N')
00370 if end.upper() == 'N':
00371 break
00372 elif edit == 'M':
00373 mess_bool = our_raw_input("Do you wish yo Add or Modify messages? (A/M)", 'A', 'M').upper()
00374 if mess_bool == 'M':
00375 for message in messages:
00376 print "The message with id "+message.attrib['id']+" is: "
00377 print ET.dump(message)
00378 option = our_raw_input("Input E to edit the message \n"
00379 " D to delete the message \n"
00380 " C to continue to the next message.\n", 'E', 'D', 'C').upper()
00381 if option == 'E':
00382 message.find("description").text = raw_input("Input message description: ") or message.find("description").text
00383 type = message_raw_input("Input message type: ")
00384 if message.find("type").text == type:
00385 message.find("content").text = json.dumps(AuxFuns.message_param_editor(json.loads(message.find("content").text)),sort_keys=True)
00386 else:
00387 message_class = roslib.message.get_message_class(type)
00388 message_body = message_converter.convert_ros_message_to_dictionary(eval("message_class()"))
00389 message.find("content").text = json.dumps(AuxFuns.message_param_editor(message_body),sort_keys=True)
00390 elif option == 'D':
00391 if our_raw_input("Are you sure? (Y/N)", 'Y', 'N').upper() == 'Y':
00392 delete_key_by_message(message, buttons)
00393 messages.remove(message)
00394
00395 elif mess_bool == 'A':
00396 while True:
00397 i = int(messages.findall('message')[len(messages.findall('message'))-1].attrib['id'])+1
00398 message = newMessage(i)
00399 messages.insert(i - 1, message)
00400 end = our_raw_input("Do you wish to add another message? (Y/N)", 'Y', 'N')
00401 if end.upper() == 'N':
00402 break
00403 elif edit == 'B':
00404 butt_bool = our_raw_input("Do you wish yo Add or Modify buttons? (A/M)", 'A', 'M').upper()
00405 if butt_bool == 'M':
00406 i = 0
00407 for button in buttons:
00408 print "The button number "+i+" is: "
00409 print ET.dump(button)
00410 i+=1
00411 option = our_raw_input("Input E to edit the button \n"
00412 " D to delete the button \n"
00413 " C to continue to the next button.\n", 'E', 'D', 'C').upper()
00414 if option == 'E':
00415 button_key = raw_input("Input button key") or button.find("key").text
00416 while len(button_key) > 1:
00417 button_key = raw_input("Error Length " + str(len(button_key)) + ".Input only one key:")or button.find("key").text
00418 message_asociated = None
00419 while message_asociated == None:
00420 button_messages = raw_input("Input number of message associated: ") or button.find("message").text
00421 for message in messages:
00422 if button_messages == message.attrib['id']:
00423 message_asociated = message
00424 if message_asociated == None:
00425 print "Error, the message wasn't found"
00426 topic_associated = None
00427 while topic_associated == None:
00428 button_topics = raw_input("Input topic associated: ") or button.find("topic").text
00429 for topic in topics:
00430 if button_topics == topic.attrib['id']:
00431 topic_associated = topic
00432 if message_asociated.find("type").text != topic_associated.find("msg_type").text:
00433 print "Error message type in the topic selected, isn't the same that the message type selected."
00434 topic_associated = None
00435
00436 button.find("topic").text = button_topics
00437 button.find("key").text = button_key
00438 button.find("message").text = button_messages
00439 elif option == 'D':
00440 if our_raw_input("Are you sure? (Y/N)", 'Y', 'N').upper() == 'Y':
00441 buttons.remove(button)
00442 elif butt_bool == 'A':
00443 while True:
00444 try:
00445 i = len(buttons.findall('button'))+1
00446 button = newButton(i, topics, messages)
00447 buttons.insert(i - 1, button)
00448 end = our_raw_input("Do you wish to add another button? (Y/N)", 'Y', 'N')
00449 if end.upper() == 'N':
00450 break
00451 except ValueError as e :
00452 print e.message
00453 break
00454
00455 if our_raw_input("Do you wish continue editing? (Y/N)", 'Y', 'N').upper() == 'N':
00456 break
00457
00458 file = raw_input("Enter the new file name: ")
00459 file_name = xmlUrl + file + ".xml"
00460 if (file== ""):
00461 file_name = xmlUrl + xml
00462 tree.write(file_name )
00463 print "The file has been saved with name "+ file_name
00464
00465
00466
00467
00468
00469