From d3a984075e0618943ba14789b8722a12ecc3651f Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Tue, 16 Sep 2025 21:05:34 +0200 Subject: [PATCH] Tracker GUI Verbesserungen --- pages/tracker.py | 116 ++++++++++++++++++++++++++++++++++++----------- tracker.py | 27 +++++++---- 2 files changed, 108 insertions(+), 35 deletions(-) diff --git a/pages/tracker.py b/pages/tracker.py index 217efea..79cb1da 100644 --- a/pages/tracker.py +++ b/pages/tracker.py @@ -24,8 +24,6 @@ TODO import os import cairo from .page import Page -from cfgmenu import Menu - class Tracker(Page): @@ -38,7 +36,9 @@ class Tracker(Page): self.raceid = None # Ausgewählte Regatta self.menupos = 0 self.buttonlabel[1] = 'MODE' - self.mode = 'N' # (N)ormal, (C)onfiguration + self.buttonlabel[2] = 'INFO' + self.buttonlabel[5] = 'ABORT' + self.mode = 'N' # (N)ormal, (C)onfiguration, (M)itteilung # Flaggengröße: 96 x 64 Pixel self.flagpos = ((208, 140), (308, 140), (208, 210), (308, 210)) @@ -73,9 +73,6 @@ class Tracker(Page): flagfile = os.path.join(cfg['imgpath'], 'flags', f + '.png') self.sym_flag[f] = cairo.ImageSurface.create_from_png(flagfile) - self._menu = Menu("Regattas", 200, 250) - self._menu.setItemDimension(120, 20) - def handle_key(self, buttonid): if buttonid == 1: # Modus umschalten @@ -90,12 +87,17 @@ class Tracker(Page): self.buttonlabel[5] = 'ON' else: self.mode = 'N' - self.buttonlabel[2] = '' + self.buttonlabel[2] = 'INFO' self.buttonlabel[3] = '#PREV' self.buttonlabel[4] = '#NEXT' - self.buttonlabel[5] = '' + self.buttonlabel[5] = 'ABORT' return True elif buttonid == 2: + if self.mode == 'N': + self.mode = 'M' # Nachrichten der Wettfahrtleitung + self.buttonlabel[2] = '' + self.buttonlabel[5] = '' + return True if self.mode == 'C': # Up if self.menupos > 1: @@ -128,7 +130,7 @@ class Tracker(Page): else: self.appdata.track.set_active(True) self.buttonlabel[5] = 'OFF' - else: + elif self.mode == 'M': self.appdata.frontend.flashled.setColor('yellow') #self.appdata.frontend.flashled.switchOn(4) self.appdata.frontend.flashled.doFlash(2) @@ -162,42 +164,54 @@ class Tracker(Page): ctx.move_to(100, 120) ctx.show_text("off") + if self.appdata.track.hero_timedelta > 5: + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + ctx.move_to(8, 260) + ctx.show_text(f"!!! Time drift of {self.appdata.track.hero_timedelta} seconds") + x0 = 8 - x1 = 104 + x1 = 96 y0 = 150 + yoffset = 18 ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(16) ctx.move_to(x0, y0) - ctx.show_text("Type: ") + ctx.show_text("Type") ctx.move_to(x1, y0) ctx.show_text(self.appdata.track.ttype) - ctx.move_to(x0, y0 + 16) + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Regatta") - ctx.move_to(x1, y0 + 16) + ctx.move_to(x1, y0) ctx.show_text(self.appdata.track.hero_raceid or '[not selected]') - ctx.move_to(x0, y0 + 32) + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Course") - ctx.move_to(x1, y0 + 32) + ctx.move_to(x1, y0) if self.appdata.track.hero_orgstatus and self.appdata.track.hero_raceid: ctx.show_text(self.appdata.track.hero_orgstatus['races'][self.appdata.track.hero_raceid]['courseid']) else: ctx.show_text('[not selected]') - ctx.move_to(x0, y0 + 48) + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Latitude") - ctx.move_to(x1, y0 + 48) + ctx.move_to(x1, y0) ctx.show_text(self.bv_lat.format()) - ctx.move_to(x0, y0 + 64) + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Longitude") - ctx.move_to(x1, y0 + 64) + ctx.move_to(x1, y0) ctx.show_text(self.bv_lon.format()) - ctx.move_to(x0, y0 + 80) + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Speed") - ctx.move_to(x1, y0 + 80) + ctx.move_to(x1, y0) ctx.show_text(self.bv_sog.format()) # Flaggen @@ -257,6 +271,8 @@ class Tracker(Page): x0 = 208 x1 = 272 + y0 = 96 + yoffset = 16 ctx.set_font_size(20) ctx.move_to(x0, 75) @@ -265,14 +281,37 @@ class Tracker(Page): ctx.set_font_size(16) ctx.move_to(x0, y0) - ctx.show_text("Type: ") + ctx.show_text("Type") + ctx.move_to(x1, y0) ctx.show_text(self.appdata.track.ttype) - ctx.move_to(x0, y0 + 16) - ctx.show_text("Status") - ctx.move_to(x0, y0 + 32) + + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Org.") - ctx.move_to(x0, y0 + 48) + ctx.move_to(x1, y0) + if self.appdata.track.hero_orgstatus: + ctx.show_text(self.appdata.track.hero_orgstatus['orgname']) + else: + ctx.show_text("n/a") + + y0 += yoffset + ctx.move_to(x0, y0) + ctx.show_text("Status") + ctx.move_to(x1, y0) + if not self.appdata.track.hero_racestatus: + ctx.show_text("inactive") + else: + #TODO Mehr Details + ctx.show_text("active") + + y0 += yoffset + ctx.move_to(x0, y0) ctx.show_text("Team") + ctx.move_to(x1, y0) + if self.appdata.track.hero_racestatus: + ctx.show_text(self.appdata.track.hero_racestatus['team']) + else: + ctx.show_text("n/a") # Mögliche Regatten self.races = self.appdata.track.hero_get_races() @@ -301,8 +340,31 @@ class Tracker(Page): ctx.move_to(x, y + 20) ctx.show_text("[ none ]") + def draw_info(self, ctx): + """ + Nachricht der Wettfahrtleitung + """ + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(24) + ctx.move_to(4, 42) + ctx.show_text("Message from race officers") + + ctx.set_font_size(16) + if not self.appdata.track.hero_orgstatus: + ctx.move_to(8, 72) + ctx.show_text("[ empty ]") + else: + lines = self.appdata.track.hero_orgstatus['message'].splitlines() + y = 72 + for l in lines: + ctx.move_to(8, y) + ctx.show_text(l) + y += 18 + def draw(self, ctx): if self.mode == 'N': self.draw_normal(ctx) - else: + elif self.mode == 'C': self.draw_config(ctx) + else: + self.draw_info(ctx) diff --git a/tracker.py b/tracker.py index cd8ce72..220e9f3 100644 --- a/tracker.py +++ b/tracker.py @@ -47,6 +47,7 @@ class Tracker(): self.hero_orgstatus = None self.hero_racestatus = None self.hero_raceid = None # Aktuelle Regatta + self.hero_timedelta = 0 # Zeitdifferenz zum Server in sec # TODO Wirklich alles im Tracker oder ist einiges generisch? self.boatid = None @@ -106,7 +107,6 @@ class Tracker(): if not r['hiderace']: races.append(r['raceid']) return races - #return [r for r in self.hero_orgstatus['races']] def mqtt_on_connect(self, client, userdata, flags, rc): print(f"MQTT connected with result code {rc}") @@ -138,6 +138,14 @@ class Tracker(): if msg.topic == "regattahero/orgstatus/thomas": # kommt alle 10s self.hero_orgstatus = json.loads(msg.payload) + + # Zeitdifferenz Client/Server ermitteln für ggf. spätere Warnung + server_ts = self.appdata.track.hero_orgstatus['timestamp'].split(':') + sec1 = int(server_ts[0]) * 3600 + int(server_ts[1]) * 60 + int(server_ts[2]) + ts = time.localtime() + sec2 = ts.tm_hour * 3600 + ts.tm_min * 60 + ts.tm_sec + self.hero_timedelta = abs(sec1 - sec2) + if self.hero_orgstatus['allLogout']: print("All logout received!") client.disconnect() @@ -145,17 +153,13 @@ class Tracker(): return if self.hero_orgstatus['message']: # TODO Alarm-Funktion nutzen? - print("Nachricht der Wettfahrtkeitung:") - print(self.hero_orgstatus['message']) - #print(self.hero_orgstatus) + pass elif msg.topic.startswith("regattahero/racestatus/thomas"): # kommt alle 1s # dem Topic angehängt ist noch die raceid payload = json.loads(msg.payload) self.hero_racestatus = payload['racestatus'] - print(self.hero_racestatus['flags']) - #print(self.hero_racestatus) """ time: negativ: Zeit vor dem Start, positiv: Zeit nach dem Start in Sekunden @@ -195,10 +199,10 @@ class Tracker(): payload['gps']['lon'] = round(lon, 5) payload['gps']['speed'] = sog payload['gps']['timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()) - print(payload) client.publish(topic, json.dumps(payload)) else: - print("No GPS data available. Nothing published!") + #print("No GPS data available. Nothing published!") + pass def mqtt_tracker(self, cfg, boat, appdata, boatdata): print("MQTT tracker enabled") @@ -251,6 +255,13 @@ class Tracker(): "boatname": boat['name'], "isTracking": True, "hasGivenUp": False + }, + "device": { # optional + "os" : "linux", + "osVer" : "OBP60v 0.1", + "isIOS" : False, + "isAndroid" : False, + "isTracker" : True } }