def set_elapsed(self): """Update elapsed time in race ui and announcer.""" self.curelap = None if self.start is not None and self.finish is not None: et = self.finish - self.start self.time_lbl.set_text(et.timestr(2)) self.curelap = et msg = unt4.unt4(header=unichr(unt4.DC3) + u'N F$', xx=0, yy=0, text=et.timestr(2)[0:12]) self.meet.udptimer.sendto(msg.pack(), (self.meet.udpaddr, 6789)) elif self.start is not None: # Note: uses 'local start' for RT runtm = (tod.tod('now') - self.lstart).timestr(1) ## UDP hack msg = unt4.unt4(header=unichr(unt4.DC3) + u'R F$', xx=0, yy=0, text=runtm[0:12]) self.meet.udptimer.sendto(msg.pack(), (self.meet.udpaddr, 6789)) self.time_lbl.set_text(runtm) elif self.timerstat == 'armstart': self.time_lbl.set_text(tod.tod(0).timestr(1)) else: self.time_lbl.set_text('')
def timeout(self, data=None): """Handle timeout.""" # 1: Terminate? if not self.running: return False # 2: Process? try: ntime = tod.tod(u'now') ntod = ntime.truncate(0) if ntime >= self.nc.truncate(1): self.tod = ntod self.nc += tod.ONE self.process_timeout() else: self.log.debug(u'Timeout called early: ' + ntime.rawtime()) # no need to advance, desired timeout not yet reached except Exception as e: self.log.error(u'Timeout: ' + unicode(e)) # 3: Re-Schedule tt = tod.tod(u'now')+tod.tod(u'0.01') while self.nc < tt: # ensure interval is positive if tod.MAX - tt < tod.ONE: self.log.debug(u'Midnight rollover.') break self.log.debug(u'May have missed an interval, catching up.') self.nc += tod.ONE # 0.01 allows for processing delay ival = int(1000.0 * float((self.nc - tod.tod(u'now')).timeval)) glib.timeout_add(ival, self.timeout) # 4: Return False return False # must return False
def __init__(self): # logger and handler self.log = logging.getLogger() self.log.setLevel(logging.DEBUG) self.loghandler = logging.FileHandler(LOGFILE) self.loghandler.setLevel(logging.DEBUG) self.loghandler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s:%(name)s: %(message)s')) self.log.addHandler(self.loghandler) self.log.info(u'IRTT Starter - Init.') # require one timy and one uscbsrv self.timer = timy.timy() self.scb = telegraph.telegraph() self.started = False self.running = True # Audio output self.player = gst.element_factory_make("playbin2", "player") self.player.set_property("audio-sink", gst.element_factory_make("alsasink", "sink")) self.player.set_property("video-sink", gst.element_factory_make("fakesink", "fakesink")) bus = self.player.get_bus() bus.add_signal_watch() bus.connect("message", self.gst_message) self.player.set_property('uri', u'file://' + os.path.join(metarace.DB_PATH, u'start.wav')) # variables self.armed = False self.width = 0 self.height = 0 self.backlight = 0.0 self.backlightmax = 20 self.backlightdev = None self.backlightlow = 0.25 self.backlighthigh = 1.0 self.syncthresh = 100000000 self.tod = tod.tod(u'now').truncate(0) self.nc = self.tod + tod.tod(u'1.22') # set interval a little off mark self.countdown = None self.riderstr = None self.bulb = None self.currider = None self.ridermap = {} self.window = gtk.Window() self.window.set_title(u'Start Clock') self.window.connect('destroy', self.window_destroy_cb) self.area_src = None self.area = gtk.DrawingArea() self.area.connect('configure_event', self.area_configure_event_cb) self.area.connect('expose_event', self.area_expose_event_cb) self.area.set_size_request(400,220) self.area.show() self.window.add(self.area) self.log.info(u'Starting clock intervals at: ' + self.nc.rawtime(3)) glib.timeout_add(2000, self.timeout) glib.timeout_add_seconds(5, self.delayed_cursor)
def __init__(self, configpath=None): """App constructor.""" # logger and log handler self.log = logging.getLogger() self.log.setLevel(logging.DEBUG) self.loghandler = None # set in loadconfig to meet dir # meet configuration path and options if configpath is None: configpath = u'.' # None assumes 'current dir' self.configpath = configpath self.loglevel = logging.INFO # UI log window # hardware connections self.remote = telegraph.telegraph() self.remoteuser = u'' # match against remote nick self.remoteport = u'' # only connect if requested self.remotechan = u'#announce' self.remote.set_pub_cb(self.remote_cb) self.port = u'' # re-set in loadconfig self.scb = None self.remote_enable = True self.obuf = [] # current output buffer for j in range(0, 2): self.obuf.append(u''.ljust(10)) self.set_remote_enable() # run state self.running = True self.started = False self.tod = tod.tod(u'now').truncate(0) self.nc = self.tod + tod.tod(u'1.22') # set interval a little off mark self.maxlaptime = tod.tod('2:00') # default maximum lap time # animation variables self.ttrank = None self.ttno = None self.ttname = None self.ttcat = None self.tttime = None self.elapstart = None self.elapfin = None self.timerstat = u'running' # default assume run state self.timelimit = None self.distance = None self.lapfin = None self.timeofday = True # show timeofday on bottom line? self.failcount = 0 self.failthresh = 30 # connect timeout ~30sec # start timer self.log.debug(u'Starting clock intervals at: ' + self.nc.rawtime(3)) glib.timeout_add(2000, self.timeout)
def __init__(self, port=None, name='rru'): """Construct thread object. Named parameters: port -- serial port name -- text identifier for use in log messages """ threading.Thread.__init__(self) self.name = name self.port = None self.looppower = 20 self.loopid = 1 self.unitno = u'rru' self.loopchannel = 2 self.lastsync = tod.tod(u'now') self.lastts = 0 self.error = False self.errstr = '' self.cqueue = Queue.Queue() # command queue self.log = logging.getLogger(self.name) self.log.setLevel(logging.DEBUG) self.__rdbuf = '' self.setcb() if port is not None: self.setport(port)
def starttrig(self, e): """React to start trigger.""" if self.timerstat == 'armstart': self.start = e self.lstart = tod.tod('now') self.setrunning() glib.timeout_add_seconds(4, self.armfinish)
def clearplaces(self): """Zero internal model for recalculate.""" for r in self.riders: r[COL_PLACE] = '' r[COL_TOTAL] = 0 r[COL_TIME] = tod.tod(0) r[COL_POINTS] = []
def __command(self, m): """Process a command out of the command queue.""" if type(m) is tuple and type(m[0]) is str: if m[0] == 'WRITE': if len(m) == 3: self.__write(m[1], m[2]) elif m[0] == 'PASSING': if len(m) == 3: if self.__cb is not None: self.__cb(m[2], m[1], self.__cbdata) else: self.log.info(u'PASS: '******' :: ' + m[2]) elif m[0] == 'STATUSACK': if len(m) == 3: if self.__statuscb is not None: self.__statuscb(m[2], m[1], self.__cbdata) else: self.log.debug(u'STATUS: ' + unicode(m[1]) + u' :: ' + m[2]) elif m[0] == 'SYNC': # broadcast or direct a rough sync command dst = self.broadcast if m[1] is not None: dst = m[1] t = tod.tod('now') while t-t.truncate(0) > tod.tod('0.02'): t = tod.tod('now') self.__write(self.__set_time_cmd(t), dst) elif m[0] == 'ALLSTAT': self.__write(STATCMD, self.broadcast) elif m[0] == 'IPCONFIG': if len(m) == 3: self.__ipconfigset(m[1], m[2]) elif m[0] == 'CONFIG': if len(m) == 3: self.__configset(m[1], m[2]) elif m[0] == 'ADD': if len(m) == 3: self.__add(m[1], m[2]) elif m[0] == 'REMOVE': if len(m) == 2: self.__remove(m[1]) else: pass else: self.log.warn(u'Unknown command: ' + repr(m))
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 process_timeout(self): """Perform required timeout activities.""" estr = u'' dstr = u'' if self.elapstart is not None: if self.elapfin is not None: # Race over - show elap and down if poss estr = (self.elapfin - self.elapstart).rawtime(0) if self.timerstat != u'finished': if self.tod > self.elapfin: dstr = u'+' + (self.tod - self.elapfin).rawtime(0) else: # race in progress, show run time and distance or lap elap = self.tod - (self.elapstart + tod.tod('0.2')) if elap > tod.MAXELAP: elap = tod.ZERO estr = elap.rawtime(0) if self.distance is not None and self.distance > 0.5: dstr = u'~{0:1.1f}km'.format(self.distance) if self.lapfin is not None: # lap down time overwrites dist, but only if valid laptm = self.tod - self.lapfin if laptm < self.maxlaptime: dstr = u'+' + laptm.rawtime(0) self.elap_lbl.set_text(estr) self.gap_lbl.set_text(dstr) #if self.timerstat == 'running' and self.laplbl: #tmsg = u'' #if self.finstr: #tmsg = self.finstr + u' ' ## add lap and sprint info #tmsg += self.laplbl #if self.laptype: #tmsg += u' - ' + self.laptype #self.lbl_header.set_text(tmsg) #else: if True: # always show the title_str, it now has better info if self.title_str: self.lbl_header.set_text(self.title_str) if self.redraw_flag: self.redraw_flag = False self.map_redraw() # update src map self.track_redraw() # update src map self.map_area.queue_draw() # queue copy to screen self.track_area.queue_draw() # queue copy to screen # check connection status if not self.io.connected(): self.failcount += 1 if self.failcount > self.failthresh: self.io.set_portstr(force=True) self.failcount = 0 self.log.debug(u'Telegraph connection failed, count = ' + repr(self.failcount)) else: self.failcount = 0
def draw_ridermap(self, cr): width = self.map_winsz height = 80 cr.identity_matrix() # bg filled cr.set_source_rgb(0.85, 0.85, 0.9) cr.paint() # scale: | . . . . i . . . . | . . . cr.set_line_width(1.0) cr.set_font_size(15.0) xof = 0 dw = width - (2 * MAPHMARGIN) dh = height - (2 * MAPVMARGIN) cnt = 0 while xof < dw: lh = 4 if cnt % 10 == 0: lh = 12 cr.set_source_rgb(0.05, 0.05, 0.05) cr.move_to(xof + MAPHMARGIN + 1, MAPVMARGIN + dh - lh - 2) cr.show_text(tod.tod(int(cnt)).rawtime(0)) elif cnt % 5 == 0: lh = 8 cr.set_source_rgb(0.05, 0.05, 0.05) cr.move_to(xof + MAPHMARGIN, MAPVMARGIN + dh - lh) cr.line_to(xof + MAPHMARGIN, MAPVMARGIN + dh) cr.stroke() if cnt % 5 == 0: cr.set_source_rgb(0.96, 0.96, 0.96) cr.move_to(xof + MAPHMARGIN, MAPVMARGIN) cr.line_to(xof + MAPHMARGIN, MAPVMARGIN + dh - lh - 2) cr.stroke() cnt += 1 xof += self.timetick cr.set_line_width(2.0) inbox = False cnt = 0 st = None x1 = None x2 = None for r in self.riders: if r[7] is not None: # have a row if st is None: st = r[7].truncate(0) # save lap split if not inbox: x1 = r[7].truncate(0) - st inbox = True x2 = r[7] - st else: # have a break if inbox: self.do_bubble(cr, cnt, x1, x2) cnt += 1 inbox = False if inbox: self.do_bubble(cr, cnt, x1, x2)
def procmsg(self, msg): """Read IPX wheeltime event and insert as tod into command queue.""" s = msg.strip() if (len(s) == 36 or len(s) == 38) and s[1] == 'a': sum = ipico_lrc(s) lrc = int(s[34:36], 16) if sum == lrc: #tagid=s[4:16] ## NOTE: Using 'shortform' tag ids if s[4:10] == TAGPREFIX: # match id prefix tagid=(s[10:16]).lower() timestr = '{0}:{1}:{2}.{3:02}'.format(s[26:28], s[28:30], s[30:32], int(s[32:34], 16)) istr = '00:0' # emulate THbC info, but zero for WT chanstr = 'BOX' t = tod.tod(timestr, index=istr, chan=chanstr, refid=tagid, source=self.srcid) if self.cb is not None: glib.idle_add(self.cb, t) self.cqueue.put_nowait(('RFID', t)) # notify only now else: self.log.warn('Spurious tag id: ' + s[4:10] + ' :: ' + s[10:16]) else: self.log.warn('Incorrect char sum message skipped: ' + hex(sum) + ' != ' + hex(lrc)) elif len(s) == 30 and s[0:8] == 'ab010a2c': # Process a trigger event sum = ipico_lrc(s, 28) lrc = int(s[28:30], 16) if sum == lrc: timestr = '{0}:{1}:{2}.{3:02}'.format(s[16:18], s[18:20], s[20:22], int(s[22:24], 16)) istr = '00:0' chanstr = 'MAN' tagid = '' # emulate THbC manual trigger (lstripped) t = tod.tod(timestr, index=istr, chan=chanstr, refid=tagid, source=self.srcid) if self.cb is not None: glib.idle_add(self.cb, t) self.cqueue.put_nowait(('RFID', t)) # notify only now else: self.log.warn('Incorrect char sum message skipped: ' + hex(sum) + ' != ' + hex(lrc)) else: self.log.debug('Non RFID message: ' + repr(msg))
def addrider(self, bib='', info=None): """Add specified rider to race model.""" nr = [bib, '', '', '', '', 0, tod.tod(0), '', []] if bib == '' or self.getrider(bib) is None: dbr = self.meet.rdb.getrider(bib, self.series) if dbr is not None: for i in range(1, 5): nr[i] = self.meet.rdb.getvalue(dbr, i) return self.riders.append(nr) else: return None
def get_dateline(width=32): dpart = time.strftime(DATE_FMT) tpart = tod.tod('now').meridian() ret = tpart totlen = len(tpart) + len(dpart) if totlen >= width: # with a space ret = tpart.center(width) # fall back to time else: ret = dpart + u' ' * (width - totlen) + tpart return ret
def toload(self, bib=None): """Load timer.""" self.status = 'load' self.starttod = None self.recovtod = tod.tod(0) self.finishtod = None self.set_time() self.lap = 0 self.set_lap() self.splits = [] if bib is not None: self.setrider(bib) uiutil.buttonchg(self.b, uiutil.bg_none, 'Ready')
def timeout(self): """Update status.""" # 1: Terminate? if not self.running: return False # 2: Process? try: ntime = tod.tod(u'now') ntod = ntime.truncate(0) if ntime >= self.nc.truncate(1): self.tod = ntod self.process_timeout() # and advance one second self.nc += tod.ONE else: self.log.debug(u'Timeout called early: ' + ntime.rawtime()) # no need to advance, desired timeout not yet reached except Exception as e: self.log.error(u'Timeout: ' + repr(e)) # 3: Re-Schedule tt = tod.tod(u'now') + tod.tod(u'0.01') count = 0 while self.nc < tt: # ensure interval is positive if tod.MAX - tt < tod.ONE: self.log.debug(u'Midnight rollover.') break self.log.debug(u'May have missed an interval, catching up.') self.nc += tod.ONE # 0.01 allows for processing delay count += 1 if count > 10: break ival = int(1000.0 * float((self.nc - tod.tod(u'now')).timeval)) if ival < 0 or ival > 10000: ival = 1000 # assume host time change glib.timeout_add(ival, self.timeout) # 4: Return False return False # must return False
def spfin_info_time_edit_activate_cb(self, button): """Display contest timing edit dialog.""" ostx = '' oftx = '' if self.start is not None: ostx = self.start.rawtime(4) else: ostx = '0.0' if self.finish is not None: oftx = self.finish.rawtime(4) ret = uiutil.edit_times_dlg(self.meet.window, ostx, oftx) if ret[0] == 1: try: stod = None if ret[1]: stod = tod.tod(ret[1], 'MANU', 'C0i') self.meet.timer.printline(' ' + str(stod)) ftod = None if ret[2]: ftod = tod.tod(ret[2], 'MANU', 'C1i') self.meet.timer.printline(' ' + str(ftod)) self.set_start(stod) self.set_finish(ftod) self.set_elapsed() cid = '' i = self.current_contest_combo.get_active_iter() if i is not None: # contest selected ok cid = self.contests.get_value(i, COL_CONTEST) self.contests.set_value(i, COL_200M, self.curelap) if self.start is not None and self.finish is not None: self.log_elapsed(cid) self.log.info('Updated race times.') except Exception as v: self.log.error('Error updating times: ' + str(v)) glib.idle_add(self.delayed_announce) else: self.log.info('Edit race times cancelled.')
def trig(self, timeval='now', index='FAKE', chan='MAN', refid='0', sourceid=None): """Create a fake timing event. Generate a new tod object to mimic a message as requested and pipe it to the command thread. Default tod is the 'now' time in the calling thread. """ src=self.name if sourceid is not None: src=sourceid t = tod.tod(timeval, index, chan, refid.lstrip('0'), source=src) self.cqueue.put_nowait(('TRIG', t))
def current_contest_combo_changed_cb(self, combo, data=None): """Copy elapsed time into timer (dodgey).""" self.resettimer() i = self.current_contest_combo.get_active_iter() if i is not None: # contest selected ok ft = self.contests.get_value(i, COL_200M) if ft is not None: self.start = tod.tod(0) self.finish = ft self.set_elapsed() else: self.start = None self.finish = None self.set_elapsed() winner = self.contests.get_value(i, COL_WINNER) self.ctrl_winner.set_text(winner)
def toidle(self): """Set timer state to idle.""" self.status = 'idle' self.bib = None self.bibent.set_text('') self.bibent.set_sensitive(True) self.serent.set_sensitive(True) self.biblbl.set_text('') self.starttod = None self.recovtod = tod.tod(0) self.finishtod = None self.lap = 0 self.set_lap() self.splits = [] self.set_time() uiutil.buttonchg(self.b, uiutil.bg_none, 'Idle')
def tstotod(self, ts): """Convert a race result timestamp to time of day.""" ret = None try: # get offset from last sync beacon in device units ti = int(ts) - self.lastts # convert offset to seconds tsec = decimal.Decimal(ti // 256) + decimal.Decimal(ti % 256) / 256 # create a new time of day object, truncated to thous ntv = self.lastsync.timeval + tsec ret = tod.tod(ntv).truncate(3) except Exception as e: self.log.debug(u'Error converting Ts: ' + repr(e)) return ret
def starttrig(self, e): """Process a trigger.""" chan = timy.chan2id(e.chan) if (chan == timy.CHAN_START and self.armed and self.currider is not None and self.currider in self.ridermap): ls = tod.tod(u'now')# log the 'now' time and the rider's wall start # emit rider vec cr = self.ridermap[self.currider] self.scb.add_rider([cr[1], cr[2], e.rawtime(), cr[0].rawtime(), ls.rawtime()], u'starter') self.log.info(u'Starter: ' + u','.join([cr[1], e.rawtime()])) self.armed = False else: # emit an anon tod self.log.info(u'Impulse: ' + e.rawtime()) self.scb.add_rider([u'Impulse: ' + unicode(e)], u'message') return False
def run(self): """Called via threading.Thread.start().""" running = True self.log.debug('Starting') while running: try: # Read phase if self.port is not None: try: self.__read() except socket.timeout: pass m = self.cqueue.get_nowait() else: # when no read port avail, block on read of command queue m = self.cqueue.get() self.cqueue.task_done() # Write phase if type(m) is tuple and type(m[0]) is str and m[0] in TCMDS: if m[0] == 'MSG' and self.port and not self.error: cmd = m[1] ##+ '\r\n' self.log.debug('Sending rawmsg ' + repr(cmd)) self.port.write(cmd) elif m[0] == 'TRIG': if type(m[1]) is tod.tod: self.log.log(RFID_LOG_LEVEL, str(m[1])) glib.idle_add(self.__cb, m[1], priority=THBC_PRIORITY) elif m[0] == 'SANE': self.log.debug('Checking config.') self.__sane() elif m[0] == 'IPCFG': self.log.debug('Updating Decoder IP.') self.__ipcfg() elif m[0] == 'SYNC': t = tod.tod('now') # DANGER: Busy wait may interfere with caller while t - t.truncate(0) > tod.tod('0.02'): t = tod.tod('now') self.port.write(self.__set_time_cmd(t)) self.log.debug('Set time on decoder: ' + t.meridian()) elif m[0] == 'REPL': self.log.info('Replay passings from: ' + repr(m[1])) with open(m[1], 'rb') as f: for l in f: self.__readline(l) self.log.info('Replay complete.') elif m[0] == 'EXIT': self.log.debug('Request to close : ' + str(m[1])) running = False # This may already be set elif m[0] == 'PORT': if self.port is not None: self.port.close() self.port = None if m[1] is not None and m[1] != '' and m[1] != 'NULL': self.log.debug('Re-Connect port : ' + str(m[1])) self.port = self.__mkport(m[1]) self.error = False self.unitno = u'' self.port.write(QUECMD) # re-identify decoder else: self.log.debug('Not connected.') self.error = True else: pass else: self.log.warn(u'Unknown message: ' + repr(m)) except Queue.Empty: pass except serial.SerialException as e: if self.port is not None: self.port.close() self.port = None self.errstr = 'Serial Port error.' self.error = True self.log.error('Closed port: ' + str(type(e)) + str(e)) except socket.error as e: self.log.error('Network error: ' + str(type(e)) + str(e)) except Exception as e: self.log.error('Exception: ' + str(type(e)) + str(e)) raise #self.errstr = str(e) #self.error = True if self.port is not None: self.port.close() self.port = None self.setcb() # make sure callback is unrefed self.log.info('Exiting')
def __init__(self, configpath=None): # logger and log handler self.log = logging.getLogger() self.log.setLevel(logging.DEBUG) self.loghandler = None # set in loadconfig to meet dir # meet configuration path and options if configpath is None: configpath = u'.' # None assumes 'current dir' self.configpath = configpath self.loglevel = logging.INFO # UI log window self.io = telegraph.telegraph() self.io.set_pub_cb(self.msg_cb) self.started = False self.running = True self.fontsize = FONTSIZE fnszstr = str(self.fontsize)+'px' self.font = pango.FontDescription(FONTFACE + ' ' + fnszstr) b = gtk.Builder() b.add_from_file(os.path.join(metarace.UI_PATH, 'irtt_announce.ui')) self.window = b.get_object('window') self.lbl_header = b.get_object('lbl_header') self.lbl_header.modify_font(self.font) self.lbl_header.set_text('metarace irtt announce ' + metarace.VERSION) self.elap_lbl = b.get_object('elap_lbl') self.elap_lbl.set_text('--:--') self.elap_lbl.modify_font(self.font) b.get_object('fin_pfx_lbl').modify_font(self.font) self.fin_label = b.get_object('fin_pfx_lbl') self.fin_label.modify_font(self.font) self.fin_rank = b.get_object('fin_rank_lbl') self.fin_rank.modify_font(self.font) self.fin_rider = b.get_object('fin_rider_lbl') self.fin_rider.modify_font(self.font) self.fin_time = b.get_object('fin_time_lbl') self.fin_time.modify_font(self.font) b.get_object('int_pfx_lbl').modify_font(self.font) self.int_label = b.get_object('int_pfx_lbl') self.int_label.modify_font(self.font) self.int_rank = b.get_object('int_rank_lbl') self.int_rank.modify_font(self.font) self.int_rider = b.get_object('int_rider_lbl') self.int_rider.modify_font(self.font) self.int_time = b.get_object('int_time_lbl') self.int_time.modify_font(self.font) self.fin_box = b.get_object('fin_box') # seems to work? self.fin_box.hide() #self.map_winsz = 0 #self.map_xoft = 0 #self.map_w = 0 #self.map_area = b.get_object('map_area') #self.map_src = None #self.map_area.set_size_request(-1, 80) #self.map_area.show() # lap & bunch status values self.cur_lap = tod.tod(0) self.cur_split = tod.tod(0) self.cur_bunchid = 0 self.cur_bunchcnt = 0 self.riders = gtk.ListStore(gobject.TYPE_STRING, # rank gobject.TYPE_STRING, # no. gobject.TYPE_STRING, # namestr gobject.TYPE_STRING, # cat/com gobject.TYPE_STRING, # timestr gobject.TYPE_STRING, # downtime gobject.TYPE_PYOBJECT) # rftod self.inters = gtk.ListStore(gobject.TYPE_STRING, # rank gobject.TYPE_STRING, # no. gobject.TYPE_STRING, # namestr gobject.TYPE_STRING, # turn gobject.TYPE_STRING, # eta gobject.TYPE_STRING, # speed gobject.TYPE_PYOBJECT) # rftod t = gtk.TreeView(self.riders) self.view = t t.set_reorderable(False) t.set_rules_hint(True) t.set_headers_visible(False) self.search_lbl = b.get_object('search_lbl') self.search_lbl.modify_font(self.font) self.search_entry = b.get_object('search_entry') self.search_entry.modify_font(self.font) t.set_search_entry(b.get_object('search_entry')) t.set_search_column(1) t.modify_font(self.font) uiutil.mkviewcoltxt(t, 'Rank', 0,width=40) uiutil.mkviewcoltxt(t, 'No.', 1,calign=1.0,width=40) uiutil.mkviewcoltxt(t, 'Rider', 2,expand=True,fixed=True) uiutil.mkviewcoltxt(t, 'Cat', 3,calign=0.0) uiutil.mkviewcoltxt(t, 'Time', 4,calign=1.0,width=100) uiutil.mkviewcoltxt(t, 'Gap', 5,calign=1.0,width=90) # "down" time? #uiutil.mkviewcoltxt(t, 'Bunch', 5,width=50,bgcol=6,calign=0.5) b.get_object('text_scroll').add(t) t.show() t.grab_focus() ## let's suppress the arrivals for now ##b.get_object('arrival_xpnd').hide() t = gtk.TreeView(self.inters) self.iview = t t.set_reorderable(False) t.set_rules_hint(True) t.set_headers_visible(False) t.modify_font(self.font) uiutil.mkviewcoltxt(t, '', 0,width=40) uiutil.mkviewcoltxt(t, 'No.', 1,calign=1.0,width=40) uiutil.mkviewcoltxt(t, 'Rider', 2,expand=True,fixed=True) uiutil.mkviewcoltxt(t, 'Turn', 3,calign=0.0) uiutil.mkviewcoltxt(t, 'Time', 4,calign=1.0,width=100) uiutil.mkviewcoltxt(t, 'Avg', 5,calign=1.0,width=90) t.show() b.get_object('arrival_scroll').add(t) b.connect_signals(self)
from metarace import tod from metarace import telegraph from metarace import unt4 ## todo remove from metarace import strops LOGHANDLER_LEVEL = logging.DEBUG CONFIGFILE = u'voladisp.json' LOGFILE = u'voladisp.log' APP_ID = u'voladisp_1.0' # configuration versioning ENCODING = 'iso8859-15' VOLA_BAUD = 9600 STX = 0x02 LF = 0x0a BRIGHTVALS = [0x31, 0x32, 0x33] ELAPOFT = tod.tod('0.05') class voladisp: """VOLA Display control console application.""" def quit_cb(self, menuitem, data=None): """Quit the application.""" self.running = False def uscb_activate_cb(self, menuitem, data=None): """Request a re-connect to the uSCBsrv IRC connection.""" if self.uscbport: self.log.info(u'Requesting re-connect to announcer: ' + repr(self.uscbport) + repr(self.uscbchan)) else: self.log.info(u'Announcer not configured.')
def run(self): """Called via threading.Thread.start().""" running = True self.log.debug('Starting') while running: try: # Read phase if self.port is not None: try: self.__read() except Exception as e: self.log.debug(u'EXCEPT timeout: ' + repr(e)) m = self.cqueue.get_nowait() else: # when no read port avail, block on read of command queue m = self.cqueue.get() self.cqueue.task_done() # Write phase if type(m) is tuple and type(m[0]) is str and m[0] in TCMDS: if m[0] == 'MSG' and self.port and not self.error: cmd = m[1] + '\n' self.log.debug('Sending rawmsg ' + repr(cmd)) self.port.write(cmd) elif m[0] == 'TRIG': if type(m[1]) is tod.tod: self.log.log(RFID_LOG_LEVEL, str(m[1])) glib.idle_add(self.__cb, m[1], priority=RRU_PRIORITY) elif m[0] == 'SANE': self.log.debug('Checking config.') self.__sane() elif m[0] == 'SYNC': # not the assumption is that 'now' is close to the # time that the Unit processses the message. It # should not then matter how long it takes to get # the reply, we capture the reference time here. self.port.write('GETTIMESTAMP\n') self.lastsync = tod.tod('now') self.lastts = 0 # error passings in transition elif m[0] == 'REPL': self.log.info('Replay passings from: ' + repr(m[1])) with open(m[1], 'rb') as f: for l in f: self.__readline(l) self.log.info('Replay complete.') elif m[0] == 'EXIT': self.log.debug('Request to close : ' + str(m[1])) running = False # This may already be set elif m[0] == 'PORT': if self.port is not None: self.port.close() self.port = None if m[1] is not None and m[1] != '' and m[1] != 'NULL': self.log.debug('Re-Connect port : ' + str(m[1])) self.port = self.__mkport(m[1]) self.error = False else: self.log.debug('Not connected.') self.error = True else: pass else: self.log.warn(u'Unknown message: ' + repr(m)) except Queue.Empty: pass except serial.SerialException as e: if self.port is not None: self.port.close() self.port = None self.errstr = 'Serial Port error.' self.error = True self.log.error('Closed port: ' + str(type(e)) + str(e)) except Exception as e: self.log.error('Exception: ' + str(type(e)) + str(e)) raise #self.errstr = str(e) #self.error = True if self.port is not None: self.port.close() self.port = None self.setcb() # make sure callback is unrefed self.log.info('Exiting')
import glib import time # for ipcompletion, this is a problem! import metarace from metarace import tod # Serial baudrate RRU_BAUD = 19200 # GTK Priority for timing message callbacks sent to main loop RRU_PRIORITY = glib.PRIORITY_HIGH # Photofinish threshold - ~20cm based on tests at DISC, # Activator period is ~20ms... with a single active # loop, all reads within 20ms must be considered same time. PHOTOTHRESH = tod.tod('0.03') # thread queue commands -> private to thread TCMDS = ('EXIT', 'PORT', 'MSG', 'TRIG', 'SYNC', 'SANE', 'REPL') RFID_LOG_LEVEL = 16 # lower so not in status and on-screen logger. logging.addLevelName(RFID_LOG_LEVEL, 'RFID') class rru(threading.Thread): """Race Result USB Active thread object class.""" def __init__(self, port=None, name='rru'): """Construct thread object. Named parameters:
def process_timeout(self): """Perform required timeout activities.""" # Check run state variables iff remote on: if self.remote_enable: if self.elapstart is not None: estr = u'' dstr = u'' if self.elapfin is not None: # Race over - show elap and down if poss elap = (self.elapfin - self.elapstart).truncate(0) estr = elap.rawtime(0) if self.timerstat != u'finished': if self.tod > self.elapfin: downtod = self.tod - self.elapfin target = self.maxlaptime if self.timelimit is not None: target = (self.timelimit - elap) + tod.tod(u'30') if downtod > tod.ZERO: if downtod < target: #if downtod < target: dstr = u'+' + downtod.rawtime(0) else: dstr = u'+0' # prepare for arrival else: dstr = u'+0' # prepare for arrival else: # race in progress, show run time and distance or lap elap = self.tod - (self.elapstart + ELAPOFT) if elap > tod.MAXELAP: elap = tod.ZERO estr = elap.rawtime(0) if self.distance is not None and self.distance > 1.9: dstr = u'{0:1.1f}km'.format(self.distance) if self.lapfin is not None: # lap down time overwrites dist, but only if valid laptm = self.tod - self.lapfin if laptm > tod.ZERO and laptm < self.maxlaptime: dstr = u'+' + laptm.rawtime(0) elif self.timerstat == u'armfinish': dstr = u'+0' self.set_left(estr) self.set_right(dstr) elif self.ttno: nostr = u'' if self.ttno: nostr = self.ttno nostr = nostr.rjust(4) #nmstr = u'' #if self.ttname: #nmstr = self.ttname #nmstr = nmstr.ljust(12) #topline = nostr + u' ' + nmstr rstr = u'' if self.ttrank: rstr = u'(' + self.ttrank + u')' + ' ' rstr = rstr.ljust(6) tstr = u'' if self.tttime: tstr = self.tttime if u'.' not in tstr: tstr += ' ' tstr = tstr.rjust(10) self.set_right(tstr) # HhMM:SS.DC self.set_left(rstr + nostr) # (rr) nnn elif self.timeofday: self.set_left(u'') dstr = u'' if self.distance is not None and self.distance > 1.9: dstr = u'{0:1.1f}km'.format(self.distance) else: dstr = self.tod.meridian(secs=False) self.set_right(dstr) # check connection status if not self.remote.connected(): self.failcount += 1 if self.failcount > self.failthresh: self.remote.set_portstr(force=True) self.failcount = 0 self.log.debug(u'Telegraph connection failed, count = ' + repr(self.failcount)) else: self.failcount = 0
def __parse_message(self, msg, ack=True): """Return tod object from timing msg or None.""" ret = None if len(msg) > 4: if msg[0] == PASSSTART: # RFID message idx = msg.find('>') if idx == 37: # Valid length data = msg[1:33] msum = msg[33:37] tsum = thbc_sum(data) if tsum == msum: # Valid 'sum' pvec = data.split() istr = pvec[3] + ':' + pvec[5] rstr = pvec[1].lstrip('0') if pvec[5] == '3': # LOW BATTERY ALERT self.log.warn('Low battery on id: ' + repr(rstr)) ret = tod.tod(pvec[2], index=istr, chan=pvec[0], refid=rstr, source=self.name) self.log.log(RFID_LOG_LEVEL, msg.strip()) if ack: self.port.write(ACKCMD) # Acknowledge if ok self.__cksumerr = 0 else: self.log.info('Invalid checksum: ' + repr(tsum) + ' != ' + repr(msum) + ' :: ' + repr(msg)) self.__cksumerr += 1 if self.__cksumerr > 3: # assume error on decoder, so acknowledge and # continue with log # NOTE: This path is triggered when serial comms # fail and a tag read happens before a manual trig self.log.error('Erroneous message from decoder.') if ack: self.port.write(ACKCMD) # Acknowledge if ok else: self.log.info('Invalid message: ' + repr(msg)) elif msg[0] == STATSTART: # Status message data = msg[1:22] pvec = data.split() if len(pvec) == 5: # Note: this path is not immune to error in stream # but in the case of exception from tod construct # it will be collected by the thread 'main loop' rstr = ':'.join(pvec[1:]) ret = tod.tod(pvec[0].rstrip('"'), index='', chan='STS', refid=rstr, source=self.name) self.log.log(RFID_LOG_LEVEL, msg.strip()) else: self.log.info('Invalid status: ' + repr(msg)) elif '+++' == msg[0:3] and len(msg) > 53: self.__parse_config(msg[3:]) else: self.log.log(RFID_LOG_LEVEL, repr(msg)) else: self.log.info('Short message: ' + repr(msg)) return ret
import threading import Queue import logging import socket import time import glib from metarace import tod # System defaults TAGPREFIX = '058001' # Wheeltime rfid prefix DEFPORT = '192.168.95.32' # CV wheeltime IP WHEELRAWPORT = 10000 # TCP Port for raw tag stream WHEELFSPORT = 10200 # TCP Port for FS/LS filtered stream (normal) WHEELCMDPORT = 8999 # TCP Wheeltime command port (defunct?) WHEELPHOTOTHRESH = tod.tod('0.1') # Wheeltime confidence ~0.1s # thread queue commands -> private to timy thread TCMDS = ('RFID', 'EXIT', 'PORT', 'TRIG', 'REPL', 'MSG') # Logging defaults RFID_LOG_LEVEL = 16 # lower so not in status and on-screen logger. logging.addLevelName(RFID_LOG_LEVEL, 'RFID') adder = lambda sum, ch: sum + ord(ch) def ipico_lrc(ipxstr='', el=34): """Return the so-called 'LRC' character sum from IPX module.""" return reduce(adder, ipxstr[2:el], 0) & 0xff def sendall(s, buf):