Go to the documentation of this file.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 import errno
00031 import argparse
00032 import os
00033 import sys
00034
00035 import rospy
00036
00037 import std_msgs.msg
00038
00039 import baxter_dataflow
00040
00041 from baxter_maintenance_msgs.msg import (
00042 UpdateSources,
00043 UpdateStatus,
00044 )
00045
00046
00047 class Updater(object):
00048 """
00049 Control the updater on the robot.
00050
00051 Signals:
00052 status_changed: Fired when the update status changes. Passes
00053 the current UpdateStatus message.
00054 """
00055 def __init__(self):
00056 self.status_changed = baxter_dataflow.Signal()
00057
00058 self._status = UpdateStatus()
00059 self._avail_updates = UpdateSources()
00060
00061 self._update_sources = rospy.Subscriber(
00062 '/usb/update_sources',
00063 UpdateSources,
00064 self._on_update_sources)
00065
00066 self._updater_status_sub = rospy.Subscriber(
00067 '/updater/status',
00068 UpdateStatus,
00069 self._on_update_status)
00070
00071 self._updater_start = rospy.Publisher(
00072 '/updater/start',
00073 std_msgs.msg.String)
00074
00075 self._updater_stop = rospy.Publisher(
00076 '/updater/stop',
00077 std_msgs.msg.Empty)
00078
00079 baxter_dataflow.wait_for(
00080 lambda: self._avail_updates.uuid != '',
00081 timeout=5.0,
00082 timeout_msg="Failed to get list of available updates"
00083 )
00084
00085 def _on_update_sources(self, msg):
00086 if msg.uuid != self._avail_updates.uuid:
00087 self._avail_updates = msg
00088
00089 def _on_update_status(self, msg):
00090 if self._status != msg:
00091 self._status = msg
00092 self.status_changed(msg)
00093
00094 def list(self):
00095 """
00096 Return a list of tuples (version, uuid) of all available updates
00097 """
00098 return [(u.version, u.uuid) for u in self._avail_updates.sources]
00099
00100 def command_update(self, uuid):
00101 """
00102 Command the robot to launch the update with the given uuid.
00103
00104 @param uuid: uuid of the update to start.
00105 """
00106 if not any([u.uuid == uuid for u in self._avail_updates.sources]):
00107 raise OSError(errno.EINVAL, "Invalid update uuid '%s'" % (uuid,))
00108
00109 self._updater_start.publish(std_msgs.msg.String(uuid))
00110
00111 def stop_update(self):
00112 """
00113 Stop the current update process, if any.
00114 """
00115 self._updater_stop.publish()
00116
00117
00118 def run_update(updater, uuid):
00119 """
00120 Run and monitor the progress of an update.
00121
00122 @param updater: Instance of Updater to use.
00123 @param uuid: update uuid.
00124 """
00125
00126
00127 class NonLocal(object):
00128 pass
00129
00130 nl = NonLocal
00131 nl.rc = 1
00132 nl.done = False
00133
00134 def on_update_status(msg):
00135 if msg.status == UpdateStatus.STS_IDLE:
00136 nl.done = True
00137 elif msg.status == UpdateStatus.STS_INVALID:
00138 print ("Invalid update uuid, '%s'." % (uuid,))
00139 nl.done = True
00140 elif msg.status == UpdateStatus.STS_BUSY:
00141 print ("Update already in progress (may be shutting down).")
00142 nl.done = True
00143 elif msg.status == UpdateStatus.STS_CANCELLED:
00144 print ("Update cancelled.")
00145 nl.done = True
00146 elif msg.status == UpdateStatus.STS_ERR:
00147 print ("Update failed: %s." % (msg.long_description,))
00148 nl.done = True
00149 nl.rc = 1
00150 elif msg.status == UpdateStatus.STS_LOAD_KEXEC:
00151 print ("Robot will now reboot to finish updating...")
00152 nl.rc = 0
00153 else:
00154 print ("Updater: %s" % (msg.long_description))
00155
00156 def on_shutdown():
00157 updater.stop_update()
00158 rospy.on_shutdown(on_shutdown)
00159
00160 updater.status_changed.connect(on_update_status)
00161
00162 try:
00163 updater.command_update(uuid)
00164 except OSError, e:
00165 if e.errno == errno.EINVAL:
00166 print e.strerror
00167 return 1
00168 raise
00169
00170 try:
00171 baxter_dataflow.wait_for(
00172 lambda: nl.done == True,
00173 timeout=5 * 60,
00174 timeout_msg="Timeout waiting for update to succeed"
00175 )
00176 except Exception, e:
00177 if not (hasattr(e, 'errno') and e.errno == errno.ESHUTDOWN):
00178 print e.strerror
00179 nl.rc = 1
00180
00181 return nl.rc
00182
00183
00184 def main():
00185 parser = argparse.ArgumentParser()
00186 required = parser.add_mutually_exclusive_group(required=True)
00187 required.add_argument('-l', '--list', action='store_const',
00188 dest='cmd', const='list', default='update',
00189 help="List available updates and UUID's")
00190 required.add_argument('-u', '--update', dest='uuid', default='',
00191 help='Launch the given update')
00192 args = parser.parse_args(rospy.myargv()[1:])
00193 cmd = args.cmd
00194 uuid = args.uuid
00195
00196 rospy.init_node('rsdk_update_robot')
00197 updater = Updater()
00198
00199 if cmd == 'list':
00200 updates = updater.list()
00201 if not len(updates):
00202 print ("No available updates")
00203 else:
00204 print ("%-30s%s" % ("Version", "UUID"))
00205 for update in updates:
00206 print("%-30s%s" % (update[0], update[1]))
00207 return 0
00208 elif cmd == 'update':
00209 if uuid == '':
00210 print "Error: no update uuid specified"
00211 return 1
00212 msg = ("NOTE: Please plug in any Rethink Electric Parallel Grippers\n"
00213 " into the robot now, so that the Gripper Firmware\n"
00214 " can be automatically upgraded with the robot.\n")
00215 print (msg)
00216 raw_input("Press <Enter> to Continue...")
00217 if rospy.is_shutdown():
00218 return 0
00219 return run_update(updater, uuid)
00220
00221 if __name__ == '__main__':
00222 sys.exit(main())