From e7a96390f2cb450081e931212dacad6aa69b71a6 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Mon, 22 Sep 2025 14:27:05 +0200 Subject: [PATCH] =?UTF-8?q?Lokaler=20Tracker=20funktionsf=C3=A4hig.=20Bugf?= =?UTF-8?q?ixes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO | 9 +++- obp60v.py | 14 +++--- pages/barograph.py | 24 +++++++--- pages/racetracker.py | 107 +++++++++++++++++++++++++++++++++++-------- pages/wind.py | 8 ++++ tracker.py | 32 ++++++++----- 6 files changed, 150 insertions(+), 44 deletions(-) diff --git a/TODO b/TODO index 3b5c2d4..b4d6aa7 100644 --- a/TODO +++ b/TODO @@ -5,7 +5,14 @@ Aufgaben, Planungs- und Ideenliste python3-paho-mqtt benötigt b) lokal c) Server - + +- Internationalisierung + 1. EN (Standardsprache) + 2. DE + 3. FR + 4. ES + 5. IT + - Satelliten: SatelliteList verwenden und dieses auch in nmea2000 implementieren - Satellitenübersicht / SkyView diff --git a/obp60v.py b/obp60v.py index 45dabea..14263bc 100755 --- a/obp60v.py +++ b/obp60v.py @@ -115,7 +115,7 @@ import pages __author__ = "Thomas Hooge" __copyright__ = "Copyleft 2024-2025, all rights reversed" -__version__ = "0.2" +__version__ = "0.3" __email__ = "thomas@hoogi.de" __status__ = "Development" @@ -128,7 +128,7 @@ cfg = { 'loglevel': 3, 'imgpath': os.path.join(sys.path[0], 'images'), 'audiopath': os.path.join(sys.path[0], 'audio'), - 'histpath' = '~/.local/lib/obp60v', + 'histpath': '~/.local/lib/obp60v', 'deviceid': 100, 'manufcode': 2046, # Open Boat Projects (OBP) 'devfunc': 120, # Display @@ -749,6 +749,7 @@ if __name__ == "__main__": cfg['tracker']['mqtt_pass'] = config.get('tracker', 'mqtt_pass') cfg['tracker']['logdir'] = cfg['logdir'] cfg['tracker']['trace'] = config.getboolean('tracker', 'trace') + cfg['tracker']['interval'] = config.getint('tracker', 'interval') # Boat data cfg['boat']['name'] = config.get('boat', 'name') @@ -806,14 +807,15 @@ if __name__ == "__main__": log.info("Networking enabled") t_rxd_net = threading.Thread(target=rxd_network, args=(cfg['net_port'],cfg['net_addr'])) t_rxd_net.start() - if cfg['tracker']['type'] == 'NONE': + if cfg['tracker']['type'] != 'NONE': log.info(f"Tracking enabled, mode {cfg['tracker']['type']}") #appdata.track.set_type( cfg['tracker']['type']) if cfg['tracker']['type'] == 'HERO': t_tracker = threading.Thread(target=appdata.track.mqtt_tracker, args=(cfg['tracker'],cfg['boat'],appdata,boatdata)) - elif cfg['tracker']['type'] IN ['LOCAL', 'SDCARD']: - t_tracker = threading.Thread(target=appdata.track.local_tracker, args=(cfg['tracker'],cfg['boat'],appdata,boatdata)) - t_tracker.start() + t_tracker.start() + 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/barograph.py b/pages/barograph.py index fe1de1c..6e3bbd2 100644 --- a/pages/barograph.py +++ b/pages/barograph.py @@ -45,7 +45,10 @@ class Barograph(Page): # Meßwert alle 15 Minuten: # 84 Stunden * 4 Werte je Stunde = 336 Meßwerte self.bd = boatdata - self.source = 'I' # (I)ntern, e(X)tern + if not cfg['bme280']: + self.source = None # keine Datenquelle! + else: + self.source = 'I' # (I)ntern, e(X)tern, None=Disabled self.zoom = (1, 2, 3, 6, 12) self.zoomindex = 4 self.series = (75, 150, 300, 600, 900) @@ -65,6 +68,7 @@ class Barograph(Page): self.buttonlabel[2] = '-' self.buttonlabel[5] = 'SRC' + self.data = None self.refresh = time.time() - 30 def handle_key(self, buttonid): @@ -113,6 +117,9 @@ class Barograph(Page): Transfer data from history to page buffer set y-axis according to data """ + if self.source == None: + # TODO hier muß noch Arbeit reingesteckt werden! + return False self.data = [] self.vmin = 9999 self.vmax = 0 @@ -363,8 +370,13 @@ class Barograph(Page): ctx.stroke() # Meßdaten - for v in self.data: - x0 -= 1 - if v > 0: - ctx.rectangle(x0, y0 - (v - ysmin) * dy, 1.5, 1.5) - ctx.fill() + if self.data: + for v in self.data: + x0 -= 1 + if v > 0: + ctx.rectangle(x0, y0 - (v - ysmin) * dy, 1.5, 1.5) + ctx.fill() + else: + # Keine Daten vorhanden! + # TODO iregndwas nettes anzeigen + pass diff --git a/pages/racetracker.py b/pages/racetracker.py index 3d0fbc0..8b039f6 100644 --- a/pages/racetracker.py +++ b/pages/racetracker.py @@ -33,14 +33,16 @@ class RaceTracker(Page): self.bv_lat = boatdata.getRef("LAT") self.bv_lon = boatdata.getRef("LON") self.bv_sog = boatdata.getRef("SOG") + self.bv_hdop = boatdata.getRef("HDOP") self.races = None + self.raceid = None if self.ttype == 'HERO': self.raceid = self.app.track.hero_raceid # Ausgewählte Regatta self.buttonlabel[1] = 'MODE' self.buttonlabel[2] = 'INFO' self.buttonlabel[5] = 'ABORT' - else: - self.raceid = None + elif self.ttype in ('LOCAL','SDCARD'): + self.buttonlabel[5] = 'ON' self.menupos = 0 self.mode = 'N' # (N)ormal, (C)onfiguration, (M)itteilung self.query_active = False @@ -92,6 +94,8 @@ class RaceTracker(Page): self.buttonlabel = self.savebuttons.copy() return True if buttonid == 1: + if not self.ttype == 'HERO': + return False # Modus umschalten if self.mode == 'N': self.mode = 'C' @@ -110,6 +114,8 @@ class RaceTracker(Page): self.buttonlabel[5] = 'ABORT' return True elif buttonid == 2: + if not self.ttype == 'HERO': + return False if self.mode == 'N': self.mode = 'M' # Nachrichten der Wettfahrtleitung self.buttonlabel[2] = '' @@ -140,15 +146,26 @@ class RaceTracker(Page): return True elif buttonid == 5: if self.mode == 'N': - self.query_active = True; - self.savebuttons = self.buttonlabel.copy() - self.buttonlabel[1] = "" - self.buttonlabel[2] = "YES" - self.buttonlabel[3] = "" - self.buttonlabel[4] = "" - self.buttonlabel[5] = "NO" - self.buttonlabel[6] = "" - # Taste 2 = JA, Taste 5 = NEIN + if self.ttype in ('LOCAL','SDCARD'): + # Tracking ein/-ausschalten + if self.app.track.is_active(): + self.app.track.set_active(False) + self.buttonlabel[5] = 'ON' + else: + self.app.track.set_active(True) + self.buttonlabel[5] = 'OFF' + else: + if self.ttype == ('HERO'): + # Rennabbruch verarbeiten + self.query_active = True; + self.savebuttons = self.buttonlabel.copy() + self.buttonlabel[1] = "" + self.buttonlabel[2] = "YES" + self.buttonlabel[3] = "" + self.buttonlabel[4] = "" + self.buttonlabel[5] = "NO" + self.buttonlabel[6] = "" + # Taste 2 = JA, Taste 5 = NEIN elif self.mode == 'C': # Tracking ein/-ausschalten if self.app.track.is_active(): @@ -180,16 +197,22 @@ class RaceTracker(Page): ctx.set_font_size(16) ctx.move_to(x, y) ctx.show_text("Disabled by 'NONE in configuration'.") - y += 40 + y += 30 ctx.move_to(x, y) - ctx.show_text("Currently only tracker type 'HERO' is") + ctx.show_text("Currently only tracker types 'HERO' and 'LOCAL'") y += 20 ctx.move_to(x, y) - ctx.show_text("implemented. Please visit the Regatta") + ctx.show_text("are implemented.") + y += 30 + ctx.move_to(x, y) + ctx.show_text("'LOCAL' tracks positions in file system.") + y += 30 + ctx.move_to(x, y) + ctx.show_text("For 'HERO' pleast visit the Regatta Hero") y += 20 ctx.move_to(x, y) - ctx.show_text("Hero web page for detailed description.") - y += 40 + ctx.show_text("web page for detailed description.") + y += 20 ctx.move_to(x, y) ctx.show_text("Additionally you should contact your local") y += 20 @@ -198,19 +221,61 @@ class RaceTracker(Page): def draw_local(self, ctx): x = 8 + x1 = 130 y = 50 ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(24) ctx.move_to(x, y) ctx.show_text("Local Tracking") + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + + y += 30 + ctx.move_to(x, y) + ctx.show_text("Activated: ") + ctx.show_text("Yes" if self.app.track.is_active() else "No") + y += 20 + ctx.move_to(x, y) + ctx.show_text(f"Log interval: {self.app.track.local_dt} seconds") + #ctx.show_text(str(self.app.track.local_dt)) + y += 20 + ctx.move_to(x, y) + ctx.show_text("Log entries written: ") + ctx.show_text(str(self.app.track.local_lfdno)) + # Anzeige # - LAT, LON # - bisher gespeicherte Anzahl Positionen # - Zeitabstand zwischen den einzelnen Messungen # - Hinweis wo gespeichert wird + y += 30 + ctx.move_to(x, y) + ctx.show_text("Latitude: ") + ctx.move_to(x1, y) + ctx.show_text(self.bv_lat.format()) + y += 20 + ctx.move_to(x, y) + ctx.show_text("Longitude: ") + ctx.move_to(x1, y) + ctx.show_text(self.bv_lon.format()) + y += 20 + ctx.move_to(x, y) + ctx.show_text("HDOP: ") + ctx.move_to(x1, y) + ctx.show_text(self.bv_hdop.format() or '---') + y += 20 + ctx.move_to(x, y) + ctx.show_text("Speed: ") + ctx.move_to(x1, y) + ctx.show_text(self.bv_sog.format()) - def draw_normal(self, ctx): + def draw_hero(self, ctx): + """ + Regatta Hero Normalansicht + TODO + racephase anzeige zu Debuggingzwecken + """ ctx.select_font_face("DSEG7 Classic") ctx.set_font_size(80) @@ -448,10 +513,12 @@ class RaceTracker(Page): def draw(self, ctx): if self.mode == 'N': - if self.ttype == 'NONE': - self.draw_none(ctx) + if self.ttype in ('LOCAL', 'SDCARD'): + self.draw_local(ctx) + elif self.ttype == 'HERO': + self.draw_hero(ctx) else: - self.draw_normal(ctx) + self.draw_none(ctx) elif self.mode == 'C': self.draw_config(ctx) else: diff --git a/pages/wind.py b/pages/wind.py index d8d4ab3..f856079 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -1,3 +1,11 @@ +""" +Wind / Windlupe + +TODO + - Windlupe: + Wenn der Wind achterlich kommt, dann andere Ansicht / Skala verwenden + +""" import os import cairo import math diff --git a/tracker.py b/tracker.py index fa04706..1c4a76c 100644 --- a/tracker.py +++ b/tracker.py @@ -48,6 +48,9 @@ class Tracker(): raise TypeError("Invalid tracker type: '{}'. Only supported: {}".format(cfg['tracker']['type'], ','.join(validtypes))) self.ttype = cfg['tracker']['type'] + self.local_lfdno = 0 + self.local_dt = cfg['tracker']['interval'] # Eintrag alle Sekunden schreiben + self.trace = cfg['tracker']['trace'] # Debugging self.trace_fh = None # File Handle der Tracedatei @@ -127,9 +130,11 @@ class Tracker(): def set_active(self, newval): self.activated = newval - def local_tracker(self, cfg, boat, appdata, boatdata): - # TODO / WIP + def local_tracker(self, cfg, appdata, boatdata): self.log.info("Local tracker enabled") + if not os.path.exists(cfg['histpath']): + os.makedirs(cfg['histpath']) + self.log.info(f"History path created: '{cfg['histpath']}'") # Zugriff auf Boatdata: Referenzen für leichten schnellen Zugriff bv_lat = boatdata.getRef("LAT") @@ -137,20 +142,25 @@ class Tracker(): bv_hdop = boatdata.getRef("HDOP") bv_tspos = boatdata.getRef("TSPOS") - self.local_dt = 15 - self.local_lfdno = 0 - trackerfile = "/tmp/test.log" # TODO Konfiguration lesen + trackerfile = os.path.join(cfg['histpath'], "localtrack.log") fh = open(trackerfile, 'a+') + n = 0 while not appdata.shutdown: - time.sleep(self.local_dt) + time.sleep(1) + if not self.activated: + continue + n += 1 + if n % self.local_dt != 0: + continue self.local_lfdno += 1 - data = f"{},{},{},{}\n".format( + data = "{},{}Z,{},{},{}\n".format( self.local_lfdno, - bv_tspos.getValueRaw(), - bv_lat.getValueRaw(), - bv_lon.getValueRaw(), - bv_hdop.getValueRaw()) + bv_tspos.getValue(), + bv_lat.getValue(), + bv_lon.getValue(), + bv_hdop.getValue()) fh.write(data) + fh.flush() fh.close() def set_hero_raceid(self, newraceid):