""" Tracker mit MQTT client Es gibt zwei Modi: Track und Race A) Track: Es wird nur der Track gesended / aufgezeichnet B) Race: Es werden zusätzlich Regattadaten angezeigt, das beinhaltet die Auswahl einer Regatta, das Teilnehmen, das Aufgeben und die Signalisierung vor und während einer Wettfahrt. Das Tracking kann über eine Taste ein- und ausgeschaltet werden. Um versehentliches Umschalten zu vermeiden ist eine Nachfrage integriert. - currentry only Ragatta hero supported Behandlung von Verbindungsabbrüchen: - on_disconnect TODO """ import os import cairo from .page import Page class RaceTracker(Page): def __init__(self, pageno, cfg, appdata, boatdata): super().__init__(pageno, cfg, appdata, boatdata) self.bv_lat = boatdata.getRef("LAT") self.bv_lon = boatdata.getRef("LON") self.bv_sog = boatdata.getRef("SOG") self.races = None self.raceid = self.app.track.hero_raceid # Ausgewählte Regatta self.menupos = 0 self.buttonlabel[1] = 'MODE' 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)) # Flaggen laden flag = ('alpha', 'answer', 'black', 'blue', 'charlie', 'class', 'finish', 'foxtrot', 'hotel', 'india', 'november', 'orange', 'papa', 'repeat_one', 'sierra', 'start', 'uniform', 'xray', 'yankee', 'zulu') # Mapping self.flagmap = { 3: 'blue', # Zielflagge 7: 'xray', # Einzelrückruf 8: 'sierra', # Bahnverkürzung 9: 'november', # Abbruch 10: 'yankee', # Schwimmwesten 11: 'repeat_one', # Allgemeiner Rückruf 12: 'answer', # Startverschiebung 14: 'start', # Startflagge 15: 'class', # Klassenflagge 18: 'alpha', 19: 'hotel', 20: 'charlie', 100: 'papa', # Vorbereitung, Frühstart: Zurückfallen über Startlinie 101: 'india', # Vorbereitung, Frühstart: Starttonne umrunden 102: 'zulu', # Frühstart: 20%-Strafe 103: 'uniform', # Frühstart: Disqualifikation, Wiederholung erlaubt 104: 'black' # Frühstart: Disqualifikation, Wiederholung nicht erlaubt } self.sym_flag = {} for f in flag: flagfile = os.path.join(cfg['imgpath'], 'flags', f + '.png') self.sym_flag[f] = cairo.ImageSurface.create_from_png(flagfile) def handle_key(self, buttonid): if buttonid == 1: # Modus umschalten if self.mode == 'N': self.mode = 'C' self.buttonlabel[2] = '#UP' self.buttonlabel[3] = '#DOWN' self.buttonlabel[4] = 'SET' if self.app.track.is_active(): self.buttonlabel[5] = 'OFF' else: self.buttonlabel[5] = 'ON' else: self.mode = 'N' self.buttonlabel[2] = 'INFO' self.buttonlabel[3] = '#PREV' self.buttonlabel[4] = '#NEXT' 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: self.menupos -= 1 else: self.menupos = len(self.races) return True elif buttonid == 3: if self.mode == 'C': # Down if self.menupos < len(self.races): self.menupos += 1 else: self.menupos = 1 return True elif buttonid == 4: if self.mode == 'C': # Set / Select regatta if self.menupos > 0: self.raceid = self.races[self.menupos - 1] # Nullbasiert self.app.track.hero_raceid = self.raceid print(f"Selected race '{self.raceid}'") return True elif buttonid == 5: if self.mode == 'C': # 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' elif self.mode == 'M': self.app.frontend.flashled.setColor('yellow') #self.app.frontend.flashled.switchOn(4) self.app.frontend.flashled.doFlash(2) return True return False def draw_normal(self, ctx): ctx.select_font_face("DSEG7 Classic") ctx.set_font_size(80) if self.app.track.is_active(): if self.app.track.hero_racestatus: counter = self.app.track.hero_racestatus['time'] minutes, seconds = divmod(abs(counter), 60) if counter < 0: ctx.move_to(16, 120) ctx.show_text(f"-{minutes:02d}:{seconds:02d}") else: ctx.move_to(28, 120) ctx.show_text(f"{minutes:03d}:{seconds:02d}") else: ctx.move_to(48, 120) ctx.show_text("--:--") else: ctx.move_to(100, 120) ctx.show_text("off") if self.app.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.app.track.hero_timedelta} seconds") x0 = 8 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.move_to(x1, y0) ctx.show_text(self.app.track.ttype) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Regatta") ctx.move_to(x1, y0) ctx.show_text(self.app.track.hero_raceid or '[not selected]') y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Course") ctx.move_to(x1, y0) if self.app.track.hero_orgstatus and self.app.track.hero_raceid: ctx.show_text(self.app.track.hero_orgstatus['races'][self.app.track.hero_raceid]['courseid']) else: ctx.show_text('[not selected]') y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Latitude") ctx.move_to(x1, y0) ctx.show_text(self.bv_lat.format()) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Longitude") ctx.move_to(x1, y0) ctx.show_text(self.bv_lon.format()) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Speed") ctx.move_to(x1, y0) ctx.show_text(self.bv_sog.format()) # Flaggen if self.app.track.hero_racestatus: pos = 0 for f in self.app.track.hero_racestatus['flags']: if f in self.flagmap: # TODO Context save/restore erforderlich? ctx.save() ctx.set_source_surface(self.sym_flag[self.flagmap[f]], *self.flagpos[pos]) ctx.paint() ctx.restore() pos += 1 def draw_config(self, ctx): ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(24) ctx.move_to(4, 42) ctx.show_text("Tracker configuration") # Linke Spalte mit Daten x0 = 8 # Labelspalte x1 = 88 # Datenspalte y0 = 75 yoffset = 16 # Bootsdaten ctx.set_font_size(20) ctx.move_to(x0, y0) ctx.show_text("Boat data") y0 += yoffset + 5 ctx.set_font_size(16) ctx.move_to(x0, y0) ctx.show_text("Name") ctx.move_to(x1, y0) ctx.show_text(self.cfg['boat']['name']) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Class") ctx.move_to(x1, y0) ctx.show_text(self.cfg['boat']['class']) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Handicap") ctx.move_to(x1, y0) ctx.show_text(str(self.cfg['boat']['handicap'])) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Club") ctx.move_to(x1, y0) ctx.show_text(self.cfg['boat']['club']) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Sailno.") ctx.move_to(x1, y0) ctx.show_text(self.cfg['boat']['sailno']) # Trackerdaten y0 += yoffset + 10 ctx.set_font_size(20) ctx.move_to(x0, y0) ctx.show_text("Tracker info") y0 += yoffset + 5 ctx.set_font_size(16) ctx.move_to(x0, y0) ctx.show_text("Type") ctx.move_to(x1, y0) ctx.show_text(self.app.track.ttype) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Org.") ctx.move_to(x1, y0) if self.app.track.hero_orgstatus: ctx.show_text(self.app.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.app.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) ctx.show_text(self.app.track.team) y0 += yoffset ctx.move_to(x0, y0) ctx.show_text("Server") ctx.move_to(x1, y0) if self.app.track.mqtt_connected: ctx.show_text("MQTT") else: ctx.show_text("offline") # Rechte Spalte mit Regattaauswahl x = 208 y = 75 yoffset = 16 # Mögliche Regatten self.races = self.app.track.hero_get_races() if len(self.races) == 1: self.hero_raceid = self.races[0] self.menupos = 1 ctx.set_font_size(20) ctx.move_to(x, y) ctx.show_text("Select Regatta") ctx.set_font_size(16) y += 4 if self.menupos > len(self.races): # Nichts auswählen self.menupos = 0 self.raceid = None i = 0 for r in self.races: i += 1 if r == self.raceid: r = f"\xbb {r} \xab" self.draw_text_boxed(ctx, x, y, 180, 20, r, (self.menupos == i)) y += 20 if i == 0: 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.app.track.hero_orgstatus or self.app.track.hero_orgstatus['message'] == '': ctx.move_to(8, 72) ctx.show_text("[ empty ]") else: lines = self.app.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) elif self.mode == 'C': self.draw_config(ctx) else: self.draw_info(ctx)