Ejemplo n.º 1
0
    def __init__(self, connmgr):
        QObject.__init__(self)

        self.serialdev = serial.Serial(None, 115200, timeout=1.0)
        self.pjcboot = PJCBootloader(self.serialdev)
        self.pjcapp = PJCApplication(self.serialdev)

        # set up connections
        connmgr.addSignal(self.devicestarted, 'DeviceStarted')
        connmgr.addSignal(self.serialclosed, 'SerialClosed')
        connmgr.addSignal(self.serialenumerated, 'SerialEnumerated')
        connmgr.addSignal(self.newlogmessage, 'WriteToLog')
        connmgr.addSlot(self.enumerateSerialPorts, 'EnumerateSerial')
        connmgr.addSlot(self.openSerialPort, 'OpenSerial')

        connmgr.addSignal(self.updateprogressed, 'UpdateProgressed')
        connmgr.addSignal(self.updatecompleted, 'UpdateCompleted')
        connmgr.addSlot(self.doFirmwareUpdate, 'StartUpdate')

        connmgr.addSignal(self.lampstateupdated, 'LampEnableChanged')
        connmgr.addSignal(self.targettempupdated, 'TargetTempChanged')
        connmgr.addSignal(self.overtempupdated, 'OvertempChanged')
        connmgr.addSignal(self.fanoffupdated, 'FanOffTempChanged')
        connmgr.addSignal(self.lampdelayupdated, 'LampOffDelayChanged')
        connmgr.addSignal(self.mindutycycleupdated, 'MinDutyCycleChanged')
        connmgr.addSlot(self.setLampState, 'SetLampEnable')
        connmgr.addSlot(self.setTargetTemp, 'SetTargetTemp')
        connmgr.addSlot(self.setOvertempLimit, 'SetOvertemp')
        connmgr.addSlot(self.setFanOffTemp, 'SetFanOffTemp')
        connmgr.addSlot(self.setLampOffDelay, 'SetLampOffDelay')
        connmgr.addSlot(self.setMinDutyCycle, 'SetMinDutyCycle')
        connmgr.addSlot(self.refreshAppSettings, 'RefreshSettings')
        connmgr.addSlot(self.saveAppSettings, 'SaveSettings')

        connmgr.addSignal(self.monitorrefreshed, 'MonitorRefreshed')
        connmgr.addSlot(self.refreshMonitorData, 'RefreshMonitor')
        connmgr.addSlot(self.printBootStatus, 'PrintBootStatus')
Ejemplo n.º 2
0
class SerialComm(QObject):
    """Class for serial communications.

    This class communicates with the board over a serial connection.  Commands are retrieved via QT
    slots and the results are transmitted using QT signals.
    """

    # new signals have to be declared out here, something the docs aren't very explicit about
    devicestarted = QtCore.Signal(bool)
    serialclosed = QtCore.Signal()
    serialenumerated = QtCore.Signal(list)
    updateprogressed = QtCore.Signal(int)
    updatecompleted = QtCore.Signal(bool)
    newlogmessage = QtCore.Signal(str)      # used to print messages to the UI
    lampstateupdated = QtCore.Signal(bool)
    targettempupdated = QtCore.Signal(float)
    overtempupdated = QtCore.Signal(float)
    fanoffupdated = QtCore.Signal(float)
    lampdelayupdated = QtCore.Signal(float)
    mindutycycleupdated = QtCore.Signal(float)
    monitorrefreshed = QtCore.Signal(tuple)

    def __init__(self, connmgr):
        QObject.__init__(self)

        self.serialdev = serial.Serial(None, 115200, timeout=1.0)
        self.pjcboot = PJCBootloader(self.serialdev)
        self.pjcapp = PJCApplication(self.serialdev)

        # set up connections
        connmgr.addSignal(self.devicestarted, 'DeviceStarted')
        connmgr.addSignal(self.serialclosed, 'SerialClosed')
        connmgr.addSignal(self.serialenumerated, 'SerialEnumerated')
        connmgr.addSignal(self.newlogmessage, 'WriteToLog')
        connmgr.addSlot(self.enumerateSerialPorts, 'EnumerateSerial')
        connmgr.addSlot(self.openSerialPort, 'OpenSerial')

        connmgr.addSignal(self.updateprogressed, 'UpdateProgressed')
        connmgr.addSignal(self.updatecompleted, 'UpdateCompleted')
        connmgr.addSlot(self.doFirmwareUpdate, 'StartUpdate')

        connmgr.addSignal(self.lampstateupdated, 'LampEnableChanged')
        connmgr.addSignal(self.targettempupdated, 'TargetTempChanged')
        connmgr.addSignal(self.overtempupdated, 'OvertempChanged')
        connmgr.addSignal(self.fanoffupdated, 'FanOffTempChanged')
        connmgr.addSignal(self.lampdelayupdated, 'LampOffDelayChanged')
        connmgr.addSignal(self.mindutycycleupdated, 'MinDutyCycleChanged')
        connmgr.addSlot(self.setLampState, 'SetLampEnable')
        connmgr.addSlot(self.setTargetTemp, 'SetTargetTemp')
        connmgr.addSlot(self.setOvertempLimit, 'SetOvertemp')
        connmgr.addSlot(self.setFanOffTemp, 'SetFanOffTemp')
        connmgr.addSlot(self.setLampOffDelay, 'SetLampOffDelay')
        connmgr.addSlot(self.setMinDutyCycle, 'SetMinDutyCycle')
        connmgr.addSlot(self.refreshAppSettings, 'RefreshSettings')
        connmgr.addSlot(self.saveAppSettings, 'SaveSettings')

        connmgr.addSignal(self.monitorrefreshed, 'MonitorRefreshed')
        connmgr.addSlot(self.refreshMonitorData, 'RefreshMonitor')
        connmgr.addSlot(self.printBootStatus, 'PrintBootStatus')

    def __del__(self):
        if self.serialdev.isOpen():
            self.serialdev.close()

    def _print(self, text):
        self.newlogmessage.emit(text)

    def _handlesPJCExceptions(func):
        """A decorator allowing the decorated functions to catch and handle exceptions thrown from
        the PJCInterface classes in a consistent manner.
        """
        def wrapper(self, *args, **kwargs):
            try:
                func(self, *args, **kwargs)
            except pjcexcept.NotRespondingError:
                self._print('The device is not responding; closing serial port.')
                self.serialdev.close()
                self.serialclosed.emit()
            except pjcexcept.UnknownCommandError:
                self._print('The device did not recognize an issued command.')
            except pjcexcept.UnexpectedResponseError:
                self._print('The device responded incorrectly to a command; closing serial port.')
                self.serialdev.close()
                self.serialclosed.emit()
            except pjcexcept.DeviceRestartError:
                self._print('The device restarted unexpectedly.')
                self.devicestarted.emit(self.pjcboot.isApplication())
            except (pjcexcept.SerialPortNotOpenError, serial.portNotOpenError):
                self._print('No serial port is open.')

        return wrapper

    @QtCore.Slot()
    @_handlesPJCExceptions
    def enumerateSerialPorts(self):
        fallback = True

        if comports:
            try:
                ports = [name for name, unused_desc, unused_hwid in sorted(comports())]
                fallback = False
            except:
                fallback = True

        if fallback:
            # PySerial 2.6 has a bug in which comports() throws an exception with USB devices, so
            # handle that and the case of having an older version.  This should always work, but
            # will miss some ports (like USB ones).
            ports = []

            for i in range(256):
                try:
                    s = serial.Serial(i)
                    ports.append(s.name)
                    s.close()
                except serial.SerialException:
                    pass

        self.serialenumerated.emit(ports)

    @QtCore.Slot(str)
    @_handlesPJCExceptions
    def openSerialPort(self, serialpath):
        try:
            if serialpath != self.serialdev.port  or  not self.serialdev.isOpen():
                if self.serialdev.isOpen():
                    self.serialdev.close()
                self.serialdev.port = serialpath
                self.serialdev.open()

                version = self.pjcboot.getVersion()
                if self.pjcboot.isApplication():
                    self.devicestarted.emit(True)
                    self._print('Application version ' + str(version) + '.')
                else:
                    self.devicestarted.emit(False)
                    self._print('Bootloader vesrion ' + str(version) + '.')
        except serial.SerialException:
            self._print('Could not open serial port at ' + serialpath + '.')

    @QtCore.Slot(str)
    @_handlesPJCExceptions
    def doFirmwareUpdate(self, hexfile):
        result = True

        if self.pjcboot.isApplication():
            if self.pjcboot.doJump():
                self._print('Failed to jump to bootloader.')
                result = False
            else:
                self.devicestarted.emit(False)

        flashmem = FlashImage(self.pjcboot.getMaxPages(), self.pjcboot.getPageSize())

        if result:
            if flashmem.buildImageFromFile(hexfile):
                self._print('Erasing old app...')
                self.pjcboot.eraseApp()

                self._print('Loading new app...')

                for i in range(flashmem.getUsedAppPages()):
                    if self.pjcboot.loadPageData(flashmem.getSinglePage(i)):
                        if self.pjcboot.programPage(i):
                            self.updateprogressed.emit(i * 100 // flashmem.getUsedAppPages())
                        else:
                            self._print('Failed to program page ' + str(i) + '.')
                            result = False
                            break;
                    else:
                        result = False
                        self._print('Failed to load page data ' + str(i) + '.')
                        break;

                if result:
                    self.pjcboot.writeCRC()
                    if self.pjcboot.doJump():
                        self.devicestarted.emit(True)
                    self._print('Update complete!')
            else:
                self._print('File parse failed.')

        self.updatecompleted.emit(result)

    @QtCore.Slot(bool)
    @_handlesPJCExceptions
    def setLampState(self, state):
        self.lampstateupdated.emit(self.pjcapp.enableLamp(state))

    @QtCore.Slot(float)
    @_handlesPJCExceptions
    def setTargetTemp(self, temp):
        self.targettempupdated.emit(self.pjcapp.setTargetTemperature(temp))

    @QtCore.Slot(float)
    @_handlesPJCExceptions
    def setOvertempLimit(self, temp):
        self.overtempupdated.emit(self.pjcapp.setOvertempLimit(temp))

    @QtCore.Slot(float)
    @_handlesPJCExceptions
    def setFanOffTemp(self, temp):
        self.fanoffupdated.emit(self.pjcapp.setFanOffPoint(temp))

    @QtCore.Slot(float)
    @_handlesPJCExceptions
    def setLampOffDelay(self, delay):
        self.lampdelayupdated.emit(self.pjcapp.setLampOffDelay(delay))

    @QtCore.Slot(float)
    @_handlesPJCExceptions
    def setMinDutyCycle(self, mindc):
        self.mindutycycleupdated.emit(self.pjcapp.setMinDutyCycle(mindc))

    @QtCore.Slot()
    @_handlesPJCExceptions
    def refreshAppSettings(self):
        self.lampstateupdated.emit(self.pjcapp.isLampEnabled())
        self.targettempupdated.emit(self.pjcapp.getTargetTemperature())
        self.overtempupdated.emit(self.pjcapp.getOvertempLimit())
        self.fanoffupdated.emit(self.pjcapp.getFanOffPoint())
        self.lampdelayupdated.emit(self.pjcapp.getLampOffDelay())
        self.mindutycycleupdated.emit(self.pjcapp.getMinDutyCycle())

    @QtCore.Slot()
    @_handlesPJCExceptions
    def saveAppSettings(self):
        if self.pjcapp.saveSettingsToEEPROM():
            self._print('Settings saved.')
        else:
            self._print('Could not save settings.')

    @QtCore.Slot()
    @_handlesPJCExceptions
    def refreshMonitorData(self):
        self.monitorrefreshed.emit((self.pjcapp.readADCs(), 
                                    self.pjcapp.getCurrentDutyCycle(), 
                                    self.pjcapp.isLampEnabled(), 
                                    self.pjcapp.getMostRecentError(True)))

    @QtCore.Slot()
    @_handlesPJCExceptions
    def printBootStatus(self):
        status = self.pjcboot.getBootStatus()

        if status == PJCBootloader.BootStatusOK:
            self._print('The application is running OK.')
        elif status == PJCBootloader.BootStatusPinSet:
            self._print('The bootloader startup jumper is set.')
        elif status == PJCBootloader.BootStatusRestart:
            self._print('The application jumped to the bootloader.')
        elif status == PJCBootloader.BootStatusNoApp:
            self._print('The device does not have an application.')
        elif status == PJCBootloader.BootStatusBadCRC:
            self._print('The application on board is corrupt.')
        else:
            self._print('The device is in the bootloader for an unknown reason.')