diff --git a/appdata.py b/appdata.py index f3747fc..8cc7256 100644 --- a/appdata.py +++ b/appdata.py @@ -4,6 +4,7 @@ Generische Applikationsdaten """ import os +from web import WebInterface from tracker import Tracker from navtex import NAVTEX @@ -14,6 +15,13 @@ class AppData(): self.log = logger if cfg['navtex']: self.navtex = NAVTEX(logger, cfg) + else: + self.navtex = None + if cfg['tide']: + self.web = WebInterface(logger, cfg) + self.web.refresh() + else: + self.web = None self.track = Tracker(logger, cfg) self.frontend = None self.bv_lat = None diff --git a/navtex.py b/navtex.py index c0f13a7..bf77c03 100644 --- a/navtex.py +++ b/navtex.py @@ -26,6 +26,9 @@ B2: Subject indicator character Y Special services — allocation by IMO NAVTEX Panel Z No message on hand +Für Konfiguration: subjects = ABCDEFGHIJKLTVWXYZ + + B3, B4: Serial number 01-99, 00: immediate printout Timecode: DDHHmm UTC MMM YY @@ -38,6 +41,7 @@ Sqlite database schema content TEXT received TEXT +siehe auch: https://www.icselectronics.co.uk/support/info/navtexdb """ import os import http.client diff --git a/obp60v.conf-sample b/obp60v.conf-sample index 9d4df19..4fc0deb 100644 --- a/obp60v.conf-sample +++ b/obp60v.conf-sample @@ -36,6 +36,8 @@ port = /dev/ttyACM0 [navtex] enabled = false source = net +navarea = I +subjects = ABDEFKLXYZ housekeeping = 72 refresh = 30 diff --git a/obp60v.py b/obp60v.py index 032b6b8..9095ece 100755 --- a/obp60v.py +++ b/obp60v.py @@ -6,7 +6,7 @@ Virtuelles Multifunktionsgerät OBP60v Zwei Displayvarianten 1. Fliegendes OBP60 auf großem Bildschirm. 400x300 Display 2. Fullscreen Display skaliert mit 1,6 ergibt 640x480 - mit Platz für große Touch-Flächen am rechten Rand + mit Platz für große Touch-Flächen (160px) am rechten Rand Das Gerät kann Daten von NMEA2000 und NMEA0183 empfangen, sowie von einem lokal angeschlossenen GPS-Empfänger. @@ -141,6 +141,7 @@ cfg = { 'gps': False, 'bme280': False, 'tracker': { 'type': 'NONE' }, + 'tide': False, 'boat': { } } @@ -798,13 +799,21 @@ if __name__ == "__main__": config.write(fh) log.info("Created new boat UUID: {}".format(cfg['boat']['uuid'])) - # Globale Daten, u.a. auch Shutdown-Indikator - appdata = AppData(log, cfg) - # Ggf. Simulationsdaten einschalten if cfg['simulation']: boatdata.enableSimulation() + # Tide wird aktiviert wenn mindestens eine Tidenseite angezeigt wird + for s in config.sections(): + if s == 'pages': + continue + if s.startswith('page'): + if config.get(s, "type") == 'Tide': + cfg['tide'] = True + + # Globale Daten, u.a. auch Shutdown-Indikator + appdata = AppData(log, cfg) + # Gerät initialisieren u.a. mit den genutzten Seiten profile = init_profile(config, cfg, boatdata) @@ -834,6 +843,7 @@ if __name__ == "__main__": elif cfg['tracker']['type'] in ['LOCAL', 'SDCARD']: t_tracker = threading.Thread(target=appdata.track.local_tracker, args=(cfg,appdata,boatdata)) t_tracker.start() + if not cfg['simulation']: if cfg['bme280']: log.info("Environment sensor enabled") diff --git a/pages/tide.py b/pages/tide.py index 332fc39..2c089c1 100644 --- a/pages/tide.py +++ b/pages/tide.py @@ -1,8 +1,7 @@ """ -NAVTEX - - Meldungen anzeigen - +Tide Vorausschau + """ import cairo @@ -25,6 +24,9 @@ class Tide(Page): ctx.set_font_size(16) ctx.set_line_width(2) + ctx.move_to(220, 40) + ctx.show_text("Schulau, Elbe") + x0 = 40 # links unten y0 = 250 x1 = 380 # rechts oben @@ -48,3 +50,28 @@ class Tide(Page): self.draw_text_center(ctx, x0 + (x1 - x0) / 2, y0 + 12, "Zeit, h") ctx.stroke() + ymin, ymax = self.app.web.get_tide_minmax() + rawdata = self.app.web.get_tide() + + ctx.move_to(x0 - 32, y0 + 8) + ctx.show_text(str(ymin)) + + ctx.move_to(x0 - 32, y1 + 8) + ctx.show_text(str(ymax)) + + ctx.move_to(x0 + 16, y0 + 16) + ctx.show_text(f"n = {len(rawdata)}") + + dx = (x1 - x0) / len(rawdata) + x = x0 + while rawdata[0][0] is None: + x += dx + del rawdata[0] + y = (rawdata[0][0] - ymin) / (ymax - ymin) * (y0 - y1) + ctx.move_to(x, y0 - (rawdata[0][0] - ymin)) + del rawdata[0] + x += dx + for val in rawdata: + y = (val[0] - ymin) / (ymax - ymin) * (y0 - y1) + ctx.line_to(x, y0 - y) + x += dx diff --git a/web.py b/web.py index 053189e..751d4d3 100644 --- a/web.py +++ b/web.py @@ -1,7 +1,7 @@ """ Web Interface -Regelmäßiges Abfragen von Daten im Internet +Regelmäßiges Abfragen von Daten aus dem Internet - NAVTEX - DWD - Pegelstände @@ -13,7 +13,7 @@ Regelmäßiges Abfragen von Daten im Internet import os import http.client import ssl -import re +import json import sqlite3 import datetime from gi.repository import GLib @@ -35,7 +35,14 @@ class WebInterface(): if self.cur.fetchone() == None: sql = ("CREATE TABLE IF NOT EXISTS station (" "uuid TEXT PRIMARY KEY NOT NULL," - "name TEXT" + "name TEXT)" + ) + self.cur.execute(sql) + sql = ("CREATE TABLE IF NOT EXISTS data (" + "timestamp TEXT PRIMARY KEY NOT NULL," + "astro NUMBER NOT NULL," + "forecast NUMBER," + "measurement NUMBER)" ) self.cur.execute(sql) self.log.info(f"Created tide database: {dbpath}") @@ -44,6 +51,7 @@ class WebInterface(): #GLib.timeout_add_seconds(cfg['tide_refresh'] * 60, self.on_timer) GLib.timeout_add_seconds(60 * 60, self.on_timer) self.running = True + self.log.info(f"Web interface started") def __del__(self): self.conn.close() @@ -62,7 +70,7 @@ class WebInterface(): """ if local: # Für Tests um nicht permanent die Webseite abzufragen - with open("DE__714P.json", "r") as fh: + with open("/tmp/DE__714P.json", "r") as fh: content = fh.read() else: ssl_context = ssl.create_default_context() @@ -88,18 +96,26 @@ class WebInterface(): def refresh(self): self.log.info("Data refresh") - data = self.bsh_get_data(True) - """ - sql = "INSERT INTO message (msgid, station, content) VALUES (?, ?, ?)" - for m in messages: - msg = self.parse_message(m) - self.cur.execute("SELECT COUNT(*) FROM message WHERE msgid=?", (msg['id'],)) - result = self.cur.fetchone() - if result[0] == 0: - self.log.debug(f"NAVTEX: insert new message '{msg['id']}'") - self.cur.execute(sql, (msg['id'], msg['station'], m)) - self.conn.commit() - """ - + data = self.bsh_get_data(False) + #tmpfile = open("/tmp/DE__714P.json", "w") + #tmpfile.write(data) + #tmpfile.close() + wvdata = json.loads(data) + sql = "INSERT OR REPLACE INTO data (timestamp, astro, forecast, measurement) VALUES (?, ?, ?, ?)" + for wv in wvdata["curve_forecast"]["data"]: + self.cur.execute(sql, (wv['timestamp'], wv['astro'], wv['curveforecast'], wv['measurement'])) + self.conn.commit() + def housekeeping(self): self.log.info("Housekeeping") + + def get_tide(self): + # 12 Stunden + sql = "select forecast from data where timestamp > '2025-10-05 12:00' and timestamp < '2025-10-06 22:00'" + self.cur.execute(sql) + return self.cur.fetchall() + + def get_tide_minmax(self): + sql = "select min(forecast), max(forecast) from data where timestamp > '2025-10-05 12:00' and timestamp < '2025-10-06 22:00'" + self.cur.execute(sql) + return self.cur.fetchone()