00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 PKG = "qualification"
00039 import roslib
00040 roslib.load_manifest(PKG)
00041 import rospy
00042
00043 import sys, os
00044 import subprocess
00045 from optparse import OptionParser
00046 from time import sleep
00047 import string
00048 import glob
00049 import traceback
00050
00051 from pr2_self_test_msgs.srv import ConfirmConf, ConfirmConfRequest, ConfirmConfResponse, ScriptDone, ScriptDoneRequest
00052
00053 from wg_invent_client import Invent
00054
00055 prog_path = os.path.join(roslib.packages.get_pkg_dir(PKG), "fwprog")
00056 ECAT_IFACE = "ecat0"
00057 PR2_GRANT = 'pr2-grant'
00058
00059 class MCBProgramConfig:
00060 def __init__(self, expected, firmware_files):
00061 rospy.init_node('mcb_prog_conf')
00062
00063 self.done_service = rospy.ServiceProxy('prestartup_done', ScriptDone)
00064
00065 self.confirm_proxy = rospy.ServiceProxy('mcb_conf_results', ConfirmConf)
00066 self.has_finished = False
00067
00068 username = rospy.get_param('/invent/username', None)
00069 password = rospy.get_param('/invent/password', None)
00070
00071 if username == None or password == None:
00072 self.finished(False, 'Invalid username or password. Cannot check MCB\'s.')
00073 return
00074
00075 self.invent = Invent(username, password)
00076 if not self.invent.login():
00077 self.finished(False, 'Unable to login to invent. Cannot check MCB\'s.')
00078 return
00079
00080 self.expected = expected
00081 self.firmware_files = firmware_files
00082
00083 self.serials = []
00084
00085
00086 def finished(self, pass_bool, msg = ''):
00087 if self.has_finished:
00088 return
00089
00090 try:
00091 result = ScriptDoneRequest()
00092 if pass_bool:
00093 result.result = 0
00094 else:
00095 result.result = 1
00096
00097 result.failure_msg = msg
00098
00099 rospy.wait_for_service('prestartup_done', 10)
00100 rospy.loginfo('Calling prestartup done service')
00101 self.done_service.call(result)
00102 self.has_finished = True
00103 except Exception, e:
00104 rospy.logerr(traceback.format_exc())
00105
00106
00107
00108
00109 def prompt_user(self, msg, details):
00110 rospy.loginfo('Prompting user: %s' % msg)
00111
00112 conf = ConfirmConfRequest()
00113 conf.message = msg
00114 conf.details = details
00115 resp = self.confirm_proxy.call(conf)
00116
00117 return resp.retry == ConfirmConfResponse.RETRY
00118
00119
00120
00121 def verify_boards(self, check):
00122 if self.has_finished:
00123 return False
00124
00125 try:
00126 rospy.wait_for_service('mcb_conf_results', 15)
00127 except:
00128 msg = "MCB conf results service not available. Unable to configure MCB's"
00129 rospy.logerr(msg)
00130 self.finished(False, msg)
00131 return False
00132
00133 if not self.count_boards():
00134 return False
00135
00136 if not self.check_link():
00137 return False
00138
00139 if not self.get_serials():
00140 return False
00141
00142 if check:
00143 return self.check_boards()
00144
00145 return True
00146
00147
00148 def count_boards(self):
00149 count_cmd = PR2_GRANT + ' ' + os.path.join(prog_path, "eccount") + " -i " + ECAT_IFACE
00150
00151 while not rospy.is_shutdown():
00152 p = subprocess.Popen(count_cmd, stdout =subprocess.PIPE,
00153 stderr = subprocess.PIPE, shell=True)
00154 stdout, stderr = p.communicate()
00155 count = p.returncode
00156 details = 'Ran eccount. Expected %s MCB\'s. Return code %s.\n\n' % (self.expected, count)
00157 details += 'STDOUT:\n' + stdout
00158 if len(stderr) > 3:
00159 details += '\nSTDERR:\n' + stderr
00160
00161
00162 if count == self.expected:
00163 rospy.logdebug("Found %s MCB's, programming" % count)
00164 return True
00165
00166 elif count == 0:
00167 msg = "Found no MCB's! Check cables and power. Retry?"
00168 elif count == 200:
00169 msg = "Unable to initialize interface. Make sure you have root access and the link is connected. Check power to the device. Retry?"
00170 elif count == 203:
00171 msg = "Ethernet interface has link, but no packets are returning. This could mean the etherCAT cable is not plugged into an MCB. Check etherCAT cable. To debug, check that the first device is good. Retry?"
00172 elif count == 204:
00173 msg = "Has no link to device. Check device cable and power. Retry?"
00174 elif count == 205:
00175 msg = "Ethernet interface is not UP. The computer needs to be configured. Run the \"Restart %s\" application and click OK to retry." % ECAT_IFACE
00176 elif count > 199:
00177 msg = "Error counting MCB's. eccount gave error code: %s. Retry?" % count
00178 elif count < self.expected:
00179 msg = "Did not find all MCB's. Found %s, expected %s. Check cables to all MCB's after MCB %s. Click OK to retry." % (count, self.expected, count)
00180 elif count > self.expected:
00181 msg = "Found more MCB's than expected. Found %s, expected %s. Did you scan the right part? Click OK to retry." % (count, self.expected)
00182 else:
00183 msg = "MCB counts don't match. Found %s, expected %s. Retry?" % (count, self.expected)
00184
00185 if not self.prompt_user(msg, details):
00186 self.finished(False, 'Failed to count boards. Operator canceled.\n\n%s\n%s' % (msg, details))
00187 return False
00188
00189 def check_link(self):
00190 emltest_cmd = PR2_GRANT + ' ' + os.path.join(prog_path, "emltest") + " -q -j8 -i %s -T10000,2" % ECAT_IFACE
00191
00192 p = subprocess.Popen(emltest_cmd, stdout = subprocess.PIPE,
00193 stderr = subprocess.PIPE, shell = True)
00194 o, e = p.communicate()
00195 retcode = p.returncode
00196
00197 if retcode != 0:
00198 msg = "Dropped packets to device while checking link. Cables may be damaged."
00199 details = "Ran \"emltest\", found dropped packets when checking link. This is probably a cable problem from the test fixture or between some MCB\'s."
00200
00201 self.finished(False, "%s\n\n%s" % (msg, details))
00202 return False
00203
00204 return True
00205
00206
00207 def get_serials(self):
00208 for board in range(0, self.expected):
00209 check_cmd = PR2_GRANT + ' ' + os.path.join(prog_path, 'device') + ' -i %s -K -p %d' % (ECAT_IFACE, board + 1)
00210 try:
00211 p = subprocess.Popen(check_cmd, stdout = subprocess.PIPE,
00212 stderr = subprocess.PIPE, shell = True)
00213 stdout, stderr = p.communicate()
00214 retcode = p.returncode
00215
00216 if retcode != 0:
00217 self.finished(False, 'Error when checking board %d. "device" returned code %d\n\nOUT: %s\nERROR: %s' % (board, retcode, stdout, stderr))
00218 return False
00219
00220 for ln in stdout.split('\n'):
00221 if ln.startswith('serial :'):
00222 self.serials.append(int(ln[9:].strip()))
00223 except Exception, e:
00224 rospy.logerr(traceback.format_exc())
00225 self.finished(False, 'Error when checking board %d. Caught exception:\n%s' % (board, traceback.format_exc()))
00226 return False
00227
00228 return True
00229
00230
00231 def check_boards(self):
00232
00233 for index, serial in enumerate(self.serials):
00234 rospy.logdebug('Checking serial %s' % serial)
00235 if not self.invent.get_test_status(serial):
00236 self.finished(False, 'Board %d has failed testing. Serial: %s' % (index, serial))
00237 return False
00238
00239 return True
00240
00241
00242
00243 def program_boards(self):
00244 firmware_files = self.firmware_files
00245 if len(firmware_files) == 0:
00246 firmware_files = glob.glob("%s/*.bit"%prog_path)
00247 print "Firmware files", firmware_files
00248
00249 for board in range(0, self.expected):
00250 program_cmd = [PR2_GRANT, os.path.join(prog_path, "fwprog"), '-i', ECAT_IFACE, '-p', "%d"%(board+1)] + firmware_files
00251
00252
00253 while not rospy.is_shutdown():
00254 p = subprocess.Popen(program_cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
00255 stdout, stderr = p.communicate()
00256 retcode = p.returncode
00257
00258 details = 'Ran fwprog on MCB %s, returned %s.\n\n' % (board, retcode)
00259 details += 'CMD:\n' + " ".join(program_cmd) + '\n'
00260 details += 'STDOUT:\n' + stdout
00261 if len(stderr) > 5:
00262 details += '\nSTDERR:\n' + stderr
00263
00264 if retcode == 0:
00265 break
00266
00267 msg = "Programming MCB firmware failed for MCB #%s with error %d! Would you like to retry?" %(board, retcode)
00268 retry = self.prompt_user(msg, details)
00269 if not retry:
00270 self.finished(False, '%s\n%s' % (msg, details))
00271 return False
00272
00273
00274 self.finished(True, "Boards programmed. Num. boards: %d. Serials: %s" % (self.expected, str(self.serials)))
00275 return True
00276
00277
00278 def configure_boards(self, mcbs, assembly = False):
00279 path = roslib.packages.get_pkg_dir("ethercat_hardware")
00280 actuator_path = os.path.join(path, "actuators.conf")
00281
00282 for mcb in mcbs:
00283 name, num = mcb.split(',')
00284
00285 cmd = PR2_GRANT + " " + os.path.join(path, "motorconf") + " -i %s -p -n %s -d %s -a %s" % (ECAT_IFACE, name, num, actuator_path)
00286
00287 while not rospy.is_shutdown():
00288 p = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
00289 stdout, stderr = p.communicate()
00290 retcode = p.returncode
00291
00292 details = 'Ran motorconf. Attempted to program MCB %s with actuator name %s. Return code: %s.\n\n' % (num, name, retcode)
00293 details += 'CMD:\n' + cmd + '\n'
00294 details += 'STDOUT:\n' + stdout
00295 if len(stderr) > 5:
00296 details += '\nSTDERR:\n' + stderr
00297
00298 if retcode != 0:
00299 msg = "Programming MCB configuration failed for %s with return code %s." % (name, retcode)
00300 retry = self.prompt_user("%s Retry?" % msg, details)
00301 if not retry:
00302 self.finished(False, '%s\n%s' % (msg, details))
00303 return
00304 else:
00305 break
00306
00307 self.update_conf(mcbs)
00308
00309 if assembly and not self.check_assembly():
00310 self.finished(False, "Mismatch between given MCB's and listed items in inventory system. Component is not properly assembled in inventory. Found MCB serials: %s." % str(self.serials))
00311
00312
00313 self.finished(True, "Boards configured. Num. boards: %d. Serials: %s" % (self.expected, str(self.serials)))
00314 return
00315
00316
00317 def update_conf(self, mcbs):
00318 for index, serial in enumerate(self.serials):
00319 conf_name = mcbs[index].split(',')[0]
00320 self.invent.setKV(serial, 'Configuration', conf_name)
00321
00322
00323 def check_assembly(self):
00324 test_component = rospy.get_param('/qual_item/serial', None)
00325 if test_component is None:
00326 rospy.logerr('Unable to find serial number of test component')
00327 return False
00328
00329 sub_components = self.invent.get_sub_items(test_component, recursive=True)
00330 if len(sub_components) == 0:
00331 rospy.logerr('Unable to find any sub components for item %s' % test_component)
00332 return False
00333
00334 rospy.logdebug('Found sub components: %s' % str(sub_components))
00335
00336 for serial in self.serials:
00337 if not str(serial) in sub_components:
00338 rospy.logerr('MCB serial %s was not listed under component %s in invent. Unit is incorrectly assembled in inventory system.' % (serial, test_component))
00339 return False
00340
00341 return True
00342
00343 if __name__ == '__main__':
00344 parser = OptionParser(usage="./%prog [options]", prog='mcb_prog_conf.py')
00345 parser.add_option("-p", "--program", action="store_true",
00346 dest="program", default=False,
00347 metavar="PROGRAM", help="Program mcbs?")
00348 parser.add_option("-c", "--configure", action="store_true",
00349 dest="configure", default=False,
00350 metavar="CONFIGURE", help="Configure mcbs?")
00351 parser.add_option("-n", "--number", type="int", dest="expected",
00352 default = 0,
00353 metavar="NUMBER", help="Number of MCB's")
00354 parser.add_option("--motor", type="string", dest="mcbs",
00355 action="append", metavar="NAME,NUM",
00356 help="MCB's to configure by name and number")
00357 parser.add_option("-a", "--assembly", action="store_true",
00358 default=False, dest="assembly",
00359 help="Verify that boards are components of qual item")
00360 parser.add_option("-f", "--firmware", action="append", default=[],
00361 dest="firmware_files",
00362 help="Firmware file to use when programming device")
00363 options, args = parser.parse_args()
00364
00365 if options.program and options.configure:
00366 parser.error("Options \'program\' and \'configure\' are mutually exclusive")
00367
00368 if not options.program and not options.configure:
00369 parser.error("Must choose to program or configure boards")
00370
00371 if options.expected == 0:
00372 parser.error("Must have MCB's to program or configure")
00373
00374 if options.configure and options.expected != len(options.mcbs):
00375 parser.error("Number of boards doesn't match with list given")
00376
00377 prog_conf = MCBProgramConfig(options.expected, options.firmware_files)
00378
00379 try:
00380 sleep(2)
00381
00382 if not prog_conf.verify_boards(options.program):
00383 sys.exit()
00384
00385 if options.program:
00386 prog_conf.program_boards()
00387 else:
00388 prog_conf.configure_boards(options.mcbs, options.assembly)
00389
00390
00391 rospy.spin()
00392 except KeyboardInterrupt:
00393 pass
00394 except Exception, e:
00395 rospy.logerr(traceback.format_exc())
00396 prog_conf.finished(False, 'Caught exception in prog_conf loop. %s' % traceback.format_exc())
00397
00398