def __init__(self): """Constructor.""" threading.Thread.__init__(self, daemon=True) self.__queue = queue.Queue() self.__cb = defcallback self.__subscriptions = set() self.__curov = None self.__deftopic = None self.__connected = False self.__connect_pending = False self.__host = None self.__qos = 0 self.__doreconnect = True # if host set, try to connect on startup # check system config for overrides if metarace.sysconf.has_option('telegraph', 'host'): self.__host = metarace.sysconf.get('telegraph', 'host') if metarace.sysconf.has_option('telegraph', 'deftopic'): # note: this may be overidden by application self.__deftopic = metarace.sysconf.get('telegraph', 'deftopic') if metarace.sysconf.has_option('telegraph', 'qos'): self.__qos = strops.confopt_posint(metarace.sysconf.get( 'telegraph', 'qos'),0) if self.__qos > 2: LOG.info('Invalid QOS %r set to %r', self.__qos, 2) self.__qos = 2 # create mqtt client self.__client = mqtt.Client() if metarace.sysconf.has_option('telegraph', 'debug'): if strops.confopt_bool(metarace.sysconf.get('telegraph', 'debug')): LOG.debug('Enabling mqtt/paho debug') mqlog = logging.getLogger('metarace.telegraph.mqtt') mqlog.setLevel(logging.DEBUG) self.__client.enable_logger(mqlog) if metarace.sysconf.has_option('telegraph', 'usetls'): if strops.confopt_bool(metarace.sysconf.get('telegraph', 'usetls')): LOG.debug('Enabling TLS connection') self.__client.tls_set() username = None password = None if metarace.sysconf.has_option('telegraph', 'username'): username = metarace.sysconf.get('telegraph', 'username') if metarace.sysconf.has_option('telegraph', 'password'): password = metarace.sysconf.get('telegraph', 'password') if username and password: self.__client.username_pw_set(username, password) self.__client.reconnect_delay_set(2, 16) self.__client.on_message = self.__on_message self.__client.on_connect = self.__on_connect self.__client.on_disconnect = self.__on_disconnect self.__running = False
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'fullscreen':False, u'groupcol':True, u'catcol':True, u'bunchmap':True, 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.fontsize = strops.confopt_posint(cr.get(u'road_announce', u'fontsize'), FONTSIZE) fnszstr = str(self.fontsize)+'px' self.font = pango.FontDescription(FONTFACE + ' ' + fnszstr) self.motd = cr.get(u'road_announce', u'motd') if strops.confopt_bool(cr.get(u'road_announce', u'fullscreen')): self.window.fullscreen() 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.') self.lbl_header.modify_font(self.font) self.elap_lbl.modify_font(self.font) self.search_lbl.modify_font(self.font) self.search_entry.modify_font(self.font) self.view.modify_font(self.font) self.iview.modify_font(self.font) self.fin_rank.modify_font(self.font) self.fin_label.modify_font(self.font) self.fin_rider.modify_font(self.font) self.fin_time.modify_font(self.font) self.int_rank.modify_font(self.font) self.int_label.modify_font(self.font) self.int_rider.modify_font(self.font) self.int_time.modify_font(self.font)
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 msg_cb(self, c, e): """Handle privmsg.""" source = self.il.nm_to_n(e.source()).lower() dest = e.target().lower() body = ''.join(e.arguments()) # RCV bytes! # determine receive stream and append to appropriate buffer if self.pub_cb is not None: # add buffer if required if source not in self.rdbuf: self.rdbuf[source] = {} sbuf = self.rdbuf[source] if dest not in sbuf: sbuf[dest] = '' sbuf[dest] += body # append payload to relevant buffer while len(sbuf[dest]) > 0: # attempt to process current buffer content if sbuf[dest][0] == 'J': # process the buffer as a JSON report joft = sbuf[dest].find('{') blen = len(sbuf[dest]) if joft > 6: # implies blen > 0 # enough length for sig and buflen, start decode mvec = sbuf[dest].split(None, 3) if len(mvec) == 4: dlen = strops.confopt_posint(mvec[2]) if len(mvec[3]) >= dlen: data = mvec[3][0:dlen] dsig = self.message_sig(data) if dsig == mvec[1]: try: o = json.loads(data) glib.idle_add(self.pub_cb, o, source, dest) except Exception as e: self.log.error(u'Error reading ob: ' + repr(e)) else: self.log.info(u'Invalid signature: ' + repr(sbuf[dest])) nstart = joft + dlen sbuf[dest] = sbuf[dest][nstart:] else: # awaiting more data break else: self.log.info( u'Skipping malformed report packet: ' + repr(sbuf[dest])) sbuf[dest] = sbuf[dest][1:] # chop off marker # terminal else: if len(sbuf[dest]) > 40: self.log.info( u'Skipping malformed report packet: ' + repr(sbuf[dest])) sbuf[dest] = sbuf[dest][1:] # chop off marker elif sbuf[dest][0] == '<': data = unt4.decode(sbuf[dest]) # attempt to decode as UNT4 encoded list oidx = data.find(chr(unt4.SOH)) eidx = data.find(chr(unt4.EOT)) # process one chunk and then unravel if oidx >= 0 and eidx >= 0 and eidx > oidx: msgtxt = data[oidx:eidx + 1] glib.idle_add( self.pub_cb, unt4.unt4( unt4str=msgtxt.decode('utf-8', 'replace')), source, dest) datlen = len(unt4.encode(data[0:eidx + 1])) sbuf[dest] = sbuf[dest][datlen:] elif eidx >= 0: # discard partial msg (not yet complete) datlen = len(unt4.encode(data[0:eidx + 1])) sbuf[dest] = sbuf[dest][datlen:] else: # awaiting more data break else: # assume buffer contains a complete utf8 command list glib.idle_add(self.pub_cb, sbuf[dest].decode('utf-8', 'ignore').split(), source, dest) sbuf[dest] = '' # truncate buffer
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']]]