00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
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
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
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()