def SetCameraState( state = False ): global camera, font camera = None font = None if state: Utils.FixPILSearchPath() try: camera = Device() except Exception as e: logException( e, sys.exc_info() ) camera = None return camera
def AddBibToPhoto( raceFileName, bib, raceSeconds ): dirName = getPhotoDirName( raceFileName ) fnameOld = GetPhotoFName( None, raceSeconds ) fnameNew = GetPhotoFName( bib, raceSeconds ) fileNameOld = os.path.join( dirName, fnameOld ) fileNameNew = os.path.join( dirName, fnameNew ) try: os.rename( fileNameOld, fileNameNew ) except Exception as e: logException( e, sys.exc_info() )
def recordVideoFrame( self ): tNow = now() self.sampleCount += 1 if not self.camera: return try: image = self.camera.getImage() self.fcb.append( tNow, image ) if self.owner is not None: wx.PostEvent( self.owner, PhotoEvent(t=tNew, photo=image) ) except Exception as e: logException( e, sys.exc_info() )
def recordVideoFrame( self ): tNow = now() self.sampleCount += 1 if not self.camera: return try: image = self.camera.getImage() self.fcb.append( tNow, image ) if self.owner is not None: wx.PostEvent( self.owner, PhotoEvent(t=tNow, photo=image) ) except Exception as e: logException( e, sys.exc_info() )
def doExport( self, event=None ): race = Model.race if not race: return fileName = Utils.getMainWin().fileName if Utils.getMainWin() else 'Test.cmn' #--------------------------------------------------------------------------------- # Create an Excel file. # xlFileName = os.path.splitext(fileName)[0] + '-TeamResults.xlsx' try: wb = xlsxwriter.Workbook( xlFileName ) formats = ExportGrid.ExportGrid.getExcelFormatsXLSX( wb ) ues = Utils.UniqueExcelSheetName() for category in race.getCategories( publishOnly=True ): eg = self.toExportGrid( category ) if eg: ws = wb.add_worksheet( ues.getSheetName(category.fullname) ) eg.toExcelSheetXLSX( formats, ws ) wb.close() except Exception as e: logException( e, sys.exc_info() ) del wb #--------------------------------------------------------------------------------- # Create a PDF file. # pdfFileName = os.path.splitext(fileName)[0] + '-TeamResults.pdf' try: pdf = PDF( orientation = 'P' ) pdf.set_font( 'Arial', '', 12 ) pdf.set_author( getpass.getuser() ) pdf.set_keywords( 'CrossMgr Team Results' ) pdf.set_creator( Version.AppVerName ) pdf.set_title( os.path.splitext(pdfFileName)[0].replace('-', ' ') ) for category in race.getCategories( publishOnly=True ): eg = self.toExportGrid( category ) if eg: eg.drawToFitPDF( pdf, orientation=wx.PORTRAIT ) pdf.output( pdfFileName, 'F' ) except Exception as e: logException( e, sys.exc_info() ) del pdf
def StartVideoBuffer( refTime, raceFileName ): global videoBuffer if not videoBuffer: camera = PhotoFinish.SetCameraState( True ) if not camera: return False dirName = PhotoFinish.getPhotoDirName( raceFileName ) if not os.path.isdir( dirName ): try: os.makedirs( dirName ) except Exception as e: logException( e, sys.exc_info() ) return False videoBuffer = VideoBuffer( camera, refTime, dirName ) videoBuffer.start() return True
def run( self ): self.reset() keepGoing = True while keepGoing: #sys.stderr.write( '.' ) tNow = now() try: self.fcb.append( tNow, self.camera.getImage() ) except Exception as e: logException( e, sys.exc_info() ) break while 1: try: message = self.queue.get( False ) except Empty: break if message[0] == 'Save': cmd, bib, t = message tFind = self.refTime + timedelta( seconds = t + getattr(Model.race, 'advancePhotoMilliseconds', Model.Race.advancePhotoMillisecondsDefault) / 1000.0 ) if tFind > tNow: threading.Timer( (tFind - tNow).total_seconds() + 0.1, self.takePhoto, args=[bib, t] ).start() continue times, frames = self.fcb.findBeforeAfter( tFind, 0, 1 ) for i, frame in enumerate( frames ): t = (times[i]-self.refTime).total_seconds() self.frameSaver.save( GetFilename(bib, t, self.dirName, i), bib, t, frame ) self.frameCount += len(frames) self.queue.task_done() elif message[0] == 'Terminate': self.queue.task_done() self.reset() keepGoing = False break # Sleep until we need to grab the next frame. frameWait = self.frameDelay - (now() - tNow).total_seconds() if frameWait < 0.0: frameWait = self.frameDelay # Give some more time if we are falling behind. time.sleep( frameWait )
def logNum( self, nums ): if nums is None: return if not isinstance(nums, (list, tuple)): nums = [nums] with Model.LockRace() as race: if race is None or not race.isRunning(): return t = race.curRaceTime() # Take the picture first to reduce latency to capturing the riders as they cross the line. if getattr(race, 'enableUSBCamera', False): for num in nums: try: num = int(num) except: continue try: race.photoCount = getattr(race,'photoCount',0) + VideoBuffer.ModelTakePhoto( num, t ) except Exception as e: logException( e, sys.exc_info() ) # Add the times to the model and write to the log. for num in nums: try: num = int(num) except: continue race.addTime( num, t ) OutputStreamer.writeNumTime( num, t ) self.playBlip() mainWin = Utils.getMainWin() if mainWin: mainWin.record.keypad.numEdit.SetValue( '' ) mainWin.record.refreshLaps() wx.CallAfter( mainWin.refresh ) if getattr(race, 'ftpUploadDuringRace', False): realTimeFtpPublish.publishEntry()
def DeletePhotos( raceFileName ): dirName = getPhotoDirName( raceFileName ) try: shutil.rmtree( dirName, True ) except Exception as e: logException( e, sys.exc_info() )
def onPageChanging(self, evt): isForward = evt.GetDirection() if not isForward: return page = evt.GetPage() if page == self.introPage: pass elif page == self.fileNamePage: fileName = self.fileNamePage.getFileName() # Check for a valid file. try: open(fileName).close() except IOError: if fileName == '': message = _('Please specify a GPX file.') else: message = u'{}:\n\n "{}"\n\n{}'.format( _('Cannot open file'), fileName, _('Please check the file name and/or its read permissions.' ), ) Utils.MessageOK(self.wizard, message, title=_('File Open Error'), iconMask=wx.ICON_ERROR) evt.Veto() return # Check for valid content. geoTrack = GeoTrack() try: geoTrack.read( fileName, isPointToPoint=self.fileNamePage.getIsPointToPoint()) except Exception as e: logException(e, sys.exc_info()) Utils.MessageOK(self.wizard, '{}: {}\n({})'.format( _('Read Error'), _('Is this GPX file properly formatted?'), e), title='Read Error', iconMask=wx.ICON_ERROR) evt.Veto() return # Check for too few points. if geoTrack.numPoints < 2: Utils.MessageOK( self.wizard, u'{}: {}'.format( _('Import Failed'), _('GPX file contains fewer than two points.')), title=_('File Format Error'), iconMask=wx.ICON_ERROR) evt.Veto() return if self.fileNamePage.getUseElevation(): fileNameElevation = os.path.join(os.path.dirname(fileName), 'elevation.csv') try: open(fileNameElevation).close() except IOError as e: logException(e, sys.exc_info()) message = u'{}: {}\n\n "{}"\n\n{}'.format( _('Cannot Open Elevation File'), e, fileNameElevation, _('Please check the file name and/or its read permissions.' ), ) Utils.MessageOK(self.wizard, message, title=_('File Open Error'), iconMask=wx.ICON_ERROR) else: try: geoTrack.readElevation(fileNameElevation) except Exception as e: logException(e, sys.exc_info()) message = u'{}: {}\n\n "{}"'.format( _('Elevation File Error'), e, fileNameElevation) Utils.MessageOK(self.wizard, message, title=_('File Read Error'), iconMask=wx.ICON_ERROR) self.geoTrackFName = fileName self.geoTrack = geoTrack self.summaryPage.setInfo( self.geoTrackFName, self.geoTrack.numPoints, self.geoTrack.lengthKm, self.geoTrack.totalElevationGainM, getattr(self.geoTrack, 'isPointToPoint', False)) self.useTimesPage.setInfo(self.geoTrackFName) elif page == self.useTimesPage: if self.useTimesPage.getUseTimes(): self.geoTrack.read(self.geoTrackFName, True)
def onPageChanging( self, evt ): isForward = evt.GetDirection() if not isForward: return page = evt.GetPage() if page == self.introPage: pass elif page == self.fileNamePage: fileName = self.fileNamePage.getFileName() # Check for a valid file. try: open(fileName).close() except IOError: if fileName == '': message = _('Please specify a GPX file.') else: message = u'{}:\n\n "{}"\n\n{}'.format( _('Cannot open file'), fileName, _('Please check the file name and/or its read permissions.'), ) Utils.MessageOK( self.wizard, message, title=_('File Open Error'), iconMask=wx.ICON_ERROR) evt.Veto() return # Check for valid content. geoTrack = GeoTrack() try: geoTrack.read( fileName, isPointToPoint = self.fileNamePage.getIsPointToPoint() ) except Exception as e: logException( e, sys.exc_info() ) Utils.MessageOK( self.wizard, '{}: {}\n({})'.format(_('Read Error'), _('Is this GPX file properly formatted?'), e), title='Read Error', iconMask=wx.ICON_ERROR) evt.Veto() return # Check for too few points. if geoTrack.numPoints < 2: Utils.MessageOK( self.wizard, u'{}: {}'.format(_('Import Failed'), _('GPX file contains fewer than two points.')), title=_('File Format Error'), iconMask=wx.ICON_ERROR) evt.Veto() return if self.fileNamePage.getUseElevation(): fileNameElevation = os.path.join( os.path.dirname(fileName), 'elevation.csv' ) try: open(fileNameElevation).close() except IOError as e: logException( e, sys.exc_info() ) message = u'{}: {}\n\n "{}"\n\n{}'.format( _('Cannot Open Elevation File'), e, fileNameElevation, _('Please check the file name and/or its read permissions.'), ) Utils.MessageOK( self.wizard, message, title=_('File Open Error'), iconMask=wx.ICON_ERROR) else: try: geoTrack.readElevation( fileNameElevation ) except Exception as e: logException( e, sys.exc_info() ) message = u'{}: {}\n\n "{}"'.format(_('Elevation File Error'), e, fileNameElevation ) Utils.MessageOK( self.wizard, message, title=_('File Read Error'), iconMask=wx.ICON_ERROR ) self.geoTrackFName = fileName self.geoTrack = geoTrack self.summaryPage.setInfo( self.geoTrackFName, self.geoTrack.numPoints, self.geoTrack.lengthKm, self.geoTrack.totalElevationGainM, getattr( self.geoTrack, 'isPointToPoint', False) ) self.useTimesPage.setInfo( self.geoTrackFName ) elif page == self.useTimesPage: if self.useTimesPage.getUseTimes(): self.geoTrack.read( self.geoTrackFName, True ) elif page == self.summaryPage: pass elif page == self.fixCategoryDistance: pass
def DeletePhotos(raceFileName): dirName = getPhotoDirName(raceFileName) try: shutil.rmtree(dirName, True) except Exception as e: logException(e, sys.exc_info())
def Server(q, shutdownQ, HOST, PORT, startTime): global readerEventWindow if not readerEventWindow: readerEventWindow = Utils.mainWin timeoutSecs = 5 delaySecs = 3 readerTime = None readerComputerTimeDiff = None s = None passingsCur = 0 status = None startOperation = None def qLog(category, message): q.put((category, message)) Utils.writeLog(u'RaceResult: {}: {}'.format(category, message)) def keepGoing(): try: shutdownQ.get_nowait() except Empty: return True return False def autoDetectCallback(m): qLog('autodetect', u'{} {}'.format(_('Checking'), m)) return keepGoing() def makeCall(s, message): cmd = message.split(';', 1)[0] qLog('command', u'sending: {}'.format(message)) try: socketSend(s, u'{}{}'.format(message, EOL)) buffer = socketReadDelimited(s) except Exception as e: qLog('connection', u'{}: {}: "{}"'.format(cmd, _('Connection failed'), e)) raise ValueError if not buffer.startswith(u'{};'.format(cmd)): qLog('command', u'{}: {} "{}"'.format(cmd, _('Unexpected return'), buffer)) raise ValueError return buffer while keepGoing(): if s: try: s.shutdown(socket.SHUT_RDWR) s.close() except Exception as e: pass time.sleep(delaySecs) #----------------------------------------------------------------------------------------------------- qLog( 'connection', u'{} {}:{}'.format( _('Attempting to connect to RaceResult reader at'), HOST, PORT)) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(timeoutSecs) s.connect((HOST, PORT)) except Exception as e: qLog( 'connection', u'{}: {}'.format(_('Connection to RaceResult reader failed'), e)) s, status, startOperation = None, None, None qLog('connection', u'{}'.format(_('Attempting AutoDetect...'))) HOST_AUTO = AutoDetect(callback=autoDetectCallback) if HOST_AUTO: qLog( 'connection', u'{}: {}'.format(_('AutoDetect RaceResult at'), HOST_AUTO)) HOST = HOST_AUTO else: time.sleep(delaySecs) continue #----------------------------------------------------------------------------------------------------- crossMgrMinProtocol = (1, 2) crossMgrMinProtocolStr = '.'.join('{}'.format(p) for p in crossMgrMinProtocol) try: buffer = makeCall(s, 'GETPROTOCOL') except ValueError as e: logException(e, sys.exc_info()) continue current, minSupported, maxSupported = [ f.strip() for f in buffer.strip().split(';')[1:] ] if tuple(int(i) for i in maxSupported.split('.')) < crossMgrMinProtocol: qLog( 'connection', u'{}. {} >={}. {} {}.'.format( _("RaceResult requires Firmware Upgrade"), _("CrossMgr requires PROTOCOL"), crossMgrMinProtocolStr, _("RaceResult supports"), maxSupported, )) time.sleep(delaySecs) continue try: buffer = makeCall(s, 'SETPROTOCOL;{}'.format(maxSupported)) except ValueError: continue qLog('status', u'{}'.format(buffer.strip())) try: buffer = makeCall(s, 'GETSTATUS') except ValueError: continue fields = [f.strip() for f in buffer.strip().split(';')] status = zip(statusFields, fields[1:]) for name, value in status: qLog('status', u'{}: {}'.format(name, value)) #----------------------------------------------------------------------------------------------------- try: buffer = makeCall(s, 'STOPOPERATION') except ValueError: continue #----------------------------------------------------------------------------------------------------- try: buffer = makeCall( s, 'SETTIME;{}'.format(datetime.datetime.now().strftime( '%Y-%m-%d;%H:%M:%S.%f')[:-3])) except ValueError: continue #----------------------------------------------------------------------------------------------------- try: buffer = makeCall(s, 'STARTOPERATION') except ValueError: continue qLog('status', u'{}'.format(buffer.strip())) #----------------------------------------------------------------------------------------------------- try: buffer = makeCall(s, 'GETTIME') except ValueError: continue try: dt = reNonDigit.sub(' ', buffer).strip() fields[-1] = ( fields[-1] + '000000')[:6] # Pad with zeros to convert to microseconds. readerTime = datetime.datetime(*[int(f) for f in dt.split()]) readerComputerTimeDiff = datetime.datetime.now() - readerTime except Exception as e: qLog( 'command', u'GETTIME: {} "{}" "{}"'.format(_('Unexpected return'), buffer, e)) continue while keepGoing(): #------------------------------------------------------------------------------------------------- cmd = 'PASSINGS' try: socketSend(s, u'{}{}'.format(cmd, EOL)) buffer = socketReadDelimited(s) if buffer.startswith('{};'.format(cmd)): fields = buffer.split(';') try: passingsText = fields[1] except Exception as e: qLog( 'command', u'{}: {} "{}" "{}"'.format(cmd, _('Unexpected return'), buffer, e)) continue try: passingsNew = int( reNonDigit.sub(' ', passingsText).strip()) except Exception as e: qLog( 'command', u'{}: {} "{}" "{}"'.format(cmd, _('Unexpected return'), buffer, e)) continue else: qLog( 'command', u'{}: {} "{}"'.format(cmd, _('Unexpected return'), buffer)) continue except Exception as e: qLog('connection', u'{}: {}: "{}"'.format(cmd, _('Connection failed'), e)) break if passingsNew != passingsCur: if passingsNew < passingsCur: passingsCur = 0 tagTimes = [] errors = [] times = set() passingsCount = passingsNew - passingsCur #--------------------------------------------------------------------------------------------- cmd = '{}:{}'.format( passingsCur + 1, passingsCount) # Add one as the reader counts inclusively. qLog( 'command', u'sending: {} ({}+{}={} passings)'.format( cmd, passingsCur, passingsCount, passingsNew)) try: # Get the passing data. socketSend(s, u'{}{}'.format(cmd, EOL)) except Exception as e: qLog( 'connection', u'cmd={}: {}: "{}"'.format(cmd, _('Connection failed'), e)) break tagReadSuccess = False try: readAllPassings = False while not readAllPassings: response = socketReadDelimited(s) sStart = 0 while 1: sEnd = response.find(EOL, sStart) if sEnd < 0: break if sEnd == sStart: # An empty passing indicates this is the last one. readAllPassings = True break line = response[sStart:sEnd] sStart = sEnd + len_EOL tag, t = parseTagTime(line, passingsCur + len(tagTimes), errors) if tag is None or t is None: qLog( 'command', u'{}: {} "{}"'.format( cmd, _('Unexpected return'), line)) continue t += readerComputerTimeDiff while t in times: # Ensure no equal times. t += tSmall times.add(t) tagTimes.append((tag, t)) tagReadSuccess = True except Exception as e: qLog( 'connection', u'cmd={}: {}: "{}"'.format(cmd, _('Connection failed'), e)) sendReaderEvent(tagTimes) for tag, t in tagTimes: q.put(('data', tag, t)) passingsCur += len(tagTimes) if not tagReadSuccess: break time.sleep(delaySecs) # Final cleanup. cmd = 'STOPOPERATION' try: socketSend(s, u'{}{}'.format(cmd, EOL)) buffer = socketReadDelimited(s) s.shutdown(socket.SHUT_RDWR) s.close() except: pass
def SavePhoto( fileName, bib, raceSeconds, cameraImage ): global font, brandingBitmap, photoCache bitmap = wx.BitmapFromImage( PilImageToWxImage(cameraImage) ) w, h = bitmap.GetSize() dc = wx.MemoryDC( bitmap ) fontHeight = h//32 if not font: font = wx.FontFromPixelSize( wx.Size(0,fontHeight), wx.FONTFAMILY_SWISS, wx.NORMAL, wx.FONTWEIGHT_BOLD ) brandingHeight = fontHeight * 2.3 brandingBitmap = wx.Bitmap( os.path.join(Utils.getImageFolder(), 'CrossMgrHeader.png'), wx.BITMAP_TYPE_PNG ) scaleMult = float(brandingHeight) / float(brandingBitmap.GetHeight()) brandingImage = brandingBitmap.ConvertToImage() brandingImage.Rescale( int(brandingImage.GetWidth() * scaleMult), int(brandingImage.GetHeight() * scaleMult), wx.IMAGE_QUALITY_HIGH ) brandingBitmap = brandingImage.ConvertToBitmap( dc.GetDepth() ) txt = [] if bib: try: riderInfo = Model.race.excelLink.read()[int(bib)] except: riderInfo = {} riderName = ', '.join( [n for n in [riderInfo.get('LastName',''), riderInfo.get('FirstName','')] if n] ) if riderName: team = riderInfo.get('Team', '') if team: txt.append( ' %s (%s)' % (riderName, team) ) else: txt.append( ' %s' % riderName ) txt.append( _(' Bib: {} RaceTime: {} {}').format( bib, formatTime(raceSeconds), datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) ) else: txt.append( _(' RaceTime: {} {}').format( formatTime(raceSeconds), datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) ) bitmapHeight = brandingBitmap.GetHeight() xText, yText = brandingBitmap.GetWidth(), int(fontHeight*0.14) dc.SetFont( font ) dc.SetPen( wx.BLACK_PEN ) dc.SetBrush( wx.WHITE_BRUSH ) dc.DrawRectangle( 0, 0, w, bitmapHeight+1 ) dc.GradientFillLinear( wx.Rect(int(w/2), 0, int(w/2), bitmapHeight), wx.WHITE, wx.Colour(200,200,200), wx.EAST ) dc.DrawBitmap( brandingBitmap, 0, 0 ) lineHeight = int(fontHeight*1.07) dc.SetTextForeground( wx.BLACK ) for t in txt: dc.DrawText( t, xText, yText ) yText += lineHeight dc.DrawText( AppVerName, w - dc.GetTextExtent(AppVerName)[0] - fontHeight*.25, yText - lineHeight ) dc.DrawLine( xText, 0, xText, bitmapHeight ) dc.SetBrush( wx.Brush(wx.WHITE, wx.TRANSPARENT) ) dc.DrawRectangle( 0, 0, w, bitmapHeight+1 ) dc.SelectObject( wx.NullBitmap ) image = wx.ImageFromBitmap( bitmap ) # Check if the folder exists before trying to write to it. # Otherwise the SaveFile will raise an error dialog, which we don't want. if not os.path.isdir( os.path.dirname(fileName) ): try: os.mkdir( os.path.dirname(fileName) ) except Exception as e: logException( e, sys.exc_info() ) return 0 # Try to save the file. try: image.SaveFile( fileName, wx.BITMAP_TYPE_JPEG ) photoCache.add( os.path.basename(fileName) ) return 1 except Exception as e: logException( e, sys.exc_info() ) return 0