Ejemplo n.º 1
0
class Main(responder.API):
    def __init__(self,
                 *,
                 port: str,
                 baudrate: int,
                 path: str = 'db',
                 bs: int = 32,
                 to: int = 5,
                 http: int = 8080):

        super().__init__()
        pid = os.getpid()
        logger.info('*** NMEA Recorder startup (%d)' % pid)

        self.process: Dict[str, int] = {}
        self.process['main'] = pid

        self.ppp: Dict[str, Patrol] = {}
        self.ppp['main'] = Patrol(pid=pid)

        self.ready: bool = True
        self.g = GPS()
        self.ws = WebsocketServer(debug=True)

        self.qp = Queue()

        self.dbsession = DBSession(path=pathlib.Path(path),
                                   buffersize=bs,
                                   timeout=to)
        self.dbsession.start()
        self.process[self.dbsession.name] = self.dbsession.pid
        self.ppp[self.dbsession.name] = Patrol(pid=self.dbsession.pid)

        self.receiver = Receiver(port=port, baudrate=baudrate, qp=self.qp)
        self.receiver.start()
        self.process[self.receiver.name] = self.receiver.pid
        self.ppp[self.receiver.name] = Patrol(pid=self.receiver.pid)

        self.mc = fromUDP(quePoint=self.qp, mcip='239.192.0.1', mcport=60001)
        self.mc.start()
        self.process[self.mc.name] = self.mc.pid
        self.ppp[self.mc.name] = Patrol(pid=self.mc.pid)

        self.main = Thread(target=self.collector, name='MainLoop', daemon=True)
        self.main.start()

        self.p = Thread(target=self.patrol, name='Patrol', daemon=True)
        self.p.start()
        for k, v in self.ppp.items():
            v.start()

        self.add_route('/ws', self.ws.wsserver, websocket=True)
        self.add_route('/', self.top)
        self.add_route('/main.js', self.mainJS)
        self.add_route('/classes.js', self.classes)

        self.add_event_handler('shutdown', self.cleanup)  # notice!
        self.run(address='0.0.0.0', port=http)

    async def cleanup(self):
        self.dbsession.join()
        self.receiver.join()
        self.mc.join()
        logger.debug('... OK! shutdown')

    def top(self, req: responder.Request, resp: responder.Response):
        resp.content = self.template('index.html')

    def classes(self, req: responder.Request, resp: responder.Response):
        resp.content = self.template('classes.js')

    def mainJS(self, req: responder.Request, resp: responder.Response):
        resp.content = self.template('main.js')

    def collector(self):
        loop: bool = True
        try:
            while loop:
                try:
                    raw: bytes = self.receiver.qp.get()
                except (KeyboardInterrupt, ) as e:
                    break
                else:
                    self.dbsession.qp.put(raw)

                    part = raw.split(b'*')
                    if len(part) > 1:
                        main = part[0][1:]
                        csum = int(part[1][:2], 16)
                        calc = reduce(xor, main, 0)
                        if calc != csum:
                            logger.error('!!! bad checksum')
                        else:
                            item = main.split(b',')
                            symbol = item[0]
                            prefix = symbol[0:2]
                            suffix = symbol[2:5]
                            if prefix == b'GP':
                                if suffix == b'RMC':
                                    location = self.g.get(item=item)
                                    ooo = {
                                        'type': 'location',
                                        'info': asdict(location)
                                    }
                                    self.ws.broadcast(message=json.dumps(ooo))
                                    if location.valid:
                                        print(location)

        except KeyboardInterrupt as e:
            loop = False

    def patrol(self):
        loop: bool = True
        try:
            while loop:
                time.sleep(5)
                stats = {'type': 'stats', 'info': {}}
                # logger.info('--------------------------------------------------------------------')
                for k, v in self.ppp.items():
                    stats['info'][k] = asdict(v.stats)

                # logger.info(stats)
                self.ws.broadcast(message='%s' % json.dumps(stats, indent=2))
        except KeyboardInterrupt as e:
            loop = False
Ejemplo n.º 2
0
class Main(responder.API):
    def __init__(self):
        super().__init__()

        logger.info('### startup')
        self.running = True

        self.lack: Dict[str, Stock] = {}
        self.holdSecs = (60 * 2)
        self.locker = Lock()

        self.collector = Collector()
        self.collector.start()

        self.dbsession = DBSession(path='NMEA', buffersize=1000)
        self.dbsession.start()

        self.ws = WebsocketServer(debug=True)
        self.add_route('/ws', self.ws.wsserver, websocket=True)

        self.counter = 0
        self.ticker = Event()
        signal.signal(signal.SIGALRM, self.clocker)
        signal.setitimer(signal.ITIMER_REAL, 1, 1)

        self.cycle = Thread(target=self.routineWorker, daemon=True)
        self.cycle.start()

        self.mainLoop = Thread(target=self.loop, daemon=True)
        self.mainLoop.start()

        # logger.debug('$$$ collector = %d' % self.collector.pid)
        # logger.debug('$$$ dbsession = %d' % self.dbsession.pid)

        self.run(address='0.0.0.0', port=8080)

        self.running = False
        # self.collector.join()
        self.dbsession.join()
        logger.info('shutdown')

    def healthChecker(self, *, pid: int, name:str):
        p = psutil.Process(pid)
        cpuP = p.cpu_percent(interval=1)
        cpuN = p.cpu_num()
        logger.debug('=== %s: %d - %.1f' % (name, cpuN, cpuP))

    def clocker(self, number, frame):
        self.ticker.set()
        self.counter += 1

    def loop(self):
        while self.running:
            try:
                stock: Stock = self.collector.outputQueue.get()
            except (KeyboardInterrupt,) as e:
                logger.warning(e)
            else:
                for nmea in stock.nmea:
                    self.ws.broadcast(message=nmea.raw.decode())
                    self.dbsession.qp.put(nmea.raw)
                    name = nmea.item[0][1:]
                    with self.locker:
                        # if name not in self.lack:
                        #     logger.success('+++ append %s' % name)
                        self.lack[name] = nmea

    def cleaner(self):
        now = dt.now()
        with self.locker:
            expired: List[str] = []
            for k, v in self.lack.items():
                if (now - v.at).total_seconds() >= self.holdSecs:
                    expired.append(k)
            if len(expired):
                # logger.debug('--- %s was expired' % expired)
                for name in expired:
                    del self.lack[name]

    def routineWorker(self):
        lastEntries: int = 0
        while self.running:
            self.ticker.clear()
            self.ticker.wait()
            thisEntries: int = len(self.lack)
            if thisEntries != lastEntries:
                logger.debug('Holding %d NMEA' % thisEntries)
            lastEntries = thisEntries

            if self.counter % 5 == 0:
                self.cleaner()
                # self.healthChecker(name='main', pid=os.getpid())
                self.healthChecker(name=self.collector.name, pid=self.collector.pid)