From 5008d0e9398edf4311d826d41e28e96cdb992cee Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 28 Apr 2020 22:06:51 +0200 Subject: Add message parser and /stats endpoint --- glutte_serial_web.py | 23 +++++++++--- serialrx.py | 99 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 10 deletions(-) diff --git a/glutte_serial_web.py b/glutte_serial_web.py index 4390f76..b5552dc 100755 --- a/glutte_serial_web.py +++ b/glutte_serial_web.py @@ -2,7 +2,7 @@ # # The MIT License (MIT) # -# Copyright (c) 2016 Matthias P. Braendli, Maximilien Cuony +# Copyright (c) 2020 Matthias P. Braendli, Maximilien Cuony # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -22,10 +22,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import time from geventwebsocket.handler import WebSocketHandler from gevent import pywsgi, Timeout from time import sleep -from flask import Flask, render_template +from flask import Flask, render_template, jsonify from flask_sockets import Sockets import serialrx import adsl @@ -36,17 +37,29 @@ sockets = Sockets(app) ser = serialrx.SerialRX() adsl = adsl.ADSL(ser) - @app.route('/') def index(): return render_template('index.html', last_lines=ser.get_last_lines()) +@sockets.route('/stats') +def stats(): + t_now = time.time() + values = ser.get_parsed_values() + + out_json = {} + + for k in values: + value, ts = values[k] + if ts + 60 < t_now: + out_json[k] = None + else: + out_json[k] = value + + return jsonify(out_json) @sockets.route('/stream') def stream(socket): - try: - queue = ser.register_client() error = False diff --git a/serialrx.py b/serialrx.py index 19a123c..4c3eac5 100644 --- a/serialrx.py +++ b/serialrx.py @@ -25,15 +25,65 @@ import serial import threading import collections +import re +import time import config +flags = re.ASCII +re_cc_capa = re.compile(r"\[\d+\] CC: CAPA,\d+,(\d+)", flags) +re_cc_vbat_plus = re.compile(r"\[\d+\] CC: VBAT\+,\d+,(\d+)", flags) +re_num_sv = re.compile(r"\[\d+\] T_GPS.+ (\d+) SV tracked", flags) +re_alim = re.compile(r"\[\d+\] ALIM (\d+) mV", flags) -class SerialRX(threading.Thread): +class MessageParser: + def __init__(self): + self._lock = threading.Lock() + + self._last_cc_capa = 0 + self._last_cc_capa_time = 0 + self._last_vbat_plus = 0 + self._last_vbat_plus_time = 0 + self._last_num_sv = 0 + self._last_num_sv_time = 0 + self._last_alim = 0 + self._last_alim_time = 0 + + def parse_message(self, message): + with self._lock: + match = re_cc_capa.search(message) + if match: + self._last_cc_capa = int(match.group(1)) + self._last_cc_capa_time = time.time() + + match = re_cc_vbat_plus.search(message) + if match: + self._last_vbat_plus = int(match.group(1)) + self._last_vbat_plus_time = time.time() + + match = re_num_sv.search(message) + if match: + self._last_num_sv = int(match.group(1)) + self._last_num_sv_time = time.time() + + match = re_alim.search(message) + if match: + self._last_alim = int(match.group(1)) + self._last_alim_time = time.time() + + def get_last_data(self): + with self._lock: + return {"capa": (self._last_cc_capa, self._last_cc_capa_time), + "vbat_plus": (self._last_vbat_plus, self._last_vbat_plus_time), + "alim": (self._last_alim, self._last_alim_time), + "num_sv": (self._last_num_sv, self._last_num_sv_time) } +class SerialRX(threading.Thread): def __init__(self): threading.Thread.__init__(self) + self._parser = MessageParser() + print("Open Serial on {} at {}".format(config.SERIALPORT, config.BAUDRATE)) self.ser = serial.Serial(config.SERIALPORT, baudrate=config.BAUDRATE) @@ -47,6 +97,9 @@ class SerialRX(threading.Thread): print("Serial port ready") + def get_parsed_values(self): + return self._parser.get_last_data() + def run(self): print("Serial port starting reception") while not self.event_stop.is_set(): @@ -54,20 +107,20 @@ class SerialRX(threading.Thread): self.line_accumulator.append(databyte) if databyte == "\n": + line = "".join(self.line_accumulator) + self._parser.parse_message(line) self.data_lock.acquire() try: - for queue in self.clients: - queue.append("".join(self.line_accumulator)) + queue.append(line) if len(queue) > config.LINES_TO_KEEP: queue.popleft() - self.last_lines.append("".join(self.line_accumulator)) + self.last_lines.append(line) if len(self.last_lines) > config.LAST_LINE_TO_KEEP: self.last_lines.pop(0) - except: raise finally: @@ -101,3 +154,39 @@ class SerialRX(threading.Thread): raise finally: self.data_lock.release() + +if __name__ == "__main__": + test_set = """[193583144] CW: K + [193583168] In cw_done change 0 0 + [193584056] FSM: FSM_ECOUTE + [193585992] In SQ 1 + [193586008] FSM: FSM_QSO + [193592816] CC: CAPA,148111,1632707 + [193605944] CC: CAPA,148121,1632682 + [193605944] CC: VBAT+,148121,12340 + [193605944] CC: VBAT-,148121,0 + [193612144] ALIM 11811 mV + [193672600] T_GPS 2020-04-28 19:07:30 12 SV tracked + [193672656] TIME 2020-04-28 21:07:30 [GPS] + [193692528] TEMP invalid + """ + test_should = { "capa": 1632682, "vbat_plus": 12340, "alim": 11811, "num_sv": 12 } + + mp = MessageParser() + + print("Testing parser") + for message in test_set.split("\n"): + print(f"Parse {message}") + mp.parse_message(message) + + test_measured = mp.get_last_data() + + for k in test_measured: + if test_measured[k][1] == 0: + print(f"Value {k} has time 0") + + values = {k: test_measured[k][0] for k in test_measured} + if values != test_should: + print("invalid values {}".format(values)) + + print("Test end") -- cgit v1.2.3