bootloader.py
Go to the documentation of this file.
00001 # Copyright 2011 Shadow Robot Company Ltd.
00002 #
00003 # This program is free software: you can redistribute it and/or modify it
00004 # under the terms of the GNU General Public License as published by the Free
00005 # Software Foundation, either version 2 of the License, or (at your option)
00006 # any later version.
00007 #
00008 # This program is distributed in the hope that it will be useful, but WITHOUT
00009 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00010 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00011 # more details.
00012 #
00013 # You should have received a copy of the GNU General Public License along
00014 # with this program.  If not, see <http://www.gnu.org/licenses/>.
00015 
00016 import os
00017 import rospy
00018 import rospkg
00019 
00020 from qt_gui.plugin import Plugin
00021 from python_qt_binding import loadUi
00022 from QtCore import QEvent, QObject, Qt, QTimer, Slot, QThread, SIGNAL, QPoint
00023 from QtGui import QWidget, QShortcut, QMessageBox, QFrame, QHBoxLayout, QCheckBox, QLabel, QCursor, QColor, QFileDialog
00024 from sr_utilities.hand_finder import HandFinder
00025 
00026 from std_srvs.srv import Empty
00027 from diagnostic_msgs.msg import DiagnosticArray
00028 
00029 from sr_robot_msgs.srv import SimpleMotorFlasher, SimpleMotorFlasherResponse
00030 
00031 
00032 class MotorBootloader(QThread):
00033 
00034     def __init__(self, parent, nb_motors_to_program, prefix):
00035         QThread.__init__(self, None)
00036         self.parent = parent
00037         self.nb_motors_to_program = nb_motors_to_program
00038         self.prefix = prefix
00039 
00040     def run(self):
00041         """
00042         perform bootloading on the selected motors
00043         """
00044         bootloaded_motors = 0
00045         firmware_path = self.parent._widget.txt_path.text()
00046         for motor in self.parent.motors:
00047             if motor.checkbox.checkState() == Qt.Checked:
00048                 try:
00049                     self.bootloader_service = rospy.ServiceProxy(self.prefix + '/SimpleMotorFlasher',
00050                                                                  SimpleMotorFlasher)
00051                     resp = self.bootloader_service(firmware_path.encode('ascii', 'ignore'), motor.motor_index)
00052                 except rospy.ServiceException, e:
00053                     self.emit(SIGNAL("failed(QString)"), "Service did not process request: %s" % str(e))
00054                     return
00055 
00056                 if resp == SimpleMotorFlasherResponse.FAIL:
00057                     self.emit(SIGNAL("failed(QString)"),
00058                               "Bootloading motor {} failed".format(bootloaded_motors))
00059                 bootloaded_motors += 1
00060                 self.emit(SIGNAL("motor_finished(QPoint)"), QPoint(bootloaded_motors, 0.0))
00061 
00062 
00063 class Motor(QFrame):
00064 
00065     def __init__(self, parent, motor_name, motor_index):
00066         QFrame.__init__(self, parent)
00067 
00068         self.motor_name = motor_name
00069         self.motor_index = motor_index
00070 
00071         self.layout = QHBoxLayout()
00072 
00073         self.checkbox = QCheckBox(
00074             motor_name + " [" + str(motor_index) + "]", self)
00075         self.layout.addWidget(self.checkbox)
00076 
00077         self.revision_label = QLabel("")
00078         self.revision_label.setToolTip("Svn Revision")
00079         self.layout.addWidget(self.revision_label)
00080 
00081         self.setLayout(self.layout)
00082 
00083 
00084 class SrGuiBootloader(Plugin):
00085 
00086     """
00087     A GUI plugin for bootloading the motors on the shadow etherCAT hand.
00088     """
00089 
00090     def __init__(self, context):
00091         super(SrGuiBootloader, self).__init__(context)
00092         self.setObjectName('SrGuiBootloader')
00093 
00094         self._publisher = None
00095         self._widget = QWidget()
00096 
00097         ui_file = os.path.join(rospkg.RosPack().get_path(
00098             'sr_gui_bootloader'), 'uis', 'SrBootloader.ui')
00099         loadUi(ui_file, self._widget)
00100         self._widget.setObjectName('SrMotorResetterUi')
00101         context.add_widget(self._widget)
00102 
00103         # setting the prefixes
00104         self._hand_finder = HandFinder()
00105         hand_parameters = self._hand_finder.get_hand_parameters()
00106         self._prefix = ""
00107         for hand in hand_parameters.mapping:
00108             self._widget.select_prefix.addItem(hand_parameters.mapping[hand])
00109         if not hand_parameters.mapping:
00110             rospy.logerr("No hand detected")
00111             QMessageBox.warning(self._widget, "warning", "No hand is detected")
00112             return
00113         else:
00114             self._widget.select_prefix.setCurrentIndex(0)
00115             self._prefix = hand_parameters.mapping.values()[0]
00116         self._widget.select_prefix.currentIndexChanged['QString'].connect(self.prefix_selected)
00117 
00118         # motors_frame is defined in the ui file with a grid layout
00119         self.motors = []
00120         self.motors_frame = self._widget.motors_frame
00121         self.progress_bar = self._widget.motors_progress_bar
00122         self.progress_bar.hide()
00123 
00124         self.server_revision = 0
00125         self.diag_sub = rospy.Subscriber("/diagnostics", DiagnosticArray, self.diagnostics_callback)
00126 
00127         # Bind button clicks
00128         self._widget.btn_select_bootloader.pressed.connect(self.on_select_bootloader_pressed)
00129         self._widget.btn_select_all.pressed.connect(self.on_select_all_pressed)
00130         self._widget.btn_select_none.pressed.connect(self.on_select_none_pressed)
00131         self._widget.btn_bootload.pressed.connect(self.on_bootload_pressed)
00132 
00133         # select the first available hand
00134         self.prefix_selected(hand_parameters.mapping.values()[0])
00135 
00136     def on_select_bootloader_pressed(self):
00137         """
00138         Select a hex file to bootload. Hex files must exist in the released_firmaware folder
00139         """
00140         path_to_bootloader = "~"
00141         try:
00142             rp = rospkg.RosPack()
00143             path_to_bootloader = os.path.join(rospkg.RosPack().get_path(
00144                 'sr_external_dependencies'), '/compiled_firmware/released_firmware/')
00145         except:
00146             rospy.logwarn(
00147                 "Couldn't find the sr_edc_controller_configuration package")
00148 
00149         filter_files = "*.hex"
00150         filename, _ = QFileDialog.getOpenFileName(
00151             self._widget.motors_frame, self._widget.tr(
00152                 'Select hex file to bootload'),
00153             self._widget.tr(
00154                 path_to_bootloader),
00155             self._widget.tr(filter_files))
00156         if filename == "":
00157             return
00158 
00159         self._widget.txt_path.setText(filename)
00160         self._widget.btn_bootload.setEnabled(True)
00161 
00162     def populate_motors(self):
00163         """
00164         Find motors according to joint_to_motor_mapping mapping that must exist on the parameter server
00165         and add to the list of Motor objects etherCAT hand node must be running
00166         """
00167         if rospy.has_param(self._prefix + "/joint_to_motor_mapping"):
00168             joint_to_motor_mapping = rospy.get_param(
00169                 self._prefix + "/joint_to_motor_mapping")
00170         else:
00171             QMessageBox.warning(self.motors_frame, "Warning",
00172                                 "Couldn't find the " + self._prefix +
00173                                 "/joint_to_motor_mapping parameter. Make sure the etherCAT Hand node is running")
00174             return
00175 
00176         joint_names = [
00177             ["FFJ0", "FFJ1", "FFJ2", "FFJ3", "FFJ4"],
00178             ["MFJ0", "MFJ1", "MFJ2", "MFJ3", "MFJ4"],
00179             ["RFJ0", "RFJ1", "RFJ2", "RFJ3", "RFJ4"],
00180             ["LFJ0", "LFJ1", "LFJ2", "LFJ3", "LFJ4", "LFJ5"],
00181             ["THJ1", "THJ2", "THJ3", "THJ4", "THJ5"],
00182             ["WRJ1", "WRJ2"]
00183         ]
00184 
00185         row = 0
00186         col = 0
00187         index_jtm_mapping = 0
00188         for finger in joint_names:
00189             col = 0
00190             for joint_name in finger:
00191                 motor_index = joint_to_motor_mapping[index_jtm_mapping]
00192                 if motor_index != -1:
00193                     motor = Motor(self.motors_frame, joint_name, motor_index)
00194                     self.motors_frame.layout().addWidget(motor, row, col)
00195                     self.motors.append(motor)
00196                     col += 1
00197                 index_jtm_mapping += 1
00198             row += 1
00199 
00200     def diagnostics_callback(self, msg):
00201         for status in msg.status:
00202             for motor in self.motors:
00203                 if motor.motor_name in status.name and self._prefix.replace("/", "") in status.name:
00204                     for key_values in status.values:
00205                         if key_values.key.startswith("Firmware git revision"):
00206                             server_current_modified = key_values.value.split(" / ")
00207 
00208                             if server_current_modified[0] > self.server_revision:
00209                                 self.server_revision = int(server_current_modified[0].strip())
00210 
00211                             palette = motor.revision_label.palette()
00212                             palette.setColor(motor.revision_label.foregroundRole(), Qt.green)
00213                             if server_current_modified[0].strip() != server_current_modified[1].strip():
00214                                 palette.setColor(motor.revision_label.foregroundRole(), QColor(255, 170, 23))
00215                                 motor.revision_label.setPalette(palette)
00216 
00217                             if "True" in server_current_modified[2]:
00218                                 palette.setColor(motor.revision_label.foregroundRole(), Qt.red)
00219                                 motor.revision_label.setText("svn: " + server_current_modified[1] + " [M]")
00220                                 motor.revision_label.setPalette(palette)
00221                             else:
00222                                 motor.revision_label.setText(" svn: " + server_current_modified[1])
00223                                 motor.revision_label.setPalette(palette)
00224 
00225     def on_select_all_pressed(self):
00226         """
00227         Select all motors
00228         """
00229         for motor in self.motors:
00230             motor.checkbox.setCheckState(Qt.Checked)
00231 
00232     def on_select_none_pressed(self):
00233         """
00234         Unselect all motors
00235         """
00236         for motor in self.motors:
00237             motor.checkbox.setCheckState(Qt.Unchecked)
00238 
00239     def on_bootload_pressed(self):
00240         """
00241         Start programming motors
00242         """
00243         self.progress_bar.reset()
00244         nb_motors_to_program = 0
00245         for motor in self.motors:
00246             if motor.checkbox.checkState() == Qt.Checked:
00247                 nb_motors_to_program += 1
00248         if nb_motors_to_program == 0:
00249             QMessageBox.warning(self._widget, "Warning", "No motors selected for resetting.")
00250             return
00251         self.progress_bar.setMaximum(nb_motors_to_program)
00252 
00253         self.motor_bootloader = MotorBootloader(
00254             self, nb_motors_to_program, self._prefix)
00255         self._widget.connect(self.motor_bootloader, SIGNAL("finished()"), self.finished_programming_motors)
00256         self._widget.connect(self.motor_bootloader, SIGNAL("motor_finished(QPoint)"), self.one_motor_finished)
00257         self._widget.connect(self.motor_bootloader, SIGNAL("failed(QString)"), self.failed_programming_motors)
00258 
00259         self._widget.setCursor(Qt.WaitCursor)
00260         self.motors_frame.setEnabled(False)
00261         self._widget.btn_select_all.setEnabled(False)
00262         self._widget.btn_select_none.setEnabled(False)
00263         self.progress_bar.show()
00264         self._widget.btn_bootload.hide()
00265 
00266         self.motor_bootloader.start()
00267 
00268     def one_motor_finished(self, point):
00269         self.progress_bar.setValue(int(point.x()))
00270 
00271     def finished_programming_motors(self):
00272         """
00273         Programming of motors completed
00274         """
00275         self.motors_frame.setEnabled(True)
00276         self._widget.btn_select_all.setEnabled(True)
00277         self._widget.btn_select_none.setEnabled(True)
00278         self._widget.setCursor(Qt.ArrowCursor)
00279         self.progress_bar.hide()
00280         self._widget.btn_bootload.show()
00281 
00282     def failed_programming_motors(self, message):
00283         QMessageBox.warning(self._widget.motors_frame, "Warning", message)
00284 
00285     def _unregisterPublisher(self):
00286         if self._publisher is not None:
00287             self._publisher.unregister()
00288             self._publisher = None
00289 
00290     def shutdown_plugin(self):
00291         self._unregisterPublisher()
00292 
00293     def save_settings(self, global_settings, perspective_settings):
00294         pass
00295 
00296     def restore_settings(self, global_settings, perspective_settings):
00297         pass
00298 
00299     def prefix_selected(self, prefix):
00300         self._prefix = prefix
00301         self.populate_motors()


sr_gui_bootloader
Author(s): Ugo Cupcic
autogenerated on Thu Jun 6 2019 21:13:48