bootloader.py
Go to the documentation of this file.
1 # Copyright 2011 Shadow Robot Company Ltd.
2 #
3 # This program is free software: you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License as published by the Free
5 # Software Foundation version 2 of the License.
6 #
7 # This program is distributed in the hope that it will be useful, but WITHOUT
8 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
10 # more details.
11 #
12 # You should have received a copy of the GNU General Public License along
13 # with this program. If not, see <http://www.gnu.org/licenses/>.
14 
15 import os
16 import rospy
17 import rospkg
18 
19 from qt_gui.plugin import Plugin
20 from python_qt_binding import loadUi
21 from QtCore import QEvent, QObject, Qt, QTimer, Slot, QThread, QPoint, pyqtSignal
22 from QtGui import QCursor, QColor
23 from QtWidgets import QWidget, QShortcut, QMessageBox, QFrame, QHBoxLayout, QCheckBox, QLabel, QFileDialog
24 from sr_utilities.hand_finder import HandFinder
25 
26 from std_srvs.srv import Empty
27 from diagnostic_msgs.msg import DiagnosticArray
28 
29 from sr_robot_msgs.srv import SimpleMotorFlasher, SimpleMotorFlasherResponse
30 
31 
32 class MotorBootloader(QThread):
33 
34  motor_finished = pyqtSignal('QPoint')
35  failed = pyqtSignal('QString')
36 
37  def __init__(self, parent, nb_motors_to_program, prefix):
38  QThread.__init__(self, None)
39  self.parent = parent
40  self.nb_motors_to_program = nb_motors_to_program
41  self.prefix = prefix
42 
43  def run(self):
44  """
45  perform bootloading on the selected motors
46  """
47  bootloaded_motors = 0
48  firmware_path = self.parent._widget.txt_path.text()
49  for motor in self.parent.motors:
50  if motor.checkbox.checkState() == Qt.Checked:
51  try:
52  self.bootloader_service = rospy.ServiceProxy(self.prefix + '/SimpleMotorFlasher',
53  SimpleMotorFlasher)
54  resp = self.bootloader_service(firmware_path.encode('ascii', 'ignore'), motor.motor_index)
55  except rospy.ServiceException, e:
56  self.failed.emit("Service did not process request: %s" % str(e))
57  return
58 
59  if resp == SimpleMotorFlasherResponse.FAIL:
60  self.failed.emit("Bootloading motor {} failed".format(bootloaded_motors))
61  bootloaded_motors += 1
62  self.motor_finished.emit(QPoint(bootloaded_motors, 0.0))
63 
64 
65 class Motor(QFrame):
66 
67  def __init__(self, parent, motor_name, motor_index):
68  QFrame.__init__(self, parent)
69 
70  self.motor_name = motor_name
71  self.motor_index = motor_index
72 
73  self.layout = QHBoxLayout()
74 
75  self.checkbox = QCheckBox(
76  motor_name + " [" + str(motor_index) + "]", self)
77  self.layout.addWidget(self.checkbox)
78 
79  self.revision_label = QLabel("")
80  self.revision_label.setToolTip("Svn Revision")
81  self.layout.addWidget(self.revision_label)
82 
83  self.setLayout(self.layout)
84 
85 
87 
88  """
89  A GUI plugin for bootloading the motors on the shadow etherCAT hand.
90  """
91 
92  def __init__(self, context):
93  super(SrGuiBootloader, self).__init__(context)
94  self.setObjectName('SrGuiBootloader')
95 
96  self._publisher = None
97  self._widget = QWidget()
98 
99  ui_file = os.path.join(rospkg.RosPack().get_path(
100  'sr_gui_bootloader'), 'uis', 'SrBootloader.ui')
101  loadUi(ui_file, self._widget)
102  self._widget.setObjectName('SrMotorResetterUi')
103  if context is not None:
104  context.add_widget(self._widget)
105 
106  # setting the prefixes
107  self._hand_finder = HandFinder()
108  hand_parameters = self._hand_finder.get_hand_parameters()
109  self._prefix = ""
110  for hand in hand_parameters.mapping:
111  self._widget.select_prefix.addItem(hand_parameters.mapping[hand])
112  if not hand_parameters.mapping:
113  rospy.logerr("No hand detected")
114  QMessageBox.warning(self._widget, "warning", "No hand is detected")
115  return
116  else:
117  self._widget.select_prefix.setCurrentIndex(0)
118  self._prefix = hand_parameters.mapping.values()[0]
119  self._widget.select_prefix.currentIndexChanged['QString'].connect(self.prefix_selected)
120 
121  # motors_frame is defined in the ui file with a grid layout
122  self.motors = []
123  self.motors_frame = self._widget.motors_frame
124  self.progress_bar = self._widget.motors_progress_bar
125  self.progress_bar.hide()
126 
128  self.diag_sub = rospy.Subscriber("/diagnostics", DiagnosticArray, self.diagnostics_callback)
129 
130  # Bind button clicks
131  self._widget.btn_select_bootloader.pressed.connect(self.on_select_bootloader_pressed)
132  self._widget.btn_select_all.pressed.connect(self.on_select_all_pressed)
133  self._widget.btn_select_none.pressed.connect(self.on_select_none_pressed)
134  self._widget.btn_bootload.pressed.connect(self.on_bootload_pressed)
135 
136  # select the first available hand
137  self.prefix_selected(hand_parameters.mapping.values()[0])
138 
140  """
141  Select a hex file to bootload. Hex files must exist in the released_firmaware folder
142  """
143  path_to_bootloader = "~"
144  try:
145  rp = rospkg.RosPack()
146  path_to_bootloader = os.path.join(rospkg.RosPack().get_path(
147  'sr_external_dependencies'), '/compiled_firmware/released_firmware/')
148  except:
149  rospy.logwarn(
150  "Couldn't find the sr_edc_controller_configuration package")
151 
152  filter_files = "*.hex"
153  filename, _ = QFileDialog.getOpenFileName(
154  self._widget.motors_frame, self._widget.tr(
155  'Select hex file to bootload'),
156  self._widget.tr(
157  path_to_bootloader),
158  self._widget.tr(filter_files))
159  if filename == "":
160  return
161 
162  self._widget.txt_path.setText(filename)
163  self._widget.btn_bootload.setEnabled(True)
164 
165  def populate_motors(self):
166  """
167  Find motors according to joint_to_motor_mapping mapping that must exist on the parameter server
168  and add to the list of Motor objects etherCAT hand node must be running
169  """
170  if rospy.has_param(self._prefix + "/joint_to_motor_mapping"):
171  joint_to_motor_mapping = rospy.get_param(
172  self._prefix + "/joint_to_motor_mapping")
173  else:
174  QMessageBox.warning(self.motors_frame, "Warning",
175  "Couldn't find the " + self._prefix +
176  "/joint_to_motor_mapping parameter. Make sure the etherCAT Hand node is running")
177  return
178 
179  joint_names = [
180  ["FFJ0", "FFJ1", "FFJ2", "FFJ3", "FFJ4"],
181  ["MFJ0", "MFJ1", "MFJ2", "MFJ3", "MFJ4"],
182  ["RFJ0", "RFJ1", "RFJ2", "RFJ3", "RFJ4"],
183  ["LFJ0", "LFJ1", "LFJ2", "LFJ3", "LFJ4", "LFJ5"],
184  ["THJ1", "THJ2", "THJ3", "THJ4", "THJ5"],
185  ["WRJ1", "WRJ2"]
186  ]
187 
188  row = 0
189  col = 0
190  index_jtm_mapping = 0
191  for finger in joint_names:
192  col = 0
193  for joint_name in finger:
194  motor_index = joint_to_motor_mapping[index_jtm_mapping]
195  if motor_index != -1:
196  motor = Motor(self.motors_frame, joint_name, motor_index)
197  self.motors_frame.layout().addWidget(motor, row, col)
198  self.motors.append(motor)
199  col += 1
200  index_jtm_mapping += 1
201  row += 1
202 
203  def diagnostics_callback(self, msg):
204  for status in msg.status:
205  for motor in self.motors:
206  if motor.motor_name in status.name and self._prefix.replace("/", "") in status.name:
207  for key_values in status.values:
208  if key_values.key.startswith("Firmware git revision"):
209  server_current_modified = key_values.value.split(" / ")
210 
211  if server_current_modified[0] > self.server_revision:
212  self.server_revision = int(server_current_modified[0].strip())
213 
214  palette = motor.revision_label.palette()
215  palette.setColor(motor.revision_label.foregroundRole(), Qt.green)
216  if server_current_modified[0].strip() != server_current_modified[1].strip():
217  palette.setColor(motor.revision_label.foregroundRole(), QColor(255, 170, 23))
218  motor.revision_label.setPalette(palette)
219 
220  if "True" in server_current_modified[2]:
221  palette.setColor(motor.revision_label.foregroundRole(), Qt.red)
222  motor.revision_label.setText("svn: " + server_current_modified[1] + " [M]")
223  motor.revision_label.setPalette(palette)
224  else:
225  motor.revision_label.setText(" svn: " + server_current_modified[1])
226  motor.revision_label.setPalette(palette)
227 
229  """
230  Select all motors
231  """
232  for motor in self.motors:
233  motor.checkbox.setCheckState(Qt.Checked)
234 
236  """
237  Unselect all motors
238  """
239  for motor in self.motors:
240  motor.checkbox.setCheckState(Qt.Unchecked)
241 
243  """
244  Start programming motors
245  """
246  self.progress_bar.reset()
247  nb_motors_to_program = 0
248  for motor in self.motors:
249  if motor.checkbox.checkState() == Qt.Checked:
250  nb_motors_to_program += 1
251  if nb_motors_to_program == 0:
252  QMessageBox.warning(self._widget, "Warning", "No motors selected for resetting.")
253  return
254  self.progress_bar.setMaximum(nb_motors_to_program)
255 
257  self, nb_motors_to_program, self._prefix)
258 
259  self.motor_bootloader.finished.connect(self.finished_programming_motors)
260  self.motor_bootloader.motor_finished.connect(self.one_motor_finished)
261  self.motor_bootloader.failed['QString'].connect(self.failed_programming_motors)
262 
263  self._widget.setCursor(Qt.WaitCursor)
264  self.motors_frame.setEnabled(False)
265  self._widget.btn_select_all.setEnabled(False)
266  self._widget.btn_select_none.setEnabled(False)
267  self.progress_bar.show()
268  self._widget.btn_bootload.hide()
269 
270  self.motor_bootloader.start()
271 
272  def one_motor_finished(self, point):
273  self.progress_bar.setValue(int(point.x()))
274 
276  """
277  Programming of motors completed
278  """
279  self.motors_frame.setEnabled(True)
280  self._widget.btn_select_all.setEnabled(True)
281  self._widget.btn_select_none.setEnabled(True)
282  self._widget.setCursor(Qt.ArrowCursor)
283  self.progress_bar.hide()
284  self._widget.btn_bootload.show()
285 
286  def failed_programming_motors(self, message):
287  QMessageBox.warning(self._widget.motors_frame, "Warning", message)
288 
290  if self._publisher is not None:
291  self._publisher.unregister()
292  self._publisher = None
293 
294  def shutdown_plugin(self):
295  self._unregisterPublisher()
296 
297  def save_settings(self, global_settings, perspective_settings):
298  pass
299 
300  def restore_settings(self, global_settings, perspective_settings):
301  pass
302 
303  def prefix_selected(self, prefix):
304  self._prefix = prefix
305  self.populate_motors()
306 
307 if __name__ == "__main__":
308  from QtWidgets import QApplication
309  import sys
310  rospy.init_node("bootloader")
311  app = QApplication(sys.argv)
312  ctrl = SrGuiBootloader(None)
313  ctrl._widget.show()
314  app.exec_()
def __init__(self, parent, motor_name, motor_index)
Definition: bootloader.py:67
def save_settings(self, global_settings, perspective_settings)
Definition: bootloader.py:297
def restore_settings(self, global_settings, perspective_settings)
Definition: bootloader.py:300
def __init__(self, parent, nb_motors_to_program, prefix)
Definition: bootloader.py:37


sr_gui_bootloader
Author(s): Ugo Cupcic
autogenerated on Wed Oct 14 2020 03:22:48