From 8b9a45d4eef4e441d3e73aa2997738e08eda5d6c Mon Sep 17 00:00:00 2001 From: Scott Petersen Date: Thu, 8 Oct 2015 14:54:24 -0700 Subject: [PATCH] More tweaks and error handling for the firmware upload. --- alarmdecoder/devices.py | 26 ++++++++++++++++++++ alarmdecoder/util.py | 54 ++++++++++++++++++++++++++++++++++------- bin/ad2-firmwareupload | 13 +++++++--- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/alarmdecoder/devices.py b/alarmdecoder/devices.py index e3acd8b..6b36b60 100644 --- a/alarmdecoder/devices.py +++ b/alarmdecoder/devices.py @@ -559,6 +559,12 @@ class USBDevice(Device): return ret + def purge(self): + """ + Purges read/write buffers. + """ + self._device.purge_buffers() + def _get_serial_number(self): """ Retrieves the FTDI device serial number. @@ -849,6 +855,13 @@ class SerialDevice(Device): return ret + def purge(self): + """ + Purges read/write buffers. + """ + self._device.flushInput() + self._device.flushOutput() + class SocketDevice(Device): """ @@ -1144,6 +1157,19 @@ class SocketDevice(Device): return ret + def purge(self): + """ + Purges read/write buffers. + """ + try: + self._device.setblocking(0) + while(self._device.recv(1)): + pass + except socket.error, err: + pass + finally: + self._device.setblocking(1) + def _init_ssl(self): """ Initializes our device as an SSL connection. diff --git a/alarmdecoder/util.py b/alarmdecoder/util.py index 66b61e0..27b90f2 100644 --- a/alarmdecoder/util.py +++ b/alarmdecoder/util.py @@ -38,6 +38,19 @@ class InvalidMessageError(Exception): pass +class UploadError(Exception): + """ + Generic firmware upload error. + """ + pass + +class UploadChecksumError(UploadError): + """ + The firmware upload failed due to a checksum error. + """ + pass + + class Firmware(object): """ Represents firmware for the `AlarmDecoder`_ devices. @@ -50,10 +63,12 @@ class Firmware(object): STAGE_LOAD = 3 STAGE_UPLOADING = 4 STAGE_DONE = 5 + STAGE_ERROR = 98 + STAGE_DEBUG = 99 # FIXME: Rewrite this monstrosity. @staticmethod - def upload(dev, filename, progress_callback=None): + def upload(dev, filename, progress_callback=None, debug=False): """ Uploads firmware to an `AlarmDecoder`_ device. @@ -70,15 +85,29 @@ class Firmware(object): Perform the actual firmware upload to the device. """ with open(filename) as upload_file: + line_cnt = 0 for line in upload_file: + line_cnt += 1 line = line.rstrip() if line[0] == ':': dev.write(line + "\r") - dev.read_line(timeout=5.0, purge_buffer=True) + response = dev.read_line(timeout=5.0, purge_buffer=True) + if debug: + stage_callback(Firmware.STAGE_DEBUG, data="line={0} - line={1} response={2}".format(line_cnt, line, response)); + + if '!ce' in response: + raise UploadChecksumError("Checksum error on line " + str(line_cnt) + " of " + filename); - if progress_callback is not None: - progress_callback(Firmware.STAGE_UPLOADING) + elif '!no' in response: + raise UploadError("Incorrect data sent to bootloader.") + + elif '!ok' in response: + break + + else: + if progress_callback is not None: + progress_callback(Firmware.STAGE_UPLOADING) time.sleep(0.0) @@ -100,6 +129,8 @@ class Firmware(object): position = 0 + dev.purge() + while timeout_event.reading: try: char = dev.read() @@ -112,7 +143,7 @@ class Firmware(object): else: position = 0 - except Exception: + except Exception, err: pass if timer: @@ -121,10 +152,10 @@ class Firmware(object): else: raise TimeoutError('Timeout while waiting for line terminator.') - def stage_callback(stage): + def stage_callback(stage, **kwargs): """Callback to update progress for the specified stage.""" if progress_callback is not None: - progress_callback(stage) + progress_callback(stage, **kwargs) if dev is None: raise NoDeviceError('No device specified for firmware upload.') @@ -152,11 +183,16 @@ class Firmware(object): stage_callback(Firmware.STAGE_LOAD) dev.write("=") read_until('!load', timeout=15.0) + except TimeoutError, err: retry -= 1 else: retry = 0 # And finally do the upload. - do_upload() - stage_callback(Firmware.STAGE_DONE) + try: + do_upload() + except UploadError, err: + stage_callback(Firmware.STAGE_ERROR, error=err) + else: + stage_callback(Firmware.STAGE_DONE) diff --git a/bin/ad2-firmwareupload b/bin/ad2-firmwareupload index 8223f91..e68e44d 100755 --- a/bin/ad2-firmwareupload +++ b/bin/ad2-firmwareupload @@ -1,9 +1,10 @@ #!/usr/bin/env python +import os import sys, time import alarmdecoder -def handle_firmware(stage): +def handle_firmware(stage, **kwargs): if stage == alarmdecoder.util.Firmware.STAGE_START: handle_firmware.wait_tick = 0 handle_firmware.upload_tick = 0 @@ -30,6 +31,10 @@ def handle_firmware(stage): sys.stdout.flush() elif stage == alarmdecoder.util.Firmware.STAGE_DONE: print "\r\nDone!" + elif stage == alarmdecoder.util.Firmware.STAGE_ERROR: + print "\r\nError: {0}".format(kwargs.get("error", "")) + elif stage == alarmdecoder.util.Firmware.STAGE_DEBUG: + print "\r\nDBG: {0}".format(kwargs.get("data", "")) def main(): device = '/dev/ttyUSB0' @@ -47,18 +52,20 @@ def main(): if len(sys.argv) > 3: baudrate = sys.argv[3] + debug = os.environ.get("ALARMDECODER_DEBUG") is not None + print "Flashing device: {0} - {2} baud\r\nFirmware: {1}".format(device, firmware, baudrate) if ':' in device: hostname, port = device.split(':') dev = alarmdecoder.devices.SocketDevice(interface=(hostname, int(port))) - dev.open() + dev.open(no_reader_thread=True) else: dev = alarmdecoder.devices.SerialDevice(interface=device) dev.open(baudrate=baudrate, no_reader_thread=True) time.sleep(3) - alarmdecoder.util.Firmware.upload(dev, firmware, handle_firmware) + alarmdecoder.util.Firmware.upload(dev, firmware, handle_firmware, debug=debug) dev.close()