Exemplo n.º 1
0
    def __init__(self,
                 link,
                 download_dir=None,
                 prompt=True,
                 serial_upgrade=False):
        """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
        self.link = link
        self.settings = {}
        self.prompt = prompt
        self.python_console_cmds = {'update': self}
        try:
            self.update_dl = UpdateDownloader()
            if download_dir:
                self.update_dl.set_root_path(download_dir)
        except URLError:
            self.update_dl = None
            pass
        self.erase_en = True
        self.stm_fw = FirmwareFileDialog('bin')
        self.stm_fw.on_trait_change(self._manage_enables, 'status')
        self.nap_fw = FirmwareFileDialog('M25')
        self.nap_fw.on_trait_change(self._manage_enables, 'status')
        self.stream = OutputStream()
        self.serial_upgrade = serial_upgrade
        self.last_call_fw_version = None
        if not self.serial_upgrade:
            self._write(
                "1. Insert the USB flash drive provided with your Piki Multi into "
                "your computer.  Select the flash drive root directory as the "
                "firmware download destination using the \"Please "
                "choose a directory for downloaded firmware files\" directory "
                "chooser above.  Press the \"Download Latest Firmware\" button.  "
                "This will download the latest Piksi Multi firmware file onto the "
                "USB flashdrive.\n"
                "2. Eject the drive from your computer and plug it "
                "into the Piksi Multi evaluation board.\n"
                "3. Reset your Piksi Multi and it will upgrade to the version "
                "on the USB flash drive. This should take less than 5 minutes.\n"
                "4. When the upgrade completes you will be prompted to remove the "
                "USB flash drive and reset your Piksi Multi.\n"
                "5. Verify that the firmware version has upgraded via inspection "
                "of the Current Firmware Version box on the Firmware Update Tab "
                "of the Swift Console.\n")
Exemplo n.º 2
0
  def __init__(self, link, prompt=True):
    self.link = link
    self.settings = {}
    self.prompt = prompt
    self.python_console_cmds = {
      'update': self

    }
    self.update_dl = None
    self.stm_fw = IntelHexFileDialog('STM')
    self.stm_fw.on_trait_change(self._manage_enables, 'status')
    self.nap_fw = IntelHexFileDialog('M25')
    self.nap_fw.on_trait_change(self._manage_enables, 'status')
    self.stream = OutputStream()
    self.get_latest_version_info()
Exemplo n.º 3
0
  def __init__(self, link, prompt=True):
    """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
    self.link = link
    self.settings = {}
    self.prompt = prompt
    self.python_console_cmds = {
      'update': self

    }
    self.update_dl = None
    self.erase_en = True
    self.stm_fw = IntelHexFileDialog('STM')
    self.stm_fw.on_trait_change(self._manage_enables, 'status')
    self.nap_fw = IntelHexFileDialog('M25')
    self.nap_fw.on_trait_change(self._manage_enables, 'status')
    self.stream = OutputStream()
    self.get_latest_version_info()
Exemplo n.º 4
0
  def __init__(self, *args, **kwargs):
    try:
      update = kwargs.pop('update')
    except KeyError:
      update = True
    self.console_output = OutputStream()
    sys.stdout = self.console_output
    sys.stderr = self.console_output
    self.log_file = None
    log = kwargs.pop('log')
    reset = kwargs.pop('reset')
    try:
      self.link = serial_link.SerialLink(*args, **kwargs)
      self.link.add_callback(ids.PRINT, self.print_message_callback)
      self.link.add_callback(ids.DEBUG_VAR, self.debug_var_callback)
      # Setup logging
      if log:
        log_name = serial_link.generate_log_filename()
        self.log_file = open(log_name, 'w+')
        print "Logging at %s." % log_name
        self.link.add_global_callback(serial_link.default_log_callback(self.log_file))
        if reset:
          self.link.send_message(ids.RESET, '')

      settings_read_finished_functions = []

      self.tracking_view = TrackingView(self.link)
      self.solution_view = SolutionView(self.link)
      self.baseline_view = BaselineView(self.link)
      self.observation_view = ObservationView(self.link,
                                              name='Rover', relay=False)
      self.observation_view_base = ObservationView(self.link,
                                              name='Base', relay=True)
      self.system_monitor_view = SystemMonitorView(self.link)

      self.update_view = UpdateView(self.link, prompt=update)
      settings_read_finished_functions.append(self.update_view.compare_versions)

      self.settings_view = \
          SettingsView(self.link, settings_read_finished_functions)
      self.update_view.settings = self.settings_view.settings

      self.python_console_env = {
          'send_message': self.link.send_message,
          'link': self.link,
      }
      self.python_console_env.update(self.tracking_view.python_console_cmds)
      self.python_console_env.update(self.solution_view.python_console_cmds)
      self.python_console_env.update(self.baseline_view.python_console_cmds)
      self.python_console_env.update(self.observation_view.python_console_cmds)
      self.python_console_env.update(self.system_monitor_view.python_console_cmds)
      self.python_console_env.update(self.update_view.python_console_cmds)
      self.python_console_env.update(self.settings_view.python_console_cmds)
    except:
      import traceback
      traceback.print_exc()
Exemplo n.º 5
0
    def __init__(self, link, prompt=True):
        """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
        self.link = link
        self.settings = {}
        self.prompt = prompt
        self.python_console_cmds = {'update': self}
        self.update_dl = None
        self.stm_fw = IntelHexFileDialog('STM')
        self.stm_fw.on_trait_change(self._manage_enables, 'status')
        self.nap_fw = IntelHexFileDialog('M25')
        self.nap_fw.on_trait_change(self._manage_enables, 'status')
        self.stream = OutputStream()
        self.get_latest_version_info()
Exemplo n.º 6
0
  def __init__(self, link, update):
    self.console_output = OutputStream()
    sys.stdout = self.console_output
    sys.stderr = self.console_output
    try:
      self.link = link
      self.link.add_callback(self.print_message_callback, SBP_MSG_PRINT)
      self.link.add_callback(self.debug_var_callback, SBP_MSG_DEBUG_VAR)
      self.link.add_callback(self.ext_event_callback, SBP_MSG_EXT_EVENT)
      self.link.start()

      settings_read_finished_functions = []

      self.tracking_view = TrackingView(self.link)
      self.solution_view = SolutionView(self.link)
      self.baseline_view = BaselineView(self.link)
      self.observation_view = ObservationView(self.link,
                                              name='Rover', relay=False)
      self.observation_view_base = ObservationView(self.link,
                                              name='Base', relay=True)
      self.system_monitor_view = SystemMonitorView(self.link)

      self.update_view = UpdateView(self.link, prompt=update)
      settings_read_finished_functions.append(self.update_view.compare_versions)

      # Once we have received the settings, update device_serial with the Piksi
      # serial number which will be displayed in the window title
      def update_serial():
        serial_string = self.settings_view.settings['system_info']['serial_number'].value
        self.device_serial = 'PK%04d' % int(serial_string)
      settings_read_finished_functions.append(update_serial)

      self.settings_view = \
          SettingsView(self.link, settings_read_finished_functions)
      self.update_view.settings = self.settings_view.settings

      self.python_console_env = {
          'send_message': self.link.send,
          'link': self.link,
      }
      self.python_console_env.update(self.tracking_view.python_console_cmds)
      self.python_console_env.update(self.solution_view.python_console_cmds)
      self.python_console_env.update(self.baseline_view.python_console_cmds)
      self.python_console_env.update(self.observation_view.python_console_cmds)
      self.python_console_env.update(self.system_monitor_view.python_console_cmds)
      self.python_console_env.update(self.update_view.python_console_cmds)
      self.python_console_env.update(self.settings_view.python_console_cmds)
    except:
      import traceback
      traceback.print_exc()
Exemplo n.º 7
0
  def __init__(self, link, prompt=True, serial_upgrade=False):
    """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
    self.link = link
    self.settings = {}
    self.prompt = prompt
    self.python_console_cmds = {
      'update': self

    }
    try:
      self.update_dl = UpdateDownloader()
    except URLError:
      self.update_dl = None
      pass
    self.erase_en = True
    self.stm_fw = FirmwareFileDialog('bin')
    self.stm_fw.on_trait_change(self._manage_enables, 'status')
    self.nap_fw = FirmwareFileDialog('M25')
    self.nap_fw.on_trait_change(self._manage_enables, 'status')
    self.stream = OutputStream()
    self.serial_upgrade = serial_upgrade
    self.last_call_fw_version = None
    if not self.serial_upgrade:
      self._write(
           "1. Insert the USB flash drive provided with your Piki Multi into "
           "your computer.  Select the flash drive root directory as the "
           "firmware download destination using the \"Please "
           "choose a directory for downloaded firmware files\" directory "
           "chooser above.  Press the \"Download Latest Firmware\" button.  "
           "This will download the latest Piksi Multi firmware file onto the "
           "USB flashdrive.\n"
           "2. Eject the drive from your computer and plug it "
           "into the Piksi Multi evaluation board.\n"
           "3. Reset your Piksi Multi and it will upgrade to the version "
           "on the USB flash drive. This should take less than 5 minutes.\n"
           "4. When the upgrade completes you will be prompted to remove the "
           "USB flash drive and reset your Piksi Multi.\n"
           "5. Verify that the firmware version has upgraded via inspection "
           "of the Current Firmware Version box on the Firmware Update Tab "
           "of the Swift Console.\n")
Exemplo n.º 8
0
    def __init__(self, port=serial_link.DEFAULT_PORT):
        self.console_output = OutputStream()

        self.link = serial_link.SerialLink(port)
        self.link.add_callback(serial_link.MSG_PRINT,
                               self.print_message_callback)

        self.tracking_view = TrackingView(self.link)
        self.almanac_view = AlmanacView(self.link)
        self.solution_view = SolutionView(self.link)

        self.flash = flash.Flash(self.link)
        self.flash.start()
        self.python_console_env = {
            'send_message': self.link.send_message,
            'link': self.link,
            'flash': self.flash
        }
        self.python_console_env.update(self.tracking_view.python_console_cmds)
        self.python_console_env.update(self.almanac_view.python_console_cmds)
        self.python_console_env.update(self.solution_view.python_console_cmds)
Exemplo n.º 9
0
class UpdateView(HasTraits):

  piksi_stm_vers = String('Waiting for Piksi to send settings...', width=COLUMN_WIDTH)
  newest_stm_vers = String('Downloading Newest Firmware info...')
  piksi_nap_vers = String('Waiting for Piksi to send settings...')
  newest_nap_vers = String('Downloading Newest Firmware info...')
  local_console_vers = String('v' + CONSOLE_VERSION)
  newest_console_vers = String('Downloading Newest Console info...')

  erase_stm = Bool(True)
  erase_en = Bool(True)

  update_stm_firmware = Button(label='Update STM')
  update_nap_firmware = Button(label='Update NAP')
  update_full_firmware = Button(label='Update Piksi STM and NAP Firmware')

  updating = Bool(False)
  update_stm_en = Bool(False)
  update_nap_en = Bool(False)
  update_en = Bool(False)

  download_firmware = Button(label='Download Newest Firmware Files')
  download_stm = Button(label='Download', height=HT)
  download_nap = Button(label='Download', height=HT)
  downloading = Bool(False)
  download_fw_en = Bool(True)

  stm_fw = Instance(IntelHexFileDialog)
  nap_fw = Instance(IntelHexFileDialog)

  stream = Instance(OutputStream)

  view = View(
    VGroup(
      HGroup(
        VGroup(
          Item('piksi_stm_vers', label='Current', resizable=True),
          Item('newest_stm_vers', label='Latest', resizable=True),
          Item('stm_fw', style='custom', show_label=True, \
               label="Local File", enabled_when='download_fw_en'),
          HGroup(Item('update_stm_firmware', show_label=False, \
                     enabled_when='update_stm_en'),
                Item('erase_stm', label='Erase STM flash\n(recommended)', \
                      enabled_when='erase_en', show_label=True)),
          show_border=True, label="STM Firmware Version"
        ),
        VGroup(
          Item('piksi_nap_vers', label='Current', resizable=True),
          Item('newest_nap_vers', label='Latest', resizable=True),
          Item('nap_fw', style='custom', show_label=True, \
               label="Local File", enabled_when='download_fw_en'),
          HGroup(Item('update_nap_firmware', show_label=False, \
                      enabled_when='update_nap_en'),
                 Item(width=50, label="                  ")),
          show_border=True, label="NAP Firmware Version"
          ),
        VGroup(
          Item('local_console_vers', label='Current', resizable=True),
          Item('newest_console_vers', label='Latest'),
          label="Piksi Console Version", show_border=True),
          ),
      UItem('download_firmware', enabled_when='download_fw_en'),
      UItem('update_full_firmware', enabled_when='update_en'),
      Item(
        'stream',
        style='custom',
        editor=InstanceEditor(),
        show_label=False,
      ),
    )
  )

  def __init__(self, link, prompt=True):
    """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
    self.link = link
    self.settings = {}
    self.prompt = prompt
    self.python_console_cmds = {
      'update': self

    }
    self.update_dl = None
    self.erase_en = True
    self.stm_fw = IntelHexFileDialog('STM')
    self.stm_fw.on_trait_change(self._manage_enables, 'status')
    self.nap_fw = IntelHexFileDialog('M25')
    self.nap_fw.on_trait_change(self._manage_enables, 'status')
    self.stream = OutputStream()
    self.get_latest_version_info()

  def _manage_enables(self):
    """ Manages whether traits widgets are enabled in the UI or not. """
    if self.updating == True or self.downloading == True:
      self.update_stm_en = False
      self.update_nap_en = False
      self.update_en = False
      self.download_fw_en = False
      self.erase_en = False
    else:
      self.download_fw_en = True
      self.erase_en = True
      if self.stm_fw.ihx is not None:
        self.update_stm_en = True
      else:
        self.update_stm_en = False
        self.update_en = False
      if self.nap_fw.ihx is not None:
        self.update_nap_en = True
      else:
        self.update_nap_en = False
        self.update_en = False
      if self.nap_fw.ihx is not None and self.stm_fw.ihx is not None:
        self.update_en = True

  def _updating_changed(self):
    """ Handles self.updating trait being changed. """
    self._manage_enables()

  def _downloading_changed(self):
    """ Handles self.downloading trait being changed. """
    self._manage_enables()

  def _write(self, text):
    """
    Stream style write function. Allows flashing debugging messages to be
    routed to embedded text console.

    Parameters
    ----------
    text : string
      Text to be written to screen.
    """
    self.stream.write(text)
    self.stream.write('\n')
    self.stream.flush()

  def _update_stm_firmware_fired(self):
    """
    Handle update_stm_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates,
                                          args=("STM",))
    self._firmware_update_thread.start()

  def _update_nap_firmware_fired(self):
    """
    Handle update_nap_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates,
                                          args=("M25",))
    self._firmware_update_thread.start()

  def _update_full_firmware_fired(self):
    """
    Handle update_full_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates,
                                          args=("ALL",))
    self._firmware_update_thread.start()

  def _download_firmware(self):
    """ Download latest firmware from swiftnav.com. """
    self._write('')

    # Check that we received the index file from the website.
    if self.update_dl == None:
      self._write("Error: Can't download firmware files")
      return

    self.downloading = True

    status = 'Downloading Newest Firmware...'
    self.nap_fw.clear(status)
    self.stm_fw.clear(status)
    self._write(status)

    # Get firmware files from Swift Nav's website, save to disk, and load.
    try:
      self._write('Downloading Newest NAP firmware')
      filepath = self.update_dl.download_nap_firmware()
      self._write('Saved file to %s' % filepath)
      self.nap_fw.load_ihx(filepath)
    except AttributeError:
      self.nap_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: index file not downloaded yet")
    except KeyError:
      self.nap_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: URL not present in index")
    except URLError:
      self.nap_fw.clear("Error downloading firmware")
      self._write("Error: Failed to download latest NAP firmware from Swift Navigation's website")

    try:
      self._write('Downloading Newest STM firmware')
      filepath = self.update_dl.download_stm_firmware()
      self._write('Saved file to %s' % filepath)
      self.stm_fw.load_ihx(filepath)
    except AttributeError:
      self.stm_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: index file not downloaded yet")
    except KeyError:
      self.stm_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: URL not present in index")
    except URLError:
      self.stm_fw.clear("Error downloading firmware")
      self._write("Error: Failed to download latest STM firmware from Swift Navigation's website")

    self.downloading = False

  def _download_firmware_fired(self):
    """
    Handle download_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._download_firmware_thread.is_alive():
        return
    except AttributeError:
      pass

    self._download_firmware_thread = Thread(target=self._download_firmware)
    self._download_firmware_thread.start()

  def compare_versions(self):
    """
    To be called after latest Piksi firmware info has been received from
    device, to decide if current firmware on Piksi is out of date. Starts a
    thread so as not to block GUI thread.
    """
    try:
      if self._compare_versions_thread.is_alive():
        return
    except AttributeError:
      pass

    self._compare_versions_thread = Thread(target=self._compare_versions)
    self._compare_versions_thread.start()

  def _compare_versions(self):
    """
    Compares version info between received firmware version / current console
    and firmware / console info from website to decide if current firmware or
    console is out of date. Prompt user to update if so.
    """
    # Check that settings received from Piksi contain FW versions.
    try:
      self.piksi_stm_vers = \
        self.settings['system_info']['firmware_version'].value
      self.piksi_nap_vers = \
        self.settings['system_info']['nap_version'].value
    except KeyError:
      self._write("\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n")
      return

    # Check that we received the index file from the website.
    if self.update_dl == None:
      self._write("Error: No website index to use to compare versions with local firmware")
      return

    # Check if console is out of date and notify user if so.
    if self.prompt:
      local_console_version = parse_version(CONSOLE_VERSION)
      remote_console_version = parse_version(self.newest_console_vers)
      self.console_outdated = remote_console_version > local_console_version

      if self.console_outdated:
        console_outdated_prompt = \
            prompt.CallbackPrompt(
                                  title="Piksi Console Outdated",
                                  actions=[prompt.close_button],
                                 )

        console_outdated_prompt.text = \
            "Your Piksi Console is out of date and may be incompatible\n" + \
            "with current firmware. We highly recommend upgrading to\n" + \
            "ensure proper behavior.\n\n" + \
            "Please visit http://downloads.swiftnav.com to\n" + \
            "download the newest version.\n\n" + \
            "Local Console Version :\n\t" + \
                "v" + CONSOLE_VERSION + \
            "\nNewest Console Version :\n\t" + \
                self.update_dl.index['piksi_v2.3.1']['console']['version'] + "\n"

        console_outdated_prompt.run()

    # For timing aesthetics between windows popping up.
    sleep(0.5)

    # Check if firmware is out of date and notify user if so.
    if self.prompt:
      local_stm_version = parse_version(
          self.settings['system_info']['firmware_version'].value)
      remote_stm_version = parse_version(self.newest_stm_vers)

      local_nap_version = parse_version(
          self.settings['system_info']['nap_version'].value)
      remote_nap_version = parse_version(self.newest_nap_vers)

      self.fw_outdated = remote_nap_version > local_nap_version or \
                         remote_stm_version > local_stm_version

      if self.fw_outdated:
        fw_update_prompt = \
            prompt.CallbackPrompt(
                                  title='Firmware Update',
                                  actions=[prompt.close_button]
                                 )

        fw_update_prompt.text = \
            "New Piksi firmware available.\n\n" + \
            "Please use the Firmware Update tab to update.\n\n" + \
            "Newest STM Version :\n\t%s\n\n" % \
                self.update_dl.index['piksi_v2.3.1']['stm_fw']['version'] + \
            "Newest SwiftNAP Version :\n\t%s\n\n" % \
                self.update_dl.index['piksi_v2.3.1']['nap_fw']['version']

        fw_update_prompt.run()

  def get_latest_version_info(self):
    """
    Get latest firmware / console version from website. Starts thread so as not
    to block the GUI thread.
    """
    try:
      if self._get_latest_version_info_thread.is_alive():
        return
    except AttributeError:
      pass

    self._get_latest_version_info_thread = Thread(target=self._get_latest_version_info)
    self._get_latest_version_info_thread.start()

  def _get_latest_version_info(self):
    """ Get latest firmware / console version from website. """
    try:
      self.update_dl = UpdateDownloader()
    except URLError:
      self._write("\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n")
      return

    # Make sure index contains all keys we are interested in.
    try:
      self.newest_stm_vers = self.update_dl.index['piksi_v2.3.1']['stm_fw']['version']
      self.newest_nap_vers = self.update_dl.index['piksi_v2.3.1']['nap_fw']['version']
      self.newest_console_vers = self.update_dl.index['piksi_v2.3.1']['console']['version']
    except KeyError:
      self._write("\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n" % INDEX_URL)
      return

  def manage_stm_firmware_update(self):
    # Erase all of STM's flash (other than bootloader) if box is checked.
    if self.erase_stm:
      text = "Erasing STM"
      self._write(text)
      self.create_flash("STM")
      sectors_to_erase = set(range(self.pk_flash.n_sectors)).difference(set(self.pk_flash.restricted_sectors))
      progress_dialog = PulsableProgressDialog(len(sectors_to_erase), False)
      progress_dialog.title = text
      GUI.invoke_later(progress_dialog.open)
      erase_count = 0
      for s in sorted(sectors_to_erase):
        progress_dialog.progress(erase_count)
        self._write('Erasing %s sector %d' % (self.pk_flash.flash_type,s))
        self.pk_flash.erase_sector(s)
        erase_count += 1
      self.stop_flash()
      self._write("")
      progress_dialog.close()

    # Flash STM.
    text = "Updating STM"
    self._write(text)
    self.create_flash("STM")
    stm_n_ops = self.pk_flash.ihx_n_ops(self.stm_fw.ihx, \
                                        erase = not self.erase_stm)
    progress_dialog = PulsableProgressDialog(stm_n_ops, True)
    progress_dialog.title = text
    GUI.invoke_later(progress_dialog.open)
    # Don't erase sectors if we've already done so above.
    self.pk_flash.write_ihx(self.stm_fw.ihx, self.stream, mod_print=0x40, \
                            elapsed_ops_cb = progress_dialog.progress, \
                            erase = not self.erase_stm)
    self.stop_flash()
    self._write("")
    progress_dialog.close()

  def manage_nap_firmware_update(self, check_version=False):
    # Flash NAP if out of date.
    try:
      local_nap_version = parse_version(
          self.settings['system_info']['nap_version'].value)
      remote_nap_version = parse_version(self.newest_nap_vers)
      nap_out_of_date = local_nap_version != remote_nap_version
    except KeyError:
      nap_out_of_date = True
    if nap_out_of_date or check_version==False:
      text = "Updating NAP"
      self._write(text)
      self.create_flash("M25")
      nap_n_ops = self.pk_flash.ihx_n_ops(self.nap_fw.ihx)
      progress_dialog = PulsableProgressDialog(nap_n_ops, True)
      progress_dialog.title = text
      GUI.invoke_later(progress_dialog.open)
      self.pk_flash.write_ihx(self.nap_fw.ihx, self.stream, mod_print=0x40, \
                              elapsed_ops_cb = progress_dialog.progress)
      self.stop_flash()
      self._write("")
      progress_dialog.close()
      return True
    else:
      text = "NAP is already to latest version, not updating!"
      self._write(text)
      self._write("")
      return False

  # Executed in GUI thread, called from Handler.
  def manage_firmware_updates(self, device):
    """
    Update Piksi firmware. Erase entire STM flash (other than bootloader)
    if so directed. Flash NAP only if new firmware is available.
    """
    self.updating = True
    update_nap = False
    self._write('')

    if device == "STM":
      self.manage_stm_firmware_update()
    elif device == "M25":
      update_nap = self.manage_nap_firmware_update()
    else:
      self.manage_stm_firmware_update()
      update_nap = self.manage_nap_firmware_update(check_version=True)

    # Must tell Piksi to jump to application after updating firmware.
    if device == "STM" or update_nap:
        self.link(MsgBootloaderJumpToApp(jump=0))
        self._write("Firmware update finished.")
        self._write("")

    self.updating = False

  def create_flash(self, flash_type):
    """
    Create flash.Flash instance and set Piksi into bootloader mode, prompting
    user to reset if necessary.

    Parameter
    ---------
    flash_type : string
      Either "STM" or "M25".
    """
    # Reset device if the application is running to put into bootloader mode.
    self.link(MsgReset())

    self.pk_boot = bootload.Bootloader(self.link)

    self._write("Waiting for bootloader handshake message from Piksi ...")
    reset_prompt = None
    handshake_received = self.pk_boot.handshake(1)

    # Prompt user to reset Piksi if we don't receive the handshake message
    # within a reasonable amount of tiime (firmware might be corrupted).
    while not handshake_received:
      reset_prompt = \
        prompt.CallbackPrompt(
                              title="Please Reset Piksi",
                              actions=[prompt.close_button],
                             )

      reset_prompt.text = \
            "You must press the reset button on your Piksi in order\n" + \
            "to update your firmware.\n\n" + \
            "Please press it now.\n\n"

      reset_prompt.run(block=False)

      while not reset_prompt.closed and not handshake_received:
        handshake_received = self.pk_boot.handshake(1)

      reset_prompt.kill()
      reset_prompt.wait()

    self._write("received bootloader handshake message.")
    self._write("Piksi Onboard Bootloader Version: " + self.pk_boot.version)

    self.pk_flash = flash.Flash(self.link, flash_type, self.pk_boot.sbp_version)

  def stop_flash(self):
    """
    Stop Flash and Bootloader instances (removes callback from SerialLink).
    """
    self.pk_flash.stop()
    self.pk_boot.stop()
Exemplo n.º 10
0
class UpdateView(HasTraits):

    piksi_stm_vers = String('Waiting for Piksi to send settings...',
                            width=COLUMN_WIDTH)
    newest_stm_vers = String('Downloading Newest Firmware info...')
    piksi_nap_vers = String('Waiting for Piksi to send settings...')
    newest_nap_vers = String('Downloading Newest Firmware info...')
    local_console_vers = String(CONSOLE_VERSION)
    newest_console_vers = String('Downloading Newest Console info...')

    erase_stm = Bool(True)
    erase_en = Bool(True)

    update_stm_firmware = Button(label='Update STM')
    update_nap_firmware = Button(label='Update NAP')
    update_full_firmware = Button(label='Update Piksi STM and NAP Firmware')

    updating = Bool(False)
    update_stm_en = Bool(False)
    update_nap_en = Bool(False)
    update_en = Bool(False)

    download_firmware = Button(label='Download Newest Firmware Files')
    download_stm = Button(label='Download', height=HT)
    download_nap = Button(label='Download', height=HT)
    downloading = Bool(False)
    download_fw_en = Bool(True)

    stm_fw = Instance(IntelHexFileDialog)
    nap_fw = Instance(IntelHexFileDialog)

    stream = Instance(OutputStream)

    view = View(
      VGroup(
        HGroup(
          VGroup(
            Item('piksi_stm_vers', label='Current', resizable=True),
            Item('newest_stm_vers', label='Latest', resizable=True),
            Item('stm_fw', style='custom', show_label=True, \
                 label="Local File", enabled_when='download_fw_en'),
            HGroup(Item('update_stm_firmware', show_label=False, \
                       enabled_when='update_stm_en'),
                  Item('erase_stm', label='Erase STM flash\n(recommended)', \
                        enabled_when='erase_en', show_label=True)),
            show_border=True, label="STM Firmware Version"
          ),
          VGroup(
            Item('piksi_nap_vers', label='Current', resizable=True),
            Item('newest_nap_vers', label='Latest', resizable=True),
            Item('nap_fw', style='custom', show_label=True, \
                 label="Local File", enabled_when='download_fw_en'),
            HGroup(Item('update_nap_firmware', show_label=False, \
                        enabled_when='update_nap_en'),
                   Item(width=50, label="                  ")),
            show_border=True, label="NAP Firmware Version"
            ),
          VGroup(
            Item('local_console_vers', label='Current', resizable=True),
            Item('newest_console_vers', label='Latest'),
            label="Piksi Console Version", show_border=True),
            ),
        UItem('download_firmware', enabled_when='download_fw_en'),
        UItem('update_full_firmware', enabled_when='update_en'),
        Item(
          'stream',
          style='custom',
          editor=InstanceEditor(),
          show_label=False,
        ),
      )
    )

    def __init__(self, link, prompt=True):
        """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
        self.link = link
        self.settings = {}
        self.prompt = prompt
        self.python_console_cmds = {'update': self}
        self.update_dl = None
        self.stm_fw = IntelHexFileDialog('STM')
        self.stm_fw.on_trait_change(self._manage_enables, 'status')
        self.nap_fw = IntelHexFileDialog('M25')
        self.nap_fw.on_trait_change(self._manage_enables, 'status')
        self.stream = OutputStream()
        self.get_latest_version_info()

    def _manage_enables(self):
        """ Manages whether traits widgets are enabled in the UI or not. """
        if self.updating == True or self.downloading == True:
            self.update_stm_en = False
            self.update_nap_en = False
            self.update_en = False
            self.download_fw_en = False
        else:
            self.download_fw_en = True
            if self.stm_fw.ihx is not None:
                self.update_stm_en = True
            else:
                self.update_stm_en = False
                self.update_en = False
            if self.nap_fw.ihx is not None:
                self.update_nap_en = True
            else:
                self.update_nap_en = False
                self.update_en = False
            if self.nap_fw.ihx is not None and self.stm_fw.ihx is not None:
                self.update_en = True
        if self.updating == True:
            self.erase_en = False
        else:
            self.erase_en = True

    def _updating_changed(self):
        """ Handles self.updating trait being changed. """
        self._manage_enables()

    def _downloading_changed(self):
        """ Handles self.downloading trait being changed. """
        self._manage_enables()

    def _write(self, text):
        """
    Stream style write function. Allows flashing debugging messages to be
    routed to embedded text console.

    Parameters
    ----------
    text : string
      Text to be written to screen.
    """
        self.stream.write(text)
        self.stream.write('\n')
        self.stream.flush()

    def _update_stm_firmware_fired(self):
        """
    Handle update_stm_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("STM", ))
        self._firmware_update_thread.start()

    def _update_nap_firmware_fired(self):
        """
    Handle update_nap_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("M25", ))
        self._firmware_update_thread.start()

    def _update_full_firmware_fired(self):
        """
    Handle update_full_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("ALL", ))
        self._firmware_update_thread.start()

    def _download_firmware(self):
        """ Download latest firmware from swiftnav.com. """
        self._write('')

        # Check that we received the index file from the website.
        if self.update_dl == None:
            self._write("Error: Can't download firmware files")
            return

        self.downloading = True

        status = 'Downloading Newest Firmware...'
        self.nap_fw.clear(status)
        self.stm_fw.clear(status)
        self._write(status)

        # Get firmware files from Swift Nav's website, save to disk, and load.
        try:
            self._write('Downloading Newest NAP firmware')
            filepath = self.update_dl.download_nap_firmware()
            self._write('Saved file to %s' % filepath)
            self.nap_fw.load_ihx(filepath)
        except AttributeError:
            self.nap_fw.clear("Error downloading firmware")
            self._write(
                "Error downloading firmware: index file not downloaded yet")
        except KeyError:
            self.nap_fw.clear("Error downloading firmware")
            self._write("Error downloading firmware: URL not present in index")
        except URLError:
            self.nap_fw.clear("Error downloading firmware")
            self._write(
                "Error: Failed to download latest NAP firmware from Swift Navigation's website"
            )

        try:
            self._write('Downloading Newest STM firmware')
            filepath = self.update_dl.download_stm_firmware()
            self._write('Saved file to %s' % filepath)
            self.stm_fw.load_ihx(filepath)
        except AttributeError:
            self.stm_fw.clear("Error downloading firmware")
            self._write(
                "Error downloading firmware: index file not downloaded yet")
        except KeyError:
            self.stm_fw.clear("Error downloading firmware")
            self._write("Error downloading firmware: URL not present in index")
        except URLError:
            self.stm_fw.clear("Error downloading firmware")
            self._write(
                "Error: Failed to download latest STM firmware from Swift Navigation's website"
            )

        self.downloading = False

    def _download_firmware_fired(self):
        """
    Handle download_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._download_firmware_thread.is_alive():
                return
        except AttributeError:
            pass

        self._download_firmware_thread = Thread(target=self._download_firmware)
        self._download_firmware_thread.start()

    def compare_versions(self):
        """
    To be called after latest Piksi firmware info has been received from
    device, to decide if current firmware on Piksi is out of date. Starts a
    thread so as not to block GUI thread.
    """
        try:
            if self._compare_versions_thread.is_alive():
                return
        except AttributeError:
            pass

        self._compare_versions_thread = Thread(target=self._compare_versions)
        self._compare_versions_thread.start()

    def _compare_versions(self):
        """
    Compares version info between received firmware version / current console
    and firmware / console info from website to decide if current firmware or
    console is out of date. Prompt user to update if so.
    """
        # Check that settings received from Piksi contain FW versions.
        try:
            self.piksi_stm_vers = \
              self.settings['system_info']['firmware_version'].value
            self.piksi_nap_vers = \
              self.settings['system_info']['nap_version'].value
        except KeyError:
            self._write(
                "\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n"
            )
            return

        # Check that we received the index file from the website.
        if self.update_dl == None:
            self._write(
                "Error: No website index to use to compare versions with local firmware"
            )
            return

        # Check if console is out of date and notify user if so.
        if self.prompt:
            local_console_version = parse_version(CONSOLE_VERSION)
            remote_console_version = parse_version(self.newest_console_vers)
            self.console_outdated = remote_console_version > local_console_version

            if self.console_outdated:
                console_outdated_prompt = \
                    prompt.CallbackPrompt(
                                          title="Piksi Console Outdated",
                                          actions=[prompt.close_button],
                                         )

                console_outdated_prompt.text = \
                    "Your Piksi Console is out of date and may be incompatible\n" + \
                    "with current firmware. We highly recommend upgrading to\n" + \
                    "ensure proper behavior.\n\n" + \
                    "Please visit http://downloads.swiftnav.com to\n" + \
                    "download the newest version.\n\n" + \
                    "Local Console Version :\n\t" + \
                        CONSOLE_VERSION + \
                    "\nNewest Console Version :\n\t" + \
                        self.update_dl.index['piksi_v2.3.1']['console']['version'] + "\n"

                console_outdated_prompt.run()

        # For timing aesthetics between windows popping up.
        sleep(0.5)

        # Check if firmware is out of date and notify user if so.
        if self.prompt:
            local_stm_version = parse_version(
                self.settings['system_info']['firmware_version'].value)
            remote_stm_version = parse_version(self.newest_stm_vers)

            local_nap_version = parse_version(
                self.settings['system_info']['nap_version'].value)
            remote_nap_version = parse_version(self.newest_nap_vers)

            self.fw_outdated = remote_nap_version > local_nap_version or \
                               remote_stm_version > local_stm_version

            if self.fw_outdated:
                fw_update_prompt = \
                    prompt.CallbackPrompt(
                                          title='Firmware Update',
                                          actions=[prompt.close_button]
                                         )

                fw_update_prompt.text = \
                    "New Piksi firmware available.\n\n" + \
                    "Please use the Firmware Update tab to update.\n\n" + \
                    "Newest STM Version :\n\t%s\n\n" % \
                        self.update_dl.index['piksi_v2.3.1']['stm_fw']['version'] + \
                    "Newest SwiftNAP Version :\n\t%s\n\n" % \
                        self.update_dl.index['piksi_v2.3.1']['nap_fw']['version']

                fw_update_prompt.run()

    def get_latest_version_info(self):
        """
    Get latest firmware / console version from website. Starts thread so as not
    to block the GUI thread.
    """
        try:
            if self._get_latest_version_info_thread.is_alive():
                return
        except AttributeError:
            pass

        self._get_latest_version_info_thread = Thread(
            target=self._get_latest_version_info)
        self._get_latest_version_info_thread.start()

    def _get_latest_version_info(self):
        """ Get latest firmware / console version from website. """
        try:
            self.update_dl = UpdateDownloader()
        except URLError:
            self._write(
                "\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n"
            )
            return

        # Make sure index contains all keys we are interested in.
        try:
            self.newest_stm_vers = self.update_dl.index['piksi_v2.3.1'][
                'stm_fw']['version']
            self.newest_nap_vers = self.update_dl.index['piksi_v2.3.1'][
                'nap_fw']['version']
            self.newest_console_vers = self.update_dl.index['piksi_v2.3.1'][
                'console']['version']
        except KeyError:
            self._write(
                "\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n"
                % INDEX_URL)
            return

    def manage_stm_firmware_update(self):
        # Erase all of STM's flash (other than bootloader) if box is checked.
        if self.erase_stm:
            text = "Erasing STM"
            self._write(text)
            self.create_flash("STM")
            sectors_to_erase = set(range(self.pk_flash.n_sectors)).difference(
                set(self.pk_flash.restricted_sectors))
            progress_dialog = PulsableProgressDialog(len(sectors_to_erase),
                                                     False)
            progress_dialog.title = text
            GUI.invoke_later(progress_dialog.open)
            erase_count = 0
            for s in sorted(sectors_to_erase):
                progress_dialog.progress(erase_count)
                self._write('Erasing %s sector %d' %
                            (self.pk_flash.flash_type, s))
                self.pk_flash.erase_sector(s)
                erase_count += 1
            self.stop_flash()
            self._write("")
            progress_dialog.close()

        # Flash STM.
        text = "Updating STM"
        self._write(text)
        self.create_flash("STM")
        stm_n_ops = self.pk_flash.ihx_n_ops(self.stm_fw.ihx, \
                                            erase = not self.erase_stm)
        progress_dialog = PulsableProgressDialog(stm_n_ops, True)
        progress_dialog.title = text
        GUI.invoke_later(progress_dialog.open)
        # Don't erase sectors if we've already done so above.
        self.pk_flash.write_ihx(self.stm_fw.ihx, self.stream, mod_print=0x40, \
                                elapsed_ops_cb = progress_dialog.progress, \
                                erase = not self.erase_stm)
        self.stop_flash()
        self._write("")
        progress_dialog.close()

    def manage_nap_firmware_update(self):
        # Flash NAP if out of date.
        try:
            local_nap_version = parse_version(
                self.settings['system_info']['nap_version'].value)
            remote_nap_version = parse_version(self.newest_nap_vers)
            nap_out_of_date = local_nap_version != remote_nap_version
        except KeyError:
            nap_out_of_date = True
        if nap_out_of_date:
            text = "Updating NAP"
            self._write(text)
            self.create_flash("M25")
            nap_n_ops = self.pk_flash.ihx_n_ops(self.nap_fw.ihx)
            progress_dialog = PulsableProgressDialog(nap_n_ops, True)
            progress_dialog.title = text
            GUI.invoke_later(progress_dialog.open)
            self.pk_flash.write_ihx(self.nap_fw.ihx, self.stream, mod_print=0x40, \
                                    elapsed_ops_cb = progress_dialog.progress)
            self.stop_flash()
            self._write("")
            progress_dialog.close()
            return True
        else:
            text = "NAP is already to latest version, not updating!"
            self._write(text)
            self._write("")
            return False

    # Executed in GUI thread, called from Handler.
    def manage_firmware_updates(self, device):
        """
    Update Piksi firmware. Erase entire STM flash (other than bootloader)
    if so directed. Flash NAP only if new firmware is available.
    """
        self.updating = True
        update_nap = False
        self._write('')

        if device == "STM":
            self.manage_stm_firmware_update()
        elif device == "M25":
            update_nap = self.manage_nap_firmware_update()
        else:
            self.manage_stm_firmware_update()
            update_nap = self.manage_nap_firmware_update()

        # Must tell Piksi to jump to application after updating firmware.
        if device == "STM" or update_nap:
            self.link(MsgBootloaderJumpToApp(jump=0))
            self._write("Firmware update finished.")
            self._write("")

        self.updating = False

    def create_flash(self, flash_type):
        """
    Create flash.Flash instance and set Piksi into bootloader mode, prompting
    user to reset if necessary.

    Parameter
    ---------
    flash_type : string
      Either "STM" or "M25".
    """
        # Reset device if the application is running to put into bootloader mode.
        self.link(MsgReset())

        self.pk_boot = bootload.Bootloader(self.link)

        self._write("Waiting for bootloader handshake message from Piksi ...")
        reset_prompt = None
        handshake_received = self.pk_boot.handshake(1)

        # Prompt user to reset Piksi if we don't receive the handshake message
        # within a reasonable amount of tiime (firmware might be corrupted).
        while not handshake_received:
            reset_prompt = \
              prompt.CallbackPrompt(
                                    title="Please Reset Piksi",
                                    actions=[prompt.close_button],
                                   )

            reset_prompt.text = \
                  "You must press the reset button on your Piksi in order\n" + \
                  "to update your firmware.\n\n" + \
                  "Please press it now.\n\n"

            reset_prompt.run(block=False)

            while not reset_prompt.closed and not handshake_received:
                handshake_received = self.pk_boot.handshake(1)

            reset_prompt.kill()
            reset_prompt.wait()

        self._write("received bootloader handshake message.")
        self._write("Piksi Onboard Bootloader Version: " +
                    self.pk_boot.version)

        self.pk_flash = flash.Flash(self.link, flash_type,
                                    self.pk_boot.sbp_version)

    def stop_flash(self):
        """
    Stop Flash and Bootloader instances (removes callback from SerialLink).
    """
        self.pk_flash.stop()
        self.pk_boot.stop()
Exemplo n.º 11
0
class UpdateView(HasTraits):
    piksi_hw_rev = String('piksi_multi')
    is_v2 = Bool(False)

    piksi_stm_vers = String('Waiting for Piksi to send settings...',
                            width=COLUMN_WIDTH)
    newest_stm_vers = String('Downloading Latest Firmware info...')
    piksi_nap_vers = String('Waiting for Piksi to send settings...')
    newest_nap_vers = String('Downloading Latest Firmware info...')
    local_console_vers = String('v' + CONSOLE_VERSION)
    newest_console_vers = String('Downloading Latest Console info...')

    erase_stm = Bool(True)
    erase_en = Bool(True)

    update_stm_firmware = Button(label='Update FW')
    update_nap_firmware = Button(label='Update NAP')
    update_full_firmware = Button(label='Update Piksi STM and NAP Firmware')

    updating = Bool(False)
    update_stm_en = Bool(False)
    update_nap_en = Bool(False)
    update_en = Bool(False)
    serial_upgrade = Bool(False)
    upgrade_steps = String("Firmware upgrade steps:")

    download_firmware = Button(label='Download Latest Firmware')
    download_directory = Directory(
        "  Please choose a directory for downloaded firmware files...")
    download_stm = Button(label='Download', height=HT)
    download_nap = Button(label='Download', height=HT)
    downloading = Bool(False)
    download_fw_en = Bool(True)

    stm_fw = Instance(FirmwareFileDialog)
    nap_fw = Instance(FirmwareFileDialog)

    stream = Instance(OutputStream)

    view = View(
      VGroup(
        Item('piksi_hw_rev', label='Hardware Revision',
             editor_args={'enabled': False}, resizable=True),
        HGroup(
          VGroup(
            Item('piksi_stm_vers', label='Current', resizable=True,
                   editor_args={'enabled': False}),
            Item('newest_stm_vers', label='Latest', resizable=True,
                   editor_args={'enabled': False,
                                'readonly_allow_selection': True}),
            Item('stm_fw', style='custom', show_label=True, \
                 label="Local File", enabled_when='download_fw_en',
                 visible_when='serial_upgrade',
                 editor_args={'enabled': False}),
            HGroup(Item('update_stm_firmware', show_label=False, \
                       enabled_when='update_stm_en', visible_when='serial_upgrade'),
                  Item('erase_stm', label='Erase STM flash\n(recommended)', \
                        enabled_when='erase_en', show_label=True, visible_when='is_v2')),
            show_border=True, label="Firmware Version"
          ),
          VGroup(
            Item('piksi_nap_vers', label='Current', resizable=True,
                 editor_args={'enabled': False}),
            Item('newest_nap_vers', label='Latest', resizable=True,
                 editor_args={'enabled': False}),
            Item('nap_fw', style='custom', show_label=True, \
                 label="Local File", enabled_when='download_fw_en',
                 editor_args={'enabled': False}),
            HGroup(Item('update_nap_firmware', show_label=False, \
                        enabled_when='update_nap_en', visible_when='serial_upgrade'),
                   Item(width=50, label="                  ")),
            show_border=True, label="NAP Version",
            visible_when='is_v2'
            ),
          VGroup(
            Item('local_console_vers', label='Current', resizable=True,
                 editor_args={'enabled': False}),
            Item('newest_console_vers', label='Latest',
                  editor_args={'enabled': False}),
            label="Swift Console Version", show_border=True),
            ),
        UItem('download_directory', enabled_when='download_fw_en'),
        UItem('download_firmware', enabled_when='download_fw_en'),
        UItem('update_full_firmware', enabled_when='update_en', visible_when='is_v2'),
        VGroup(
          UItem('upgrade_steps',
                visible_when='not serial_upgrade', style='readonly'),
          Item(
            'stream',
            style='custom',
            editor=InstanceEditor(),
            show_label=False,
          ),
        show_border=True,
        )
      )
    )

    def __init__(self,
                 link,
                 download_dir=None,
                 prompt=True,
                 serial_upgrade=False):
        """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
        self.link = link
        self.settings = {}
        self.prompt = prompt
        self.python_console_cmds = {'update': self}
        try:
            self.update_dl = UpdateDownloader()
            if download_dir:
                self.update_dl.set_root_path(download_dir)
        except URLError:
            self.update_dl = None
            pass
        self.erase_en = True
        self.stm_fw = FirmwareFileDialog('bin')
        self.stm_fw.on_trait_change(self._manage_enables, 'status')
        self.nap_fw = FirmwareFileDialog('M25')
        self.nap_fw.on_trait_change(self._manage_enables, 'status')
        self.stream = OutputStream()
        self.serial_upgrade = serial_upgrade
        self.last_call_fw_version = None
        if not self.serial_upgrade:
            self._write(
                "1. Insert the USB flash drive provided with your Piki Multi into "
                "your computer.  Select the flash drive root directory as the "
                "firmware download destination using the \"Please "
                "choose a directory for downloaded firmware files\" directory "
                "chooser above.  Press the \"Download Latest Firmware\" button.  "
                "This will download the latest Piksi Multi firmware file onto the "
                "USB flashdrive.\n"
                "2. Eject the drive from your computer and plug it "
                "into the Piksi Multi evaluation board.\n"
                "3. Reset your Piksi Multi and it will upgrade to the version "
                "on the USB flash drive. This should take less than 5 minutes.\n"
                "4. When the upgrade completes you will be prompted to remove the "
                "USB flash drive and reset your Piksi Multi.\n"
                "5. Verify that the firmware version has upgraded via inspection "
                "of the Current Firmware Version box on the Firmware Update Tab "
                "of the Swift Console.\n")

    def _manage_enables(self):
        """ Manages whether traits widgets are enabled in the UI or not. """
        if self.updating == True or self.downloading == True:
            self.update_stm_en = False
            self.update_nap_en = False
            self.update_en = False
            self.download_fw_en = False
            self.erase_en = False
        else:
            self.download_fw_en = True
            self.erase_en = True
            if self.stm_fw.ihx is not None or self.stm_fw.blob is not None:
                self.update_stm_en = True
            else:
                self.update_stm_en = False
                self.update_en = False
            if self.nap_fw.ihx is not None:
                self.update_nap_en = True
            else:
                self.update_nap_en = False
                self.update_en = False
            if self.nap_fw.ihx is not None and self.stm_fw.ihx is not None:
                self.update_en = True

    def _download_directory_changed(self):
        if self.update_dl:
            self.update_dl.set_root_path(self.download_directory)

    def _updating_changed(self):
        """ Handles self.updating trait being changed. """
        self._manage_enables()

    def _downloading_changed(self):
        """ Handles self.downloading trait being changed. """
        self._manage_enables()

    def _write(self, text):
        """
    Stream style write function. Allows flashing debugging messages to be
    routed to embedded text console.

    Parameters
    ----------
    text : string
      Text to be written to screen.
    """
        self.stream.write(text)
        self.stream.write('\n')
        self.stream.flush()

    def _update_stm_firmware_fired(self):
        """
    Handle update_stm_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("STM", ))
        self._firmware_update_thread.start()

    def _update_nap_firmware_fired(self):
        """
    Handle update_nap_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("M25", ))
        self._firmware_update_thread.start()

    def _update_full_firmware_fired(self):
        """
    Handle update_full_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("ALL", ))
        self._firmware_update_thread.start()

    def _download_firmware(self):
        """ Download latest firmware from swiftnav.com. """
        self._write('')

        # Check that we received the index file from the website.
        if self.update_dl == None:
            self._write("Error: Can't download firmware files")
            return

        self.downloading = True
        status = 'Downloading Latest Firmware...'
        self.nap_fw.clear(status)
        self.stm_fw.clear(status)
        self._write(status)

        # Get firmware files from Swift Nav's website, save to disk, and load.
        if self.update_dl.index[self.piksi_hw_rev].has_key('fw'):
            try:
                self._write('Downloading Latest Multi firmware')
                filepath = self.update_dl.download_multi_firmware(
                    self.piksi_hw_rev)
                self._write('Saved file to %s' % filepath)
                self.stm_fw.load_bin(filepath)
            except AttributeError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error downloading firmware: index file not downloaded yet"
                )
            except KeyError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error downloading firmware: URL not present in index")
            except URLError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error: Failed to download latest NAP firmware from Swift Navigation's website"
                )
            self.downloading = False
            return

        try:
            self._write('Downloading Latest NAP firmware')
            filepath = self.update_dl.download_nap_firmware(self.piksi_hw_rev)
            self._write('Saved file to %s' % filepath)
            self.nap_fw.load_ihx(filepath)
        except AttributeError:
            self.nap_fw.clear("Error downloading firmware")
            self._write(
                "Error downloading firmware: index file not downloaded yet")
        except KeyError:
            self.nap_fw.clear("Error downloading firmware")
            self._write("Error downloading firmware: URL not present in index")
        except URLError:
            self.nap_fw.clear("Error downloading firmware")
            self._write(
                "Error: Failed to download latest NAP firmware from Swift Navigation's website"
            )

        try:
            self._write('Downloading Latest STM firmware')
            filepath = self.update_dl.download_stm_firmware(self.piksi_hw_rev)
            self._write('Saved file to %s' % filepath)
            self.stm_fw.load_ihx(filepath)
        except AttributeError:
            self.stm_fw.clear("Error downloading firmware")
            self._write(
                "Error downloading firmware: index file not downloaded yet")
        except KeyError:
            self.stm_fw.clear("Error downloading firmware")
            self._write("Error downloading firmware: URL not present in index")
        except URLError:
            self.stm_fw.clear("Error downloading firmware")
            self._write(
                "Error: Failed to download latest STM firmware from Swift Navigation's website"
            )

        self.downloading = False

    def _download_firmware_fired(self):
        """
    Handle download_firmware button. Starts thread so as not to block the GUI
    thread.
    """
        try:
            if self._download_firmware_thread.is_alive():
                return
        except AttributeError:
            pass

        self._download_firmware_thread = Thread(target=self._download_firmware)
        self._download_firmware_thread.start()

    def compare_versions(self):
        """
    To be called after latest Piksi firmware info has been received from
    device, to decide if current firmware on Piksi is out of date. Also informs
    user if the firmware was successfully upgraded. Starts a thread so as not
    to block GUI thread.
    """
        try:
            if self._compare_versions_thread.is_alive():
                return
        except AttributeError:
            pass

        self._compare_versions_thread = Thread(target=self._compare_versions)
        self._compare_versions_thread.start()

    def _compare_versions(self):
        """
    Compares version info between received firmware version / current console
    and firmware / console info from website to decide if current firmware or
    console is out of date. Prompt user to update if so. Informs user if
    firmware successfully upgraded.
    """
        # Check that settings received from Piksi contain FW versions.
        try:
            self.piksi_hw_rev = \
              HW_REV_LOOKUP[self.settings['system_info']['hw_revision'].value]
            self.piksi_stm_vers = \
              self.settings['system_info']['firmware_version'].value
        except KeyError:
            self._write(
                "\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n"
            )
            return

        self.is_v2 = self.piksi_hw_rev.startswith('piksi_v2')
        if self.is_v2:
            self.stm_fw.set_flash_type('STM')
            self.serial_upgrade = True
        else:
            self.stm_fw.set_flash_type('bin')

        self._get_latest_version_info()

        # Check that we received the index file from the website.
        if self.update_dl == None:
            self._write(
                "Error: No website index to use to compare versions with local firmware"
            )
            return
        # Get local stm version
        local_stm_version = None
        try:
            local_stm_version = self.settings['system_info'][
                'firmware_version'].value
        except:
            pass
        # Check if console is out of date and notify user if so.
        if self.prompt:
            local_console_version = parse_version(CONSOLE_VERSION)
            remote_console_version = parse_version(self.newest_console_vers)
            self.console_outdated = remote_console_version > local_console_version

            # we want to warn users using v2 regardless of version logic
            if self.console_outdated or self.is_v2:
                if not self.is_v2:
                    console_outdated_prompt = \
                    prompt.CallbackPrompt(
                                          title="Swift Console Outdated",
                                          actions=[prompt.close_button],
                                          )
                    console_outdated_prompt.text = \
                        "Your console is out of date and may be incompatible\n" + \
                        "with current firmware. We highly recommend upgrading to\n" + \
                        "ensure proper behavior.\n\n" + \
                        "Please visit http://support.swiftnav.com to\n" + \
                        "download the latest version.\n\n" + \
                        "Local Console Version :\n\t" + \
                            "v" + CONSOLE_VERSION + \
                        "\nLatest Console Version :\n\t" + \
                            self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"
                else:
                    console_outdated_prompt = \
                    prompt.CallbackPrompt(
                                          title="Swift Console Incompatible",
                                          actions=[prompt.close_button],
                                          )
                    console_outdated_prompt.text = \
                        "Your console is incompatible with your hardware revision.\n" + \
                        "We highly recommend using a compatible console version\n" + \
                        "to ensure proper behavior.\n\n" + \
                        "Please visit http://support.swiftnav.com to\n" + \
                        "download the latest compatible version.\n\n" + \
                        "Current Hardware revision :\n\t" + \
                             self.piksi_hw_rev + \
                        "\nLast supported Console Version: \n\t" + \
                            self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"

                console_outdated_prompt.run()

            # For timing aesthetics between windows popping up.
            sleep(0.5)

            # Check if firmware is out of date and notify user if so.
            remote_stm_version = self.newest_stm_vers

            self.fw_outdated = remote_stm_version > local_stm_version

            if self.fw_outdated:
                fw_update_prompt = \
                    prompt.CallbackPrompt(
                                          title='Firmware Update',
                                          actions=[prompt.close_button]
                                         )

                if self.update_dl.index[self.piksi_hw_rev].has_key('fw'):
                    fw_update_prompt.text = \
                      "New Piksi firmware available.\n\n" + \
                      "Please use the Firmware Update tab to update.\n\n" + \
                      "Newest Firmware Version :\n\t%s\n\n" % \
                          self.update_dl.index[self.piksi_hw_rev]['fw']['version']
                else:
                    fw_update_prompt.text = \
                      "New Piksi firmware available.\n\n" + \
                      "Please use the Firmware Update tab to update.\n\n" + \
                      "Newest STM Version :\n\t%s\n\n" % \
                          self.update_dl.index[self.piksi_hw_rev]['stm_fw']['version'] + \
                      "Newest SwiftNAP Version :\n\t%s\n\n" % \
                          self.update_dl.index[self.piksi_hw_rev]['nap_fw']['version']

                fw_update_prompt.run()

        # Check if firmware successfully upgraded and notify user if so.
        if self.last_call_fw_version is not None and \
            self.last_call_fw_version != local_stm_version:
            fw_success_str = "Firmware successfully upgraded from %s to %s." % \
                             (self.last_call_fw_version, local_stm_version)
            print fw_success_str
            self._write(fw_success_str)

        # Record firmware version reported each time this callback is called.
        self.last_call_fw_version = local_stm_version

    def _get_latest_version_info(self):
        """ Get latest firmware / console version from website. """
        try:
            self.update_dl = UpdateDownloader()
        except URLError:
            self._write(
                "\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n"
            )
            return

        # Make sure index contains all keys we are interested in.
        try:
            if self.update_dl.index[self.piksi_hw_rev].has_key('fw'):
                self.newest_stm_vers = self.update_dl.index[
                    self.piksi_hw_rev]['fw']['version']
            else:
                self.newest_stm_vers = self.update_dl.index[
                    self.piksi_hw_rev]['stm_fw']['version']
                self.newest_nap_vers = self.update_dl.index[
                    self.piksi_hw_rev]['nap_fw']['version']
            self.newest_console_vers = self.update_dl.index[
                self.piksi_hw_rev]['console']['version']
        except KeyError:
            self._write(
                "\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n"
                % INDEX_URL)
            return

    def manage_stm_firmware_update(self):
        # Erase all of STM's flash (other than bootloader) if box is checked.
        if self.erase_stm:
            text = "Erasing STM"
            self._write(text)
            self.create_flash("STM")
            sectors_to_erase = set(range(self.pk_flash.n_sectors)).difference(
                set(self.pk_flash.restricted_sectors))
            progress_dialog = PulsableProgressDialog(len(sectors_to_erase),
                                                     False)
            progress_dialog.title = text
            GUI.invoke_later(progress_dialog.open)
            erase_count = 0
            for s in sorted(sectors_to_erase):
                progress_dialog.progress(erase_count)
                self._write('Erasing %s sector %d' %
                            (self.pk_flash.flash_type, s))
                self.pk_flash.erase_sector(s)
                erase_count += 1
            self.stop_flash()
            self._write("")
            try:
                progress_dialog.close()
            except AttributeError:
                pass
        # Flash STM.
        text = "Updating STM"
        self._write(text)
        self.create_flash("STM")
        stm_n_ops = self.pk_flash.ihx_n_ops(self.stm_fw.ihx, \
                                            erase = not self.erase_stm)
        progress_dialog = PulsableProgressDialog(stm_n_ops, True)
        progress_dialog.title = text
        GUI.invoke_later(progress_dialog.open)
        # Don't erase sectors if we've already done so above.
        self.pk_flash.write_ihx(self.stm_fw.ihx, self.stream, mod_print=0x40, \
                                elapsed_ops_cb = progress_dialog.progress, \
                                erase = not self.erase_stm)
        self.stop_flash()
        self._write("")
        try:
            progress_dialog.close()
        except AttributeError:
            pass

    def manage_nap_firmware_update(self, check_version=False):
        # Flash NAP if out of date.
        try:
            local_nap_version = parse_version(
                self.settings['system_info']['nap_version'].value)
            remote_nap_version = parse_version(self.newest_nap_vers)
            nap_out_of_date = local_nap_version != remote_nap_version
        except KeyError:
            nap_out_of_date = True
        if nap_out_of_date or check_version == False:
            text = "Updating NAP"
            self._write(text)
            self.create_flash("M25")
            nap_n_ops = self.pk_flash.ihx_n_ops(self.nap_fw.ihx)
            progress_dialog = PulsableProgressDialog(nap_n_ops, True)
            progress_dialog.title = text
            GUI.invoke_later(progress_dialog.open)
            self.pk_flash.write_ihx(self.nap_fw.ihx, self.stream, mod_print=0x40, \
                                    elapsed_ops_cb = progress_dialog.progress)
            self.stop_flash()
            self._write("")
            try:
                progress_dialog.close()
            except AttributeError:
                pass
            return True
        else:
            text = "NAP is already to latest version, not updating!"
            self._write(text)
            self._write("")
            return False

    def manage_multi_firmware_update(self):
        # Set up progress dialog and transfer file to Piksi using SBP FileIO
        progress_dialog = PulsableProgressDialog(len(self.stm_fw.blob))
        progress_dialog.title = "Transferring image file"
        GUI.invoke_later(progress_dialog.open)
        self._write("Transferring image file...")
        try:
            FileIO(self.link).write("upgrade.image_set.bin",
                                    self.stm_fw.blob,
                                    progress_cb=progress_dialog.progress)
        except Exception as e:
            self._write("Failed to transfer image file to Piksi: %s\n" % e)
            progress_dialog.close()
            return
        try:
            progress_dialog.close()
        except AttributeError:
            pass

        # Setup up pulsed progress dialog and commit to flash
        progress_dialog = PulsableProgressDialog(100, True)
        progress_dialog.title = "Committing to flash"
        GUI.invoke_later(progress_dialog.open)
        self._write("Committing file to flash...")

        def log_cb(msg, **kwargs):
            self._write(msg.text)

        self.link.add_callback(log_cb, SBP_MSG_LOG)
        code = shell_command(self.link,
                             "upgrade_tool upgrade.image_set.bin",
                             600,
                             progress_cb=progress_dialog.progress)
        self.link.remove_callback(log_cb, SBP_MSG_LOG)
        progress_dialog.close()

        if code != 0:
            self._write('Failed to perform upgrade (code = %d)' % code)
            if code == -255:
                self._write('Shell command timed out.  Please try again.')
            return
        self._write('Resetting Piksi...')
        self.link(MsgReset(flags=0))

    # Executed in GUI thread, called from Handler.
    def manage_firmware_updates(self, device):
        """
    Update Piksi firmware. Erase entire STM flash (other than bootloader)
    if so directed. Flash NAP only if new firmware is available.
    """
        self.updating = True
        update_nap = False
        self._write('')
        if not self.is_v2:
            self.manage_multi_firmware_update()
            self.updating = False
            return
        elif device == "STM":
            self.manage_stm_firmware_update()
        elif device == "M25":
            update_nap = self.manage_nap_firmware_update()
        else:
            self.manage_stm_firmware_update()
            update_nap = self.manage_nap_firmware_update(check_version=True)

        # Must tell Piksi to jump to application after updating firmware.
        if device == "STM" or update_nap:
            self.link(MsgBootloaderJumpToApp(jump=0))
            self._write("Firmware update finished.")
            self._write("")

        self.updating = False

    def create_flash(self, flash_type):
        """
    Create flash.Flash instance and set Piksi into bootloader mode, prompting
    user to reset if necessary.

    Parameter
    ---------
    flash_type : string
      Either "STM" or "M25".
    """
        # Reset device if the application is running to put into bootloader mode.
        self.link(MsgReset(flags=0))

        self.pk_boot = bootload.Bootloader(self.link)

        self._write("Waiting for bootloader handshake message from Piksi ...")
        reset_prompt = None
        handshake_received = self.pk_boot.handshake(1)

        # Prompt user to reset Piksi if we don't receive the handshake message
        # within a reasonable amount of tiime (firmware might be corrupted).
        while not handshake_received:
            reset_prompt = \
              prompt.CallbackPrompt(
                                    title="Please Reset Piksi",
                                    actions=[prompt.close_button],
                                   )

            reset_prompt.text = \
                  "You must press the reset button on your Piksi in order\n" + \
                  "to update your firmware.\n\n" + \
                  "Please press it now.\n\n"

            reset_prompt.run(block=False)

            while not reset_prompt.closed and not handshake_received:
                handshake_received = self.pk_boot.handshake(1)

            reset_prompt.kill()
            reset_prompt.wait()

        self._write("received bootloader handshake message.")
        self._write("Piksi Onboard Bootloader Version: " +
                    self.pk_boot.version)

        self.pk_flash = flash.Flash(self.link, flash_type,
                                    self.pk_boot.sbp_version)

    def stop_flash(self):
        """
    Stop Flash and Bootloader instances (removes callback from SerialLink).
    """
        self.pk_flash.stop()
        self.pk_boot.stop()
Exemplo n.º 12
0
class UpdateView(HasTraits):

  piksi_stm_vers = String('Waiting for Piksi to send settings...')
  newest_stm_vers = String('Downloading Newest Firmware info...')
  piksi_nap_vers = String('Waiting for Piksi to send settings...')
  newest_nap_vers = String('Downloading Newest Firmware info...')
  local_console_vers = String(CONSOLE_VERSION)
  newest_console_vers = String('Downloading Newest Console info...')

  erase_stm = Bool(True)
  erase_en = Bool(True)

  update_firmware = Button(label='Update Piksi Firmware')
  updating = Bool(False)
  update_en = Bool(False)

  download_firmware = Button(label='Download Newest Firmware Files')
  downloading = Bool(False)
  download_fw_en = Bool(True)

  stm_fw = Instance(IntelHexFileDialog)
  nap_fw = Instance(IntelHexFileDialog)

  stream = Instance(OutputStream)

  view = View(
    VGroup(
      HGroup(
        VGroup(
          Item('piksi_stm_vers', label='Piksi STM Firmware Version'),
          Item('newest_stm_vers', label='Newest STM Firmware Version'),
          Item('piksi_nap_vers', label='Piksi NAP Firmware Version'),
          Item('newest_nap_vers', label='Newest NAP Firmware Version'),
          Item('local_console_vers', label='Local Piksi Console Version'),
          Item('newest_console_vers', label='Newest Piksi Console Version'),
        ),
        VGroup(
          Item('stm_fw', style='custom', label='STM Firmware File', \
               enabled_when='download_fw_en'),
          Item('nap_fw', style='custom', label='NAP Firmware File', \
               enabled_when='download_fw_en'),
          Item('erase_stm', label='Erase Entire STM flash', \
               enabled_when='erase_en'),
        ),
      ),
      UItem('download_firmware', enabled_when='download_fw_en'),
      UItem('update_firmware', enabled_when='update_en'),
      Item(
        'stream',
        style='custom',
        editor=InstanceEditor(),
        label='Update Status',
      ),
    )
  )

  def __init__(self, link, prompt=True):
    self.link = link
    self.settings = {}
    self.prompt = prompt
    self.python_console_cmds = {
      'update': self

    }
    self.update_dl = None
    self.stm_fw = IntelHexFileDialog('STM')
    self.stm_fw.on_trait_change(self._manage_enables, 'status')
    self.nap_fw = IntelHexFileDialog('M25')
    self.nap_fw.on_trait_change(self._manage_enables, 'status')
    self.stream = OutputStream()
    self.get_latest_version_info()

  def _manage_enables(self):
    if self.updating == True or self.downloading == True:
      self.update_en = False
      self.download_fw_en = False
    else:
      self.download_fw_en = True
      if self.stm_fw.ihx != None and self.nap_fw.ihx != None:
        self.update_en = True
      else:
        self.update_en = False

    if self.updating == True:
      self.erase_en = False
    else:
      self.erase_en = True

  def _updating_changed(self):
    self._manage_enables()

  def _downloading_changed(self):
    self._manage_enables()

  def _write(self, text):
    self.stream.write(text)
    self.stream.write('\n')
    self.stream.flush()

  def _update_firmware_fired(self):
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates)
    self._firmware_update_thread.start()

  def _download_firmware(self):

    self._write('')

    # Check that we received the index file from the website.
    if self.update_dl == None:
      self._write("Error: Can't download firmware files")
      return

    self.downloading = True

    status = 'Downloading Newest Firmware...'
    self.nap_fw.set_status(status)
    self.stm_fw.set_status(status)
    self._write(status)

    # Get firmware files from Swift Nav's website, save to disk, and load.
    try:
      self._write('Downloading Newest NAP firmware')
      filepath = self.update_dl.download_nap_firmware()
      self._write('Saved file to %s' % filepath)
      self.nap_fw.load_ihx(filepath)
    except AttributeError:
      self.nap_fw.set_status("Error downloading firmware")
      self._write("Error downloading firmware: index file not downloaded yet")
    except KeyError:
      self.nap_fw.set_status("Error downloading firmware")
      self._write("Error downloading firmware: URL not present in index")
    except URLError:
      self.nap_fw.set_status("Error downloading firmware")
      self._write("Error: Failed to download latest NAP firmware from Swift Navigation's website")

    try:
      self._write('Downloading Newest STM firmware')
      filepath = self.update_dl.download_stm_firmware()
      self._write('Saved file to %s' % filepath)
      self.stm_fw.load_ihx(filepath)
    except AttributeError:
      self.stm_fw.set_status("Error downloading firmware")
      self._write("Error downloading firmware: index file not downloaded yet")
    except KeyError:
      self.stm_fw.set_status("Error downloading firmware")
      self._write("Error downloading firmware: URL not present in index")
    except URLError:
      self.stm_fw.set_status("Error downloading firmware")
      self._write("Error: Failed to download latest STM firmware from Swift Navigation's website")

    self.downloading = False

  def _download_firmware_fired(self):
    try:
      if self._download_firmware_thread.is_alive():
        return
    except AttributeError:
      pass

    self._download_firmware_thread = Thread(target=self._download_firmware)
    self._download_firmware_thread.start()

  def compare_versions(self):
    try:
      if self._compare_versions_thread.is_alive():
        return
    except AttributeError:
      pass

    self._compare_versions_thread = Thread(target=self._compare_versions)
    self._compare_versions_thread.start()

  def _compare_versions(self):

    # Check that settings received from Piksi contain FW versions.
    try:
      self.piksi_stm_vers = \
        self.settings['system_info']['firmware_version'].value
      self.piksi_nap_vers = \
        self.settings['system_info']['nap_version'].value
    except KeyError:
      self._write("\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n")
      return

    # Check that we received the index file from the website.
    if self.update_dl == None:
      self._write("Error: No website index to use to compare versions with local firmware")
      return

    # Check if console is out of date and notify user if so.
    if self.prompt:
      local_console_version = parse_version(CONSOLE_VERSION)
      remote_console_version = parse_version(self.newest_console_vers)
      self.console_outdated = remote_console_version > local_console_version

      if self.console_outdated:
        console_outdated_prompt = \
            prompt.CallbackPrompt(
                                  title="Piksi Console Outdated",
                                  actions=[prompt.close_button],
                                 )

        console_outdated_prompt.text = \
            "Your Piksi Console is out of date and may be incompatible\n" + \
            "with current firmware. We highly recommend upgrading to\n" + \
            "ensure proper behavior.\n\n" + \
            "Please visit http://downloads.swiftnav.com to\n" + \
            "download the newest version.\n\n" + \
            "Local Console Version :\n\t" + \
                CONSOLE_VERSION + \
            "\nNewest Console Version :\n\t" + \
                self.update_dl.index['piksi_v2.3.1']['console']['version'] + "\n"

        console_outdated_prompt.run()

    # For timing aesthetics between windows popping up.
    sleep(0.5)

    # Check if firmware is out of date and notify user if so.
    if self.prompt:
      local_stm_version = parse_version(
          self.settings['system_info']['firmware_version'].value)
      remote_stm_version = parse_version(self.newest_stm_vers)

      local_nap_version = parse_version(
          self.settings['system_info']['nap_version'].value)
      remote_nap_version = parse_version(self.newest_nap_vers)

      self.fw_outdated = remote_nap_version > local_nap_version or \
                         remote_stm_version > local_stm_version

      if self.fw_outdated:
        fw_update_prompt = \
            prompt.CallbackPrompt(
                                  title='Firmware Update',
                                  actions=[prompt.close_button]
                                 )

        fw_update_prompt.text = \
            "New Piksi firmware available.\n\n" + \
            "Please use the Firmware Update tab to update.\n\n" + \
            "Newest STM Version :\n\t%s\n\n" % \
                self.update_dl.index['piksi_v2.3.1']['stm_fw']['version'] + \
            "Newest SwiftNAP Version :\n\t%s\n\n" % \
                self.update_dl.index['piksi_v2.3.1']['nap_fw']['version']

        fw_update_prompt.run()

  def get_latest_version_info(self):

    try:
      if self._get_latest_version_info_thread.is_alive():
        return
    except AttributeError:
      pass

    self._get_latest_version_info_thread = Thread(target=self._get_latest_version_info)
    self._get_latest_version_info_thread.start()

  def _get_latest_version_info(self):

    try:
      self.update_dl = UpdateDownloader()
    except URLError:
      self._write("\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n")
      return

    # Make sure index contains all keys we are interested in.
    try:
      self.newest_stm_vers = self.update_dl.index['piksi_v2.3.1']['stm_fw']['version']
      self.newest_nap_vers = self.update_dl.index['piksi_v2.3.1']['nap_fw']['version']
      self.newest_console_vers = self.update_dl.index['piksi_v2.3.1']['console']['version']
    except KeyError:
      self._write("\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n" % INDEX_URL)
      return

  # Executed in GUI thread, called from Handler.
  def manage_firmware_updates(self):
    self.updating = True

    self._write('')

    # Erase STM if so directed.
    if self.erase_stm:
      self._write("Erasing STM flash...")
      self.erase_flash("STM")
      self._write("")

    # Flash STM.
    self._write("Updating STM firmware...")
    self.update_flash(self.stm_fw.ihx, "STM")
    self._write("")

    # Flash NAP.
    self._write("Updating SwiftNAP firmware...")
    self.update_flash(self.nap_fw.ihx, "M25")
    self._write("")

    # Must tell Piksi to jump to application after updating firmware.
    self.link.send_message(ids.BOOTLOADER_JUMP_TO_APP, '\x00')
    self._write("Firmware updates finished.")
    self._write("")

    self.updating = False

  def erase_flash(self, flash_type):

    self.create_flash(flash_type)

    sectors_to_erase = \
        set(range(self.pk_flash.n_sectors)).difference(set(self.pk_flash.restricted_sectors))
    for s in sorted(sectors_to_erase):
      self._write('Erasing %s sector %d' % (self.pk_flash.flash_type,s))
      self.pk_flash.erase_sector(s)

    self.stop_flash()

  def update_flash(self, ihx, flash_type):

    self.create_flash(flash_type)

    self.pk_flash.write_ihx(ihx, self.stream, mod_print = 0x10)

    self.stop_flash()

  def create_flash(self, flash_type):

    # Reset device if the application is running to put into bootloader mode.
    self.link.send_message(ids.RESET, '')

    self.pk_boot = bootload.Bootloader(self.link)

    self._write("Waiting for bootloader handshake message from Piksi ...")
    reset_prompt = None
    handshake_received = self.pk_boot.wait_for_handshake(1)

    # Prompt user to reset Piksi if we don't receive the handshake message
    # within a reasonable amount of tiime (firmware might be corrupted).
    while not handshake_received:
      reset_prompt = \
        prompt.CallbackPrompt(
                              title="Please Reset Piksi",
                              actions=[prompt.close_button],
                             )

      reset_prompt.text = \
            "You must press the reset button on your Piksi in order\n" + \
            "to update your firmware.\n\n" + \
            "Please press it now.\n\n"

      reset_prompt.run(block=False)

      while not reset_prompt.closed and not handshake_received:
        handshake_received = self.pk_boot.wait_for_handshake(1)

      reset_prompt.kill()
      reset_prompt.wait()

    self.pk_boot.reply_handshake()
    self._write("received bootloader handshake message.")
    self._write("Piksi Onboard Bootloader Version: " + self.pk_boot.version)

    self.pk_flash = flash.Flash(self.link, flash_type)

  def stop_flash(self):
    self.pk_flash.stop()
    self.pk_boot.stop()
Exemplo n.º 13
0
    def __init__(self, *args, **kwargs):
        try:
            update = kwargs.pop('update')
        except KeyError:
            update = True

        self.console_output = OutputStream()
        sys.stdout = self.console_output
        sys.stderr = self.console_output

        try:
            self.link = serial_link.SerialLink(*args, **kwargs)
            self.link.add_callback(ids.PRINT, self.print_message_callback)

            self.link.add_callback(ids.DEBUG_VAR, self.debug_var_callback)

            settings_read_finished_functions = []

            self.tracking_view = TrackingView(self.link)
            self.almanac_view = AlmanacView(self.link)
            self.solution_view = SolutionView(self.link)
            self.baseline_view = BaselineView(self.link)
            self.observation_view = ObservationView(self.link,
                                                    name='Rover',
                                                    relay=False)
            self.observation_view_base = ObservationView(self.link,
                                                         name='Base',
                                                         relay=True)
            self.system_monitor_view = SystemMonitorView(self.link)
            self.simulator_view = SimulatorView(self.link)
            self.settings_view = SettingsView(self.link)

            if update:
                self.ocu = OneClickUpdate(self.link, self.console_output)
                settings_read_finished_functions.append(self.ocu.start)

            self.settings_view = \
                SettingsView(self.link, settings_read_finished_functions)

            if update:
                self.ocu.point_to_settings(self.settings_view.settings)

            self.python_console_env = {
                'send_message': self.link.send_message,
                'link': self.link,
            }
            self.python_console_env.update(
                self.tracking_view.python_console_cmds)
            self.python_console_env.update(
                self.almanac_view.python_console_cmds)
            self.python_console_env.update(
                self.solution_view.python_console_cmds)
            self.python_console_env.update(
                self.baseline_view.python_console_cmds)
            self.python_console_env.update(
                self.observation_view.python_console_cmds)
            self.python_console_env.update(
                self.system_monitor_view.python_console_cmds)
        except:
            import traceback
            traceback.print_exc()
Exemplo n.º 14
0
class UpdateView(HasTraits):
  piksi_hw_rev = String('piksi_multi')
  is_v2 = Bool(False)

  piksi_stm_vers = String('Waiting for Piksi to send settings...', width=COLUMN_WIDTH)
  newest_stm_vers = String('Downloading Latest Firmware info...')
  piksi_nap_vers = String('Waiting for Piksi to send settings...')
  newest_nap_vers = String('Downloading Latest Firmware info...')
  local_console_vers = String('v' + CONSOLE_VERSION)
  newest_console_vers = String('Downloading Latest Console info...')

  erase_stm = Bool(True)
  erase_en = Bool(True)

  update_stm_firmware = Button(label='Update FW')
  update_nap_firmware = Button(label='Update NAP')
  update_full_firmware = Button(label='Update Piksi STM and NAP Firmware')

  updating = Bool(False)
  update_stm_en = Bool(False)
  update_nap_en = Bool(False)
  update_en = Bool(False)
  serial_upgrade = Bool(False)
  upgrade_steps = String("Firmware upgrade steps:")

  download_firmware = Button(label='Download Latest Firmware')
  download_directory = Directory("  Please choose a directory for downloaded firmware files...")
  download_stm = Button(label='Download', height=HT)
  download_nap = Button(label='Download', height=HT)
  downloading = Bool(False)
  download_fw_en = Bool(True)

  stm_fw = Instance(FirmwareFileDialog)
  nap_fw = Instance(FirmwareFileDialog)

  stream = Instance(OutputStream)

  view = View(
    VGroup(
      Item('piksi_hw_rev', label='Hardware Revision',
           editor_args={'enabled': False}, resizable=True),
      HGroup(
        VGroup(
          Item('piksi_stm_vers', label='Current', resizable=True, 
                 editor_args={'enabled': False}),
          Item('newest_stm_vers', label='Latest', resizable=True, 
                 editor_args={'enabled': False, 
                              'readonly_allow_selection': True}),
          Item('stm_fw', style='custom', show_label=True, \
               label="Local File", enabled_when='download_fw_en',
               visible_when='serial_upgrade',
               editor_args={'enabled': False}),
          HGroup(Item('update_stm_firmware', show_label=False, \
                     enabled_when='update_stm_en', visible_when='serial_upgrade'),
                Item('erase_stm', label='Erase STM flash\n(recommended)', \
                      enabled_when='erase_en', show_label=True, visible_when='is_v2')),
          show_border=True, label="Firmware Version"
        ),
        VGroup(
          Item('piksi_nap_vers', label='Current', resizable=True, 
               editor_args={'enabled': False}),
          Item('newest_nap_vers', label='Latest', resizable=True,
               editor_args={'enabled': False}),
          Item('nap_fw', style='custom', show_label=True, \
               label="Local File", enabled_when='download_fw_en',
               editor_args={'enabled': False}),
          HGroup(Item('update_nap_firmware', show_label=False, \
                      enabled_when='update_nap_en', visible_when='serial_upgrade'),
                 Item(width=50, label="                  ")),
          show_border=True, label="NAP Version",
          visible_when='is_v2'
          ),
        VGroup(
          Item('local_console_vers', label='Current', resizable=True,
               editor_args={'enabled': False}),
          Item('newest_console_vers', label='Latest',
                editor_args={'enabled': False}),
          label="Swift Console Version", show_border=True),
          ),
      UItem('download_directory', enabled_when='download_fw_en'),
      UItem('download_firmware', enabled_when='download_fw_en'),
      UItem('update_full_firmware', enabled_when='update_en', visible_when='is_v2'),
      VGroup(
        UItem('upgrade_steps', 
              visible_when='not serial_upgrade', style='readonly'),
        Item(
          'stream',
          style='custom',
          editor=InstanceEditor(),
          show_label=False,
        ),
      show_border=True,
      )
    )
  )

  def __init__(self, link, prompt=True, serial_upgrade=False):
    """
    Traits tab with UI for updating Piksi firmware.

    Parameters
    ----------
    link : sbp.client.handler.Handler
      Link for SBP transfer to/from Piksi.
    prompt : bool
      Prompt user to update console/firmware if out of date.
    """
    self.link = link
    self.settings = {}
    self.prompt = prompt
    self.python_console_cmds = {
      'update': self

    }
    try:
      self.update_dl = UpdateDownloader()
    except URLError:
      self.update_dl = None
      pass
    self.erase_en = True
    self.stm_fw = FirmwareFileDialog('bin')
    self.stm_fw.on_trait_change(self._manage_enables, 'status')
    self.nap_fw = FirmwareFileDialog('M25')
    self.nap_fw.on_trait_change(self._manage_enables, 'status')
    self.stream = OutputStream()
    self.serial_upgrade = serial_upgrade
    self.last_call_fw_version = None
    if not self.serial_upgrade:
      self._write(
           "1. Insert the USB flash drive provided with your Piki Multi into "
           "your computer.  Select the flash drive root directory as the "
           "firmware download destination using the \"Please "
           "choose a directory for downloaded firmware files\" directory "
           "chooser above.  Press the \"Download Latest Firmware\" button.  "
           "This will download the latest Piksi Multi firmware file onto the "
           "USB flashdrive.\n"
           "2. Eject the drive from your computer and plug it "
           "into the Piksi Multi evaluation board.\n"
           "3. Reset your Piksi Multi and it will upgrade to the version "
           "on the USB flash drive. This should take less than 5 minutes.\n"
           "4. When the upgrade completes you will be prompted to remove the "
           "USB flash drive and reset your Piksi Multi.\n"
           "5. Verify that the firmware version has upgraded via inspection "
           "of the Current Firmware Version box on the Firmware Update Tab "
           "of the Swift Console.\n")

  def _manage_enables(self):
    """ Manages whether traits widgets are enabled in the UI or not. """
    if self.updating == True or self.downloading == True:
      self.update_stm_en = False
      self.update_nap_en = False
      self.update_en = False
      self.download_fw_en = False
      self.erase_en = False
    else:
      self.download_fw_en = True
      self.erase_en = True
      if self.stm_fw.ihx is not None or self.stm_fw.blob is not None:
        self.update_stm_en = True
      else:
        self.update_stm_en = False
        self.update_en = False
      if self.nap_fw.ihx is not None:
        self.update_nap_en = True
      else:
        self.update_nap_en = False
        self.update_en = False
      if self.nap_fw.ihx is not None and self.stm_fw.ihx is not None:
        self.update_en = True

  def _download_directory_changed(self):
    if self.update_dl:
      self.update_dl.set_root_path(self.download_directory)

  def _updating_changed(self):
    """ Handles self.updating trait being changed. """
    self._manage_enables()

  def _downloading_changed(self):
    """ Handles self.downloading trait being changed. """
    self._manage_enables()

  def _write(self, text):
    """
    Stream style write function. Allows flashing debugging messages to be
    routed to embedded text console.

    Parameters
    ----------
    text : string
      Text to be written to screen.
    """
    self.stream.write(text)
    self.stream.write('\n')
    self.stream.flush()

  def _update_stm_firmware_fired(self):
    """
    Handle update_stm_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates,
                                          args=("STM",))
    self._firmware_update_thread.start()

  def _update_nap_firmware_fired(self):
    """
    Handle update_nap_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates,
                                          args=("M25",))
    self._firmware_update_thread.start()

  def _update_full_firmware_fired(self):
    """
    Handle update_full_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._firmware_update_thread.is_alive():
        return
    except AttributeError:
      pass

    self._firmware_update_thread = Thread(target=self.manage_firmware_updates,
                                          args=("ALL",))
    self._firmware_update_thread.start()

  def _download_firmware(self):
    """ Download latest firmware from swiftnav.com. """
    self._write('')

    # Check that we received the index file from the website.
    if self.update_dl == None:
      self._write("Error: Can't download firmware files")
      return

    self.downloading = True
    status = 'Downloading Latest Firmware...'
    self.nap_fw.clear(status)
    self.stm_fw.clear(status)
    self._write(status)

    # Get firmware files from Swift Nav's website, save to disk, and load.
    if self.update_dl.index[self.piksi_hw_rev].has_key('fw'):
      try:
        self._write('Downloading Latest Multi firmware')
        filepath = self.update_dl.download_multi_firmware(self.piksi_hw_rev)
        self._write('Saved file to %s' % filepath)
        self.stm_fw.load_bin(filepath)
      except AttributeError:
        self.nap_fw.clear("Error downloading firmware")
        self._write("Error downloading firmware: index file not downloaded yet")
      except KeyError:
        self.nap_fw.clear("Error downloading firmware")
        self._write("Error downloading firmware: URL not present in index")
      except URLError:
        self.nap_fw.clear("Error downloading firmware")
        self._write("Error: Failed to download latest NAP firmware from Swift Navigation's website")
      self.downloading = False
      return

    try:
      self._write('Downloading Latest NAP firmware')
      filepath = self.update_dl.download_nap_firmware(self.piksi_hw_rev)
      self._write('Saved file to %s' % filepath)
      self.nap_fw.load_ihx(filepath)
    except AttributeError:
      self.nap_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: index file not downloaded yet")
    except KeyError:
      self.nap_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: URL not present in index")
    except URLError:
      self.nap_fw.clear("Error downloading firmware")
      self._write("Error: Failed to download latest NAP firmware from Swift Navigation's website")

    try:
      self._write('Downloading Latest STM firmware')
      filepath = self.update_dl.download_stm_firmware(self.piksi_hw_rev)
      self._write('Saved file to %s' % filepath)
      self.stm_fw.load_ihx(filepath)
    except AttributeError:
      self.stm_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: index file not downloaded yet")
    except KeyError:
      self.stm_fw.clear("Error downloading firmware")
      self._write("Error downloading firmware: URL not present in index")
    except URLError:
      self.stm_fw.clear("Error downloading firmware")
      self._write("Error: Failed to download latest STM firmware from Swift Navigation's website")

    self.downloading = False

  def _download_firmware_fired(self):
    """
    Handle download_firmware button. Starts thread so as not to block the GUI
    thread.
    """
    try:
      if self._download_firmware_thread.is_alive():
        return
    except AttributeError:
      pass

    self._download_firmware_thread = Thread(target=self._download_firmware)
    self._download_firmware_thread.start()

  def compare_versions(self):
    """
    To be called after latest Piksi firmware info has been received from
    device, to decide if current firmware on Piksi is out of date. Also informs
    user if the firmware was successfully upgraded. Starts a thread so as not
    to block GUI thread.
    """
    try:
      if self._compare_versions_thread.is_alive():
        return
    except AttributeError:
      pass

    self._compare_versions_thread = Thread(target=self._compare_versions)
    self._compare_versions_thread.start()

  def _compare_versions(self):
    """
    Compares version info between received firmware version / current console
    and firmware / console info from website to decide if current firmware or
    console is out of date. Prompt user to update if so. Informs user if
    firmware successfully upgraded.
    """
    # Check that settings received from Piksi contain FW versions.
    try:
      self.piksi_hw_rev = \
        HW_REV_LOOKUP[self.settings['system_info']['hw_revision'].value]
      self.piksi_stm_vers = \
        self.settings['system_info']['firmware_version'].value
    except KeyError:
      self._write("\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n")
      return

    self.is_v2 = self.piksi_hw_rev.startswith('piksi_v2')
    if self.is_v2:
      self.stm_fw.set_flash_type('STM')
      self.serial_upgrade = True
    else:
      self.stm_fw.set_flash_type('bin')

    self._get_latest_version_info()

    # Check that we received the index file from the website.
    if self.update_dl == None:
      self._write("Error: No website index to use to compare versions with local firmware")
      return

    # Check if console is out of date and notify user if so.
    if self.prompt:
      local_console_version = parse_version(CONSOLE_VERSION)
      remote_console_version = parse_version(self.newest_console_vers)
      self.console_outdated = remote_console_version > local_console_version

      # we want to warn users using v2 regardless of version logic
      if self.console_outdated or self.is_v2:
        if not self.is_v2:
          console_outdated_prompt = \
          prompt.CallbackPrompt(
                                title="Swift Console Outdated",
                                actions=[prompt.close_button],
                                )
          console_outdated_prompt.text = \
              "Your console is out of date and may be incompatible\n" + \
              "with current firmware. We highly recommend upgrading to\n" + \
              "ensure proper behavior.\n\n" + \
              "Please visit http://support.swiftnav.com to\n" + \
              "download the latest version.\n\n" + \
              "Local Console Version :\n\t" + \
                  "v" + CONSOLE_VERSION + \
              "\nLatest Console Version :\n\t" + \
                  self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"
        else:
          console_outdated_prompt = \
          prompt.CallbackPrompt(
                                title="Swift Console Incompatible",
                                actions=[prompt.close_button],
                                )
          console_outdated_prompt.text = \
              "Your console is incompatible with your hardware revision.\n" + \
              "We highly recommend using a compatible console version\n" + \
              "to ensure proper behavior.\n\n" + \
              "Please visit http://support.swiftnav.com to\n" + \
              "download the latest compatible version.\n\n" + \
              "Current Hardware revision :\n\t" + \
                   self.piksi_hw_rev + \
              "\nLast supported Console Version: \n\t" + \
                  self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"

        console_outdated_prompt.run()

      # For timing aesthetics between windows popping up.
      sleep(0.5)

      # Check if firmware is out of date and notify user if so.
      local_stm_version = self.settings['system_info']['firmware_version'].value
      remote_stm_version = self.newest_stm_vers

      self.fw_outdated = remote_stm_version > local_stm_version

      if self.fw_outdated:
        fw_update_prompt = \
            prompt.CallbackPrompt(
                                  title='Firmware Update',
                                  actions=[prompt.close_button]
                                 )

        if self.update_dl.index[self.piksi_hw_rev].has_key('fw'):
          fw_update_prompt.text = \
            "New Piksi firmware available.\n\n" + \
            "Please use the Firmware Update tab to update.\n\n" + \
            "Newest Firmware Version :\n\t%s\n\n" % \
                self.update_dl.index[self.piksi_hw_rev]['fw']['version']
        else:
          fw_update_prompt.text = \
            "New Piksi firmware available.\n\n" + \
            "Please use the Firmware Update tab to update.\n\n" + \
            "Newest STM Version :\n\t%s\n\n" % \
                self.update_dl.index[self.piksi_hw_rev]['stm_fw']['version'] + \
            "Newest SwiftNAP Version :\n\t%s\n\n" % \
                self.update_dl.index[self.piksi_hw_rev]['nap_fw']['version']

        fw_update_prompt.run()

    # Check if firmware successfully upgraded and notify user if so.
    if self.last_call_fw_version is not None and \
        self.last_call_fw_version != local_stm_version:
      fw_success_str = "Firmware successfully upgraded from %s to %s." % \
                       (self.last_call_fw_version, local_stm_version)
      print fw_success_str
      self._write(fw_success_str)

    # Record firmware version reported each time this callback is called.
    self.last_call_fw_version = local_stm_version

  def _get_latest_version_info(self):
    """ Get latest firmware / console version from website. """
    try:
      self.update_dl = UpdateDownloader()
    except URLError:
      self._write("\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n")
      return

    # Make sure index contains all keys we are interested in.
    try:
      if self.update_dl.index[self.piksi_hw_rev].has_key('fw'):
        self.newest_stm_vers = self.update_dl.index[self.piksi_hw_rev]['fw']['version']
      else:
        self.newest_stm_vers = self.update_dl.index[self.piksi_hw_rev]['stm_fw']['version']
        self.newest_nap_vers = self.update_dl.index[self.piksi_hw_rev]['nap_fw']['version']
      self.newest_console_vers = self.update_dl.index[self.piksi_hw_rev]['console']['version']
    except KeyError:
      self._write("\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n" % INDEX_URL)
      return

  def manage_stm_firmware_update(self):
    # Erase all of STM's flash (other than bootloader) if box is checked.
    if self.erase_stm:
      text = "Erasing STM"
      self._write(text)
      self.create_flash("STM")
      sectors_to_erase = set(range(self.pk_flash.n_sectors)).difference(set(self.pk_flash.restricted_sectors))
      progress_dialog = PulsableProgressDialog(len(sectors_to_erase), False)
      progress_dialog.title = text
      GUI.invoke_later(progress_dialog.open)
      erase_count = 0
      for s in sorted(sectors_to_erase):
        progress_dialog.progress(erase_count)
        self._write('Erasing %s sector %d' % (self.pk_flash.flash_type,s))
        self.pk_flash.erase_sector(s)
        erase_count += 1
      self.stop_flash()
      self._write("")
      try:
        progress_dialog.close()
      except AttributeError:
        pass
    # Flash STM.
    text = "Updating STM"
    self._write(text)
    self.create_flash("STM")
    stm_n_ops = self.pk_flash.ihx_n_ops(self.stm_fw.ihx, \
                                        erase = not self.erase_stm)
    progress_dialog = PulsableProgressDialog(stm_n_ops, True)
    progress_dialog.title = text
    GUI.invoke_later(progress_dialog.open)
    # Don't erase sectors if we've already done so above.
    self.pk_flash.write_ihx(self.stm_fw.ihx, self.stream, mod_print=0x40, \
                            elapsed_ops_cb = progress_dialog.progress, \
                            erase = not self.erase_stm)
    self.stop_flash()
    self._write("")
    try:
      progress_dialog.close()
    except AttributeError:
      pass

  def manage_nap_firmware_update(self, check_version=False):
    # Flash NAP if out of date.
    try:
      local_nap_version = parse_version(
          self.settings['system_info']['nap_version'].value)
      remote_nap_version = parse_version(self.newest_nap_vers)
      nap_out_of_date = local_nap_version != remote_nap_version
    except KeyError:
      nap_out_of_date = True
    if nap_out_of_date or check_version==False:
      text = "Updating NAP"
      self._write(text)
      self.create_flash("M25")
      nap_n_ops = self.pk_flash.ihx_n_ops(self.nap_fw.ihx)
      progress_dialog = PulsableProgressDialog(nap_n_ops, True)
      progress_dialog.title = text
      GUI.invoke_later(progress_dialog.open)
      self.pk_flash.write_ihx(self.nap_fw.ihx, self.stream, mod_print=0x40, \
                              elapsed_ops_cb = progress_dialog.progress)
      self.stop_flash()
      self._write("")
      try:
        progress_dialog.close()
      except AttributeError:
        pass
      return True
    else:
      text = "NAP is already to latest version, not updating!"
      self._write(text)
      self._write("")
      return False

  def manage_multi_firmware_update(self):
    # Set up progress dialog and transfer file to Piksi using SBP FileIO
    progress_dialog = PulsableProgressDialog(len(self.stm_fw.blob))
    progress_dialog.title = "Transferring image file"
    GUI.invoke_later(progress_dialog.open)
    self._write("Transferring image file...")
    try:
      FileIO(self.link).write("upgrade.image_set.bin", self.stm_fw.blob,
                              progress_cb=progress_dialog.progress)
    except Exception as e:
      self._write("Failed to transfer image file to Piksi: %s\n" % e)
      progress_dialog.close()
      return
    try:
      progress_dialog.close()
    except AttributeError:
      pass

    # Setup up pulsed progress dialog and commit to flash
    progress_dialog = PulsableProgressDialog(100, True)
    progress_dialog.title = "Committing to flash"
    GUI.invoke_later(progress_dialog.open)
    self._write("Committing file to flash...")
    def log_cb(msg, **kwargs): self._write(msg.text)
    self.link.add_callback(log_cb, SBP_MSG_LOG)
    code = shell_command(self.link, "upgrade_tool upgrade.image_set.bin", 600,
                         progress_cb=progress_dialog.progress)
    self.link.remove_callback(log_cb, SBP_MSG_LOG)
    progress_dialog.close()

    if code != 0:
      self._write('Failed to perform upgrade (code = %d)' % code)
      if code == -255:
        self._write('Shell command timed out.  Please try again.')
      return
    self._write('Resetting Piksi...')
    self.link(MsgReset(flags=0))

  # Executed in GUI thread, called from Handler.
  def manage_firmware_updates(self, device):
    """
    Update Piksi firmware. Erase entire STM flash (other than bootloader)
    if so directed. Flash NAP only if new firmware is available.
    """
    self.updating = True
    update_nap = False
    self._write('')
    if not self.is_v2:
      self.manage_multi_firmware_update()
      self.updating = False
      return
    elif device == "STM":
      self.manage_stm_firmware_update()
    elif device == "M25":
      update_nap = self.manage_nap_firmware_update()
    else:
      self.manage_stm_firmware_update()
      update_nap = self.manage_nap_firmware_update(check_version=True)

    # Must tell Piksi to jump to application after updating firmware.
    if device == "STM" or update_nap:
        self.link(MsgBootloaderJumpToApp(jump=0))
        self._write("Firmware update finished.")
        self._write("")

    self.updating = False

  def create_flash(self, flash_type):
    """
    Create flash.Flash instance and set Piksi into bootloader mode, prompting
    user to reset if necessary.

    Parameter
    ---------
    flash_type : string
      Either "STM" or "M25".
    """
    # Reset device if the application is running to put into bootloader mode.
    self.link(MsgReset(flags=0))

    self.pk_boot = bootload.Bootloader(self.link)

    self._write("Waiting for bootloader handshake message from Piksi ...")
    reset_prompt = None
    handshake_received = self.pk_boot.handshake(1)

    # Prompt user to reset Piksi if we don't receive the handshake message
    # within a reasonable amount of tiime (firmware might be corrupted).
    while not handshake_received:
      reset_prompt = \
        prompt.CallbackPrompt(
                              title="Please Reset Piksi",
                              actions=[prompt.close_button],
                             )

      reset_prompt.text = \
            "You must press the reset button on your Piksi in order\n" + \
            "to update your firmware.\n\n" + \
            "Please press it now.\n\n"

      reset_prompt.run(block=False)

      while not reset_prompt.closed and not handshake_received:
        handshake_received = self.pk_boot.handshake(1)

      reset_prompt.kill()
      reset_prompt.wait()

    self._write("received bootloader handshake message.")
    self._write("Piksi Onboard Bootloader Version: " + self.pk_boot.version)

    self.pk_flash = flash.Flash(self.link, flash_type, self.pk_boot.sbp_version)

  def stop_flash(self):
    """
    Stop Flash and Bootloader instances (removes callback from SerialLink).
    """
    self.pk_flash.stop()
    self.pk_boot.stop()