def remote_cb(self, cmd, nick, chan): """Handle unt message from remote (in main loop).""" if self.remoteuser and self.remoteuser.lower() != nick.lower(): return False if cmd.header == u'start': self.elapstart = tod.str2tod(cmd.text) elif cmd.header == u'finish': self.elapfin = tod.str2tod(cmd.text) elif cmd.header == u'redraw' and cmd.text == u'timer': # fake a timeout in the cb self.process_timeout() elif cmd.header == u'brightness': self.set_brightness(cmd.text) elif cmd.header == u'lapfin': self.lapfin = tod.str2tod(cmd.text) elif cmd.header == u'finpanel': fpvec = cmd.text.split(unichr(unt4.US)) self.ttrank = None self.ttno = None self.ttname = None self.ttcat = None self.tttime = None if len(fpvec) > 4: # rank/no/name/cat/time if fpvec[0]: self.ttrank = fpvec[0] if fpvec[1]: self.ttno = fpvec[1] if fpvec[2]: self.ttname = fpvec[2] if fpvec[3]: self.ttcat = fpvec[3] if fpvec[4]: self.tttime = fpvec[4] elif cmd.header == u'distance': self.distance = None try: a = float(cmd.text) if a > 0.1: self.distance = a except: self.log.debug(u'Invalid distance: ' + repr(cmd.text)) elif cmd.header == u'timerstat': self.timerstat = cmd.text elif cmd.header == u'timelimit': self.timelimit = tod.str2tod(cmd.text) elif cmd.header == u'clearall': self.reset() self.clearlines() elif cmd.erp: # general clearing self.reset() return False
def append_rider(self, msg): sr = msg.split(chr(unt4.US)) if len(sr) == 5: rftime = tod.str2tod(sr[4]) if rftime is not None: if len(self.riders) == 0: # Case 1: Starting a new lap if self.lapfin is not None: self.cur_split = self.lapfin else: self.cur_split = rftime if self.lapstart is not None: self.cur_lap = (self.cur_split - self.lapstart).truncate(0) elif self.elapstart: # no lap start? self.cur_lap = (self.cur_split - self.elapstart).truncate(0) else: self.cur_lap = self.cur_split.truncate(0) if self.final: # overwrite cur split for down times self.cur_split = rftime # check if down from here? self.cur_lap = rftime self.cur_bunchid = 0 self.cur_bunchcnt = 1 self.last_time = rftime nr = [ sr[0], sr[1], sr[2], sr[3], self.cur_lap.rawtime(0), self.cur_bunchcnt, COLOURMAP[self.cur_bunchid][0], rftime ] elif rftime < self.last_time or rftime - self.last_time < tod.tod( '5.0'): # Case 2: Same bunch self.last_time = rftime self.cur_bunchcnt += 1 nr = [ sr[0], sr[1], sr[2], sr[3], '', self.cur_bunchcnt, COLOURMAP[self.cur_bunchid][0], rftime ] else: # Case 3: New bunch self.riders.append( ['', '', '', '', '', '', '#fefefe', None]) self.cur_bunchid = (self.cur_bunchid + 1) % COLOURMAPLEN self.cur_bunchcnt = 1 self.last_time = rftime nr = [ sr[0], sr[1], sr[2], sr[3], '+' + (rftime - self.cur_split).rawtime(0), self.cur_bunchcnt, COLOURMAP[self.cur_bunchid][0], rftime ] else: # Informative non-timeline record nr = [sr[0], sr[1], sr[2], sr[3], '', '', '#fefefe', None] self.riders.append(nr) self.redraw_flag = True
def set_start(self, start='', lstart=None): """Set the race start.""" if type(start) is tod.tod: self.start = start if lstart is not None: self.lstart = lstart else: self.lstart = self.start else: self.start = tod.str2tod(start) if lstart is not None: self.lstart = tod.str2tod(lstart) else: self.lstart = self.start if self.start is None: pass else: if self.finish is None: self.setrunning()
def set_finish(self, finish=''): """Set the race finish.""" if type(finish) is tod.tod: self.finish = finish else: self.finish = tod.str2tod(finish) if self.finish is None: if self.start is not None: self.setrunning() else: if self.start is None: self.set_start('0') self.setfinished()
def inter_line(self, msg): sr = msg.split(chr(unt4.US)) if len(sr) == 5: if sr[1] != '': # got a rider et = tod.str2tod(sr[4]) ets = '' if et is not None: if '.' in sr[4]: ets = et.rawtime(2) else: ets = et.rawtime(0) + ' ' self.int_time.set_text(ets) rs = sr[0] if rs != '' and rs.isdigit(): rs += '.' self.int_rank.set_text(rs) self.int_rider.set_text(sr[1] + ' ' + sr[2] + ' ' + sr[3]) #self.int_box.show() else: # clearit self.int_rank.set_text('') self.int_rider.set_text('') self.int_time.set_text('')
def msg_cb(self, m, nick, chan): """Handle message packet in main thread.""" redraw = False if self.remoteuser and self.remoteuser.lower() != nick.lower(): #self.log.debug(u'Ignoring command from ' + repr(nick)) return False if m.header == 'rider': self.append_rider(m.text) if m.header == 'arrivalrow': self.append_arrival(m.text) elif m.header == 'finish': self.finish_line(m.text) elif m.header == 'ttsplit': self.inter_line(m.text) elif m.header == 'time': self.elap_lbl.set_text(m.text) elif m.header == 'title': self.lbl_header.set_text(m.text) elif m.header == 'start': self.cur_split = tod.str2tod(m.text) elif m.erp: self.clear() return False
def append_rider(self, msg): sr = msg.split(chr(unt4.US)) if len(sr) == 5: rs = sr[0] if rs != '' and rs.isdigit(): rs += '.' rftime = tod.str2tod(sr[4]) if rftime is not None: if len(self.riders) == 0: # Case 1: Starting a new cat self.cur_cat = rftime nr=[rs,sr[1],sr[2],sr[3],rftime.rawtime(2),'',None] else: # Case 2: down on leader downtime = rftime - self.cur_cat self.last_time = rftime nr=[rs,sr[1],sr[2],sr[3],rftime.rawtime(2), '+' + downtime.rawtime(1),None] else: # Informative non-timeline record nr=[sr[0],sr[1],sr[2],sr[3], '', '', None] self.riders.append(nr)
def loadconfig(self): """Load race config from disk.""" self.contests.clear() deftimetype = '200m' defdistance = '200' defdistunits = 'metres' self.seedsrc = None # default is no seed info cr = jsonconfig.config({ u'event': { u'id': EVENT_ID, u'contests': ['Heat 1', 'Heat 2', 'Heat 3'], u'timerstat': None, u'showinfo': True, u'comment': [], u'autospec': u'' }, u'contests': {} }) cr.add_section(u'event') cr.add_section(u'contests') try: with open(self.configpath, 'rb') as f: cr.read(f) except Exception as e: self.log.error(u'Reading event config: ' + unicode(e)) # event metas self.info_expand.set_expanded( strops.confopt_bool(cr.get(u'event', u'showinfo'))) #self.autospec = cr.get(u'event', u'autospec') self.comment = cr.get(u'event', u'comment') self.onestart = False self.set_elapsed() #places = strops.reformat_placelist(cr.get(u'event',u'ctrl_places')) #self.ctrl_places.set_text(places) #self.placexfer(places) #if places: # self.doscbplaces = False # only show places on board if not set # self.setfinished() #else: if self.autospec: self.meet.autostart_riders(self, self.autospec, infocol=self.seedsrc) if self.evtype in ['handicap', 'keirin'] or self.inomnium: self.reorder_handicap() # restore contests oft = 0 curactive = -1 for c in cr.get(u'event', u'contests'): if cr.has_option(u'contests', c): res = cr.get(u'contests', c) ft = tod.str2tod(res[4]) if ft or res[5]: self.onestart = True # at least one run so far else: if curactive == -1: curactive = oft astr = '' if res[0]: astr = self.rider_name(res[0]) bstr = '' if res[2]: bstr = self.rider_name(res[2]) nr = [ c, res[0], astr, res[1], res[2], bstr, res[3], ft, res[5], res[6] ] self.add_contest(c, nr) else: self.add_contest(c) oft += 1 if not self.onestart and self.autospec: self.del_riders() self.meet.autostart_riders(self, self.autospec) self.current_contest_combo.set_active(curactive) eid = cr.get('event', 'id') if eid and eid != EVENT_ID: self.log.error('Event configuration mismatch: ' + repr(eid) + ' != ' + repr(EVENT_ID))
def loadconfig(self): """Load app config from disk.""" cr = jsonconfig.config({ u'voladisp': { u'id': '', u'port': u'', u'brightness': u'1', u'remoteport': u'', u'remotechan': u'', u'remoteuser': u'', u'loglevel': unicode(logging.INFO), u'maxlaptime': u'4:00' } }) cr.add_section(u'voladisp') cwfilename = metarace.default_file(CONFIGFILE) cr.merge(metarace.sysconf, u'voladisp') # re-set log file if self.loghandler is not None: self.log.removeHandler(self.loghandler) self.loghandler.close() self.loghandler = None self.loghandler = logging.FileHandler( os.path.join(self.configpath, LOGFILE)) self.loghandler.setLevel(LOGHANDLER_LEVEL) self.loghandler.setFormatter( logging.Formatter( '%(asctime)s %(levelname)s:%(name)s: %(message)s')) self.log.addHandler(self.loghandler) # check for config file try: with open(cwfilename, 'rb') as f: cr.read(f) except Exception as e: self.log.error(u'Reading app config: ' + repr(e)) # set uSCBsrv connection self.remoteuser = cr.get(u'voladisp', u'remoteuser') self.remotechan = cr.get(u'voladisp', u'remotechan') self.remoteport = cr.get(u'voladisp', u'remoteport') self.remote.set_portstr(portstr=self.remoteport, channel=self.remotechan) if self.remoteuser: self.log.info(u'Enabled remote control by: ' + repr(self.remoteuser)) else: self.log.info(u'Promiscuous remote control enabled.') # set display serial port self.port = cr.get(u'voladisp', u'port') self.reconnect_display() # check the maximum lap time field mlap = tod.str2tod(cr.get(u'voladisp', u'maxlaptime')) if mlap is not None: self.maxlaptime = mlap # set display birightness self.set_brightness( strops.confopt_posint(cr.get(u'voladisp', u'brightness'), 1)) cid = cr.get(u'voladisp', u'id') if cid and cid != APP_ID: self.log.error(u'Meet configuration mismatch: ' + repr(cid) + u' != ' + repr(APP_ID))
def loadconfig(self): """Load config from disk.""" cr = jsonconfig.config({u'irtt_start':{ u'uscbsrv':'', u'channel':USCBSRV_CHANNEL, u'fullscreen':False, u'timer':'', u'backlightlow':0.25, u'backlighthigh':1.0, u'backlightdev':None, u'syncthresh':DEFAUDIOSYNCTHRESH, u'startlist':u'startlist.csv'}}) cr.add_section(u'irtt_start') # check for config file cfile = metarace.default_file(CONFIGFILE) # read in sysdefaults before checking for config file cr.merge(metarace.sysconf, u'irtt_start') try: self.log.info(u'Reading config from: ' +repr(cfile)) with open(cfile, 'rb') as f: cr.read(f) except Exception as e: self.log.error(u'Reading config: ' + unicode(e)) if strops.confopt_bool(cr.get(u'irtt_start', u'fullscreen')): self.window.fullscreen() # set timer port tport = cr.get(u'irtt_start', u'timer') self.timer.setport(tport) self.timer.sane() self.timer.keylock() self.timer.setcb(self.starttrig) self.timer.armlock(True) # always re-armlock self.timer.arm(timy.CHAN_START) # load backlight parameters self.backlightdev = cr.get(u'irtt_start', u'backlightdev') self.backlightlow = strops.confopt_float(cr.get(u'irtt_start', u'backlightlow'),0.25) self.backlighthigh = strops.confopt_float(cr.get(u'irtt_start', u'backlighthigh'),1.0) if os.path.exists(self.backlightdev): try: with open(os.path.join(self.backlightdev,u'max_brightness'), 'rb') as bf: mbstr = bf.read() self.backlightmax = strops.confopt_posint(mbstr,20) self.backlightdev = os.path.join(self.backlightdev, u'brightness') self.log.info(u'Using backlight dev ' + repr(self.backlightdev) + u'; Max={0}, Low={1}%, High={2}%'.format( self.backlightmax, int(100.0*self.backlightlow), int(100.0*self.backlighthigh))) except Exception as e: self.log.error(u'Reading from backlight device: ' + repr(e)) self.backlightdev = None else: self.log.info(u'Backlight control not configured.') self.backlightdev = None # audio sync thresh self.syncthresh = strops.confopt_posint( cr.get(u'irtt_start', u'syncthresh'), DEFAUDIOSYNCTHRESH) self.log.info(u'Sync threshold set to: {0:0.3f}s'.format( float(self.syncthresh)*1e-9)) # set sender port nhost = cr.get(u'irtt_start', u'uscbsrv') nchannel = cr.get(u'irtt_start', u'channel') self.scb.set_portstr(nhost, nchannel) # load riders datafile = cr.get(u'irtt_start', u'startlist') try: rlist = [] with open(datafile,'rb') as f: cr = ucsv.UnicodeReader(f) for r in cr: key = None st = None bib = u'' series = u'' name = u'' next = None # load rider info # start, no, series, name, cat if len(r) > 0 and r[0] not in NOHDR: # time & no provided st = tod.str2tod(r[0]) if len(r) > 1: # got bib bib = r[1] if len(r) > 2: # got series series = r[2] if len(r) > 3: # got name name = u' '.join([r[1],r[3]]) if st is not None: # enough data to add a starter key = tod2key(st) nr = [st, bib, series, name, next] self.ridermap[key] = nr rlist.append(key) # sort startlist and build list linkages curoft = tod2key(tod.tod(u'now')) self.currider = None rlist.sort() prev = None for r in rlist: if prev is not None: self.ridermap[prev][4] = r # prev -> next prev = r if self.currider is None and r > curoft: self.currider = r rvec = self.ridermap[r] stxt = tod.tod(r).meridian() sno = rvec[1] sname = rvec[3] self.log.info(u'Setting first rider to: ' + u','.join([sno, sname]) + u' @ ' + stxt) # last link will be None except Exception as e: # always an error - there must be startlist to continue self.log.error(u'Error loading from startlist: ' + unicode(e))
def __init__(self, linelen=28): """Constructor.""" threading.Thread.__init__(self) self.running = False self.debug = False self.il = None self.localsrv = False self.rdbuf = {} # source -> dest -> buf[] self.pub_cb = None self.iohandle = {} self.addchans = set() ## HACK self.linelen = linelen self.pagelen = 7 self.encoding = 'utf-8' self.log = logging.getLogger('telegraph') self.log.setLevel(logging.DEBUG) try: import irclib # CHECK: 16.2.9. "all import attempts must be completed # before the interpreter starts shutting itself down." self.ih = irclib.IRC(fn_to_add_socket=self._addsock, fn_to_remove_socket=self._delsock) self.il = irclib except ImportError: self.log.warn(u'irclib not present: Telegraph will not function.') self.ih = fakeirc() self.ic = self.ih.server() self.ping_interval = PING_INTERVAL self.np = tod.tod('now') + self.ping_interval self.name = 'telegraph' self.chanstatus = False self.nick = TELEGRAPH_USER + unicode(random.randint(1000, 9999)) self.host = TELEGRAPH_HOST self.port = TELEGRAPH_PORT self.username = TELEGRAPH_USERNAME self.fullname = TELEGRAPH_FULLNAME self.channel = TELEGRAPH_CHANNEL self.srvid = TELEGRAPH_FAKEHOST self.privateservers = TELEGRAPH_PRIVATESERVERS self.opername = TELEGRAPH_OPERNAME self.operpass = TELEGRAPH_OPERPASS self.doreconnect = False self.connect_pending = False self.dumbcnt = 0 self.curov = None # allow use as a sender #self.linelen = linelen self.queue = Queue.Queue() # check system config for overrides if metarace.sysconf.has_option(u'telegraph', u'username'): self.username = metarace.sysconf.get(u'telegraph', u'username') if metarace.sysconf.has_option(u'telegraph', u'fullname'): self.fullname = metarace.sysconf.get(u'telegraph', u'fullname') if metarace.sysconf.has_option(u'telegraph', u'channel'): self.channel = metarace.sysconf.get(u'telegraph', u'channel') if metarace.sysconf.has_option(u'telegraph', u'privateservers'): self.privateservers = metarace.sysconf.get(u'telegraph', u'privateservers') if metarace.sysconf.has_option(u'telegraph', u'opername'): self.opername = metarace.sysconf.get(u'telegraph', u'opername') if metarace.sysconf.has_option(u'telegraph', u'operpass'): self.operpass = metarace.sysconf.get(u'telegraph', u'operpass') if metarace.sysconf.has_option(u'sender', u'pagelen'): self.pagelen = strops.confopt_posint( metarace.sysconf.get(u'sender', u'pagelen'), self.pagelen) if metarace.sysconf.has_option(u'telegraph', u'ping_interval'): ck = tod.str2tod( metarace.sysconf.get(u'telegraph', u'ping_interval')) if ck is not None: self.ping_interval = ck
def loadconfig(self): """Load config from disk.""" cr = jsonconfig.config({ u'road_announce': { u'remoteport': u'', u'remoteuser': u'', u'remotechan': u'#agora', u'autoscroll': False, u'timetick': 12, u'fontsize': 20, u'minelevrange': 300.0, u'fullscreen': False, u'groupcol': True, u'catcol': True, u'bunchmap': True, u'showtrack': False, u'search': False, u'maxlaptime': '2:00', u'motd': MOTD } }) cr.add_section(u'road_announce') cwfilename = metarace.default_file(CONFIGFILE) # read in sysdefaults before checking for config file cr.merge(metarace.sysconf, u'road_announce') # re-set log file if self.loghandler is not None: self.log.removeHandler(self.loghandler) self.loghandler.close() self.loghandler = None self.loghandler = logging.FileHandler( os.path.join(self.configpath, LOGFILE)) self.loghandler.setLevel(LOGHANDLER_LEVEL) self.loghandler.setFormatter( logging.Formatter( '%(asctime)s %(levelname)s:%(name)s: %(message)s')) self.log.addHandler(self.loghandler) # check for config file try: with open(cwfilename, 'rb') as f: cr.read(f) except Exception as e: self.log.error(u'Reading app config: ' + repr(e)) self.timetick = strops.confopt_posint( cr.get(u'road_announce', u'timetick'), TIMETICK) self.fontsize = strops.confopt_posint( cr.get(u'road_announce', u'fontsize'), FONTSIZE) maxlap = tod.str2tod(cr.get(u'road_announce', u'maxlaptime')) if maxlap is not None: self.maxlaptime = maxlap self.motd = cr.get(u'road_announce', u'motd') if strops.confopt_bool(cr.get(u'road_announce', u'fullscreen')): self.window.fullscreen() if strops.confopt_bool(cr.get(u'road_announce', u'search')): self.search_ent.show() self.search_ent.set_sensitive(True) self.showtrack = strops.confopt_bool( cr.get(u'road_announce', u'showtrack')) if self.showtrack: self.track_area.show() autoscroll = strops.confopt_bool( cr.get(u'road_announce', u'autoscroll')) if autoscroll != self.autoscroll: self.autoscroll = True self.scrollcnt = 0 glib.timeout_add(SCROLLDELAY, self.doautoscroll) self.remoteport = cr.get(u'road_announce', u'remoteport') self.remotechan = cr.get(u'road_announce', u'remotechan') self.remoteuser = cr.get(u'road_announce', u'remoteuser') self.io.set_portstr(portstr=self.remoteport, channel=self.remotechan) if self.remoteuser: self.log.info(u'Enabled remote control by: ' + repr(self.remoteuser)) else: self.log.info(u'Promiscuous remote control enabled.') if not strops.confopt_bool(cr.get(u'road_announce', 'groupcol')): self.view.get_column(5).set_visible(False) if not strops.confopt_bool(cr.get(u'road_announce', 'catcol')): self.view.get_column(3).set_visible(False) if not strops.confopt_bool(cr.get(u'road_announce', 'bunchmap')): self.map_area.hide() fnszstr = str(self.fontsize) + 'px' self.lbl_header.modify_font( pango.FontDescription(FONTFACE + ' ' + fnszstr)) self.elap_lbl.modify_font( pango.FontDescription(FONTFACE + ' ' + fnszstr)) self.gap_lbl.modify_font( pango.FontDescription(FONTFACE + ' ' + fnszstr)) self.search_entry.modify_font( pango.FontDescription(FONTFACE + ' ' + fnszstr)) self.lbl_resultmsg.modify_font( pango.FontDescription(FONTFACE + ' ' + fnszstr)) self.view.modify_font(pango.FontDescription(FONTFACE + ' ' + fnszstr)) zrangemin = strops.confopt_float( cr.get(u'road_announce', u'minelevrange'), 200.0) routefile = metarace.default_file(u'route.dat') self.profile = None self.zmin = None self.zmax = None self.dmax = None if os.path.exists(routefile): print(u'loading profile...') self.profile = [] with open(routefile, 'rb') as f: dmax = 0.0 xmin = -1.0 xmax = 1.0 ymin = -1.0 ymax = 1.0 zmin = 90000.0 zmax = -100.0 for l in f: r = l.split() t = strops.confopt_float(r[1]) x = strops.confopt_float(r[2]) y = strops.confopt_float(r[3]) z = strops.confopt_float(r[4]) d = strops.confopt_float(r[6]) if z < zmin: zmin = z if z > zmax: zmax = z if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y if d > dmax: dmax = d self.profile.append([t, z, d, x, y]) self.zmin = zmin self.zmax = zmax self.xmin = xmin self.xmax = xmax self.ymin = ymin self.ymax = ymax self.dmax = dmax erange = zmax - zmin # profile min elevation range if erange < zrangemin: self.zmin -= 0.5 * (zrangemin - erange) self.zmax = self.zmin + zrangemin #print(repr([zmin,zmax,dmax])) routemeta = metarace.default_file(u'route.json') rjdat = jsonconfig.config() if os.path.exists(routemeta): with open(routemeta, 'rb') as g: rjdat.read(g) bbmin = rjdat.get(u'route', u'mapmin') bbmax = rjdat.get(u'route', u'mapmax') self.track_bbox = [[bbmin[u'x'], bbmin[u'y']], [bbmax[u'x'], bbmax[u'y']]]
def msg_cb(self, m, nick, chan): """Handle message packet in main thread.""" if self.remoteuser and self.remoteuser.lower() != nick.lower(): #self.log.debug(u'Ignoring command from ' + repr(nick)) return False if m.header == 'rider': self.append_rider(m.text) elif m.header == 'title': if m.text: self.title_str = m.text else: self.title_str = None elif m.header == 'finstr': if m.text: self.finstr = m.text else: self.finstr = None elif m.header == 'start': self.elapstart = tod.str2tod(m.text) elif m.header == 'finish': self.elapfin = tod.str2tod(m.text) elif m.header == 'lapstart': self.lapstart = tod.str2tod(m.text) elif m.header == 'lapfin': self.lapfin = tod.str2tod(m.text) elif m.header == 'resultmsg': self.lbl_resultmsg.set_text(m.text) if m.text: self.lbl_resultmsg.show() else: self.lbl_resultmsg.hide() elif m.header == u'laplbl': if m.text: self.laplbl = m.text else: self.laplbl = None # force None for empty string elif m.header == u'laptype': if m.text: self.laptype = m.text else: self.laptype = None # force None for empty string elif m.header == u'oncourse': self.lambert_fix(m.text) elif m.header == u'leadpos': self.projectpos(u'lead', m.text) elif m.header == u'mainpos': self.projectpos(u'main', m.text) elif m.header == u'chasepos': self.projectpos(u'chase', m.text) elif m.header == u'sagpos': self.projectpos(u'sag', m.text) elif m.header == u'distance': self.distance = None try: a = float(m.text) if a > 0.1: self.distance = a self.redraw_flag = True except: self.log.debug(u'Invalid lead distance: ' + repr(m.text)) elif m.header == u'distancemain': self.distancemain = None try: a = float(m.text) if a > 0.1: self.distancemain = a self.redraw_flag = True # might get two at a time except: self.log.debug(u'Invalid main distance: ' + repr(m.text)) elif m.header == u'timerstat': self.timerstat = m.text elif m.header == u'bunches': self.final = True elif m.erp: self.clear() return False