mcb_prog_conf.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Software License Agreement (BSD License)
00004 #
00005 # Copyright (c) 2009, Willow Garage, Inc.
00006 # All rights reserved.
00007 #
00008 # Redistribution and use in source and binary forms, with or without
00009 # modification, are permitted provided that the following conditions
00010 # are met:
00011 #
00012 #  * Redistributions of source code must retain the above copyright
00013 #    notice, this list of conditions and the following disclaimer.
00014 #  * Redistributions in binary form must reproduce the above
00015 #    copyright notice, this list of conditions and the following
00016 #    disclaimer in the documentation and/or other materials provided
00017 #    with the distribution.
00018 #  * Neither the name of the Willow Garage nor the names of its
00019 #    contributors may be used to endorse or promote products derived
00020 #    from this software without specific prior written permission.
00021 #
00022 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 # POSSIBILITY OF SUCH DAMAGE.
00034 
00035 ##\author Kevin Watts
00036 ##\brief Programs and configures MCB's during component qualification
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" # EtherCAT interface
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 # OK
00094             else:
00095                 result.result = 1 # Fail
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     ##\brief Ask user for retry prompt using confirm_conf service
00107     ##
00108     ##\return True if should retry
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     ## Counts, gets serials and verifies boards
00120     ##\brief param check bool : Whether to check if boards passed EE qualification
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     ## Counts boards, returns true if passed
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     ##@brief Get serial numbers for each MCB
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     ##@brief Check that boards have passed MCB qualification in Invent
00231     def check_boards(self):
00232         # Get invent username/password
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     ##@brief Programs MCB's and calls result service when finished
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             #program_cmd = PR2_GRANT + ' ' + os.path.join(prog_path, "fwprog") + " -i %s -p %s %s/*.bit" % (ECAT_IFACE, (board + 1), prog_path)
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     ##@brief Configures boards with given configurations
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     ##@brief Updates logs in invent for each board
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) # Wait for boards to warm up
00381 
00382         if not prog_conf.verify_boards(options.program):
00383             sys.exit() # Exit code is taken care of by verify functions
00384 
00385         if options.program:
00386             prog_conf.program_boards()
00387         else: # Configure
00388             prog_conf.configure_boards(options.mcbs, options.assembly)
00389         
00390         # Wait to be killed
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             


qualification
Author(s): Kevin Watts (watts@willowgarage.com), Josh Faust (jfaust@willowgarage.com)
autogenerated on Sat Dec 28 2013 17:57:34