def run(self):
        logger.info('thread started')
        pltData = {}
        cps = FPS()
        cps.getFPS = lambda: (0 if cps._numFrames == 0 else cps.fps())  # pylint: disable=protected-access
        startTime = epochTime()
        cps.start()
        while True:
            try:
                serialInput = self.handler.read_until(
                    '#'.encode('utf-8')).decode('utf-8')
                logger.info('INPUT: %s', serialInput)
                if not serialInput.endswith('@#'):
                    tiltOutput, panOutput, _ = serialInput.split(' ')
                    pltData['tiltOutput'] = int(tiltOutput)
                    pltData['panOutput'] = int(panOutput)
                    self.outQ.put(pltData)
            except Exception as err:
                logger.error('serial input failed. %s', err)
                continue

            self.readyEvent.set()
            data = self.inQ.get()
            if data is None:
                break
            tiltErr, panErr = [data[key] for key in ('tiltErr', 'panErr')]

            serialOutput = f'{tiltErr * -1} {panErr * -1}$'
            self.handler.write(serialOutput.encode('utf-8'))
            logger.info('OUTPUT: %s', serialOutput)
            cps.update()
            cps.stop()
            pltData['time'] = epochTime() - startTime
            pltData['panErr'] = panErr
            pltData['tiltErr'] = tiltErr

        self.outQ.put(None)

        self.handler.close()
        logger.info('CPS: %s', cps.getFPS())
        logger.info('thread finished')