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
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)