Beispiel #1
0
class QSerialDevice(QSerialPort):
    '''
    Abstraction of an instrument connected to a serial port

    ...

    Attributes
    ----------
    eol : str, optional
        End-of-line character.
        Default: '\r' (carriage return)
    manufacturer : str, optional
        Identifier for the serial interface manufacturer.
        Default: 'Prolific'
    baudrate : int, optional
        Baud rate for serial communication.
        Default: 9600
    parity : int, optional
        One of the constants defined in the serial package
    stopbits : int, optional
        One of the constants defined in the serial package
    timeout : float
        Read timeout period [s].
        Default: 0.1

    Methods
    -------
    find() : bool
        Find the serial device that satisfies identify().
        Returns True if the device is found and correctly opened.
    identify() : bool
        Returns True if the device on the opened port correctly
        identifies itself.  Subclasses must override this method.
    send(cmd)
        Write cmd to serial device with eol termination.
        Response is handled by call to process().
    handshake(cmd) : str
        Write cmd to serial device and return response.

    Signals
    -------
    dataReady(data)
        Emitted when eol-terminated data is returned by the device.
    '''

    dataReady = pyqtSignal(str)

    def __init__(self,
                 parent=None,
                 port=None,
                 eol='\r',
                 manufacturer='Prolific',
                 baudrate=QSerialPort.Baud9600,
                 databits=QSerialPort.Data8,
                 parity=QSerialPort.NoParity,
                 stopbits=QSerialPort.OneStop,
                 timeout=1000,
                 **kwargs):
        super(QSerialDevice, self).__init__(parent=parent, **kwargs)
        self.eol = eol
        self.manufacturer = manufacturer
        self.baudrate = baudrate
        self.databits = databits
        self.parity = parity
        self.stopbits = stopbits
        self.timeout = timeout
        self.readyRead.connect(self.receive)
        self.buffer = QByteArray()
        if port is None:
            self.find()
        else:
            self.setup(port)
        if not self.isOpen():
            raise ValueError('Could not find serial device')

    def setup(self, portinfo):
        if portinfo is None:
            logger.info('No serial port specified')
            return False
        name = portinfo.systemLocation()
        logger.debug(' Setting up {}'.format(name))
        if portinfo.isBusy():
            logger.debug(' Port is busy: {}'.format(name))
            return False
        self.setPort(portinfo)
        self.setBaudRate(self.baudrate)
        self.setDataBits(self.databits)
        self.setParity(self.parity)
        self.setStopBits(self.stopbits)
        if not self.open(QSerialPort.ReadWrite):
            logger.debug(' Could not open port: {}'.format(name))
            return False
        if self.bytesAvailable():
            tmp = self.readAll()
            logger.info(' Cleared bytes from device: {}'.format(tmp))
        if self.identify():
            logger.info(' Device found at {}'.format(name))
            return True
        self.close()
        logger.debug(' Device not connected to {}'.format(name))
        return False

    def find(self):
        '''
        Attempt to identify and open the serial port

        Returns
        -------
        find : bool
            True if port identified and successfully opened.
        '''
        ports = QSerialPortInfo.availablePorts()
        if len(ports) < 1:
            logger.warning(' No serial ports detected')
            return
        for port in ports:
            portinfo = QSerialPortInfo(port)
            if self.setup(portinfo):
                break

    def identify(self):
        '''
        Identify this device

        Subclasses must override this method

        Returns
        -------
        identify : bool
            True if attached device correctly identifies itself.
        '''
        return True

    def send(self, data):
        '''
        Write string to serial device with eol termination

        Parameters
        ----------
        str : string
            String to be transferred
        '''
        cmd = data + self.eol
        self.write(cmd.encode())
        self.flush()
        logger.debug(' Data sent: {}'.format(data))

    @pyqtSlot()
    def receive(self):
        '''
        Slot for readyRead signal

        Appends data received from device to a buffer
        until eol character is received, then processes
        the contents of the buffer.
        '''
        logger.debug(' Data received')
        self.buffer.append(self.readAll())
        if self.buffer.contains(self.eol):
            logger.debug(' EOL character received')
            data = self.buffer.trimmed().data()  # .decode()
            self.dataReady.emit(data)
            self.process(data)
            self.buffer.clear()

    def gets(self, raw=False):
        '''
        Read characters from the serial port until eol is received

        Returns
        -------
        s : str
            Decoded string
        '''
        while not self.buffer.contains(self.eol):
            if self.waitForReadyRead(self.timeout):
                self.buffer.append(self.readAll())
            else:
                logger.debug(' gets() timed out')
                break
        if raw:
            s = self.buffer.trimmed().data()
        else:
            s = self.buffer.trimmed().data().decode()
        logger.debug(' gets() received {} bytes: {}'.format(len(s), s))
        self.buffer.clear()
        return s

    def handshake(self, cmd, raw=False):
        '''
        Send command string to device and return the
        response from the device

        ...

        This form of communication bypasses the
        signal/slot mechanism and thus is blocking.

        Arguments
        ---------
        cmd : str
            String to be transmitted to device

        Returns
        -------
        res : str
            Response from device
        '''
        self.blockSignals(True)
        self.send(cmd)
        res = self.gets(raw)
        self.blockSignals(False)
        return res