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 __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 __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 __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()
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 __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()
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 __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)
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()
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()
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()
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()
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()
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()