# -*- coding: utf-8 -*- """ Created initially on Tue Apr 28 2020 @author: David Hannaford program names with version numbers are the development copies. The working copy should be saved as boattimer.py in the /home/pi folder to autostart at boot up When running on a Windows PC, set field "pc" to 1, or if on a Raspberry Pi set field "pc" to 0 The hardware design and software are freely available from LSSC for use by any sailing club who wishes to use them, but no warranty or support is offered on them and LSSC can take no liability for any consequence of using it. It is up to the user to determine if the system performs a useful function for them and to use it appropriately. The user is free to amend or modify the system as they wish, but a reference should be retained at the start of the main program to the original source and to these conditions. Raspberry PI physical pin usage 2 - Red 5 volt Power to fan via 27 ohm speed limiting resistor 4 - Red 5 volt power to horn relay board 6 - Black Ground for Piezzo sounder 11 - Red GPIO17 Piezzo sounder PWM signal 14 - Black Ground for fan 16 - Green GPIO23 Signal out to sound horn 18 - Blue GPIO24 Return signal from horn board to show it is connected 30 - Black Ground for horn relay board Chronopi Real time clock module using pins: 1 - Red 3.3 volt supply 3 - Yellow GPIO2 SDA1 for I2C 5 - Green GPIO3 SCL1 for I2C 9 - Black - Ground to run automatically at pi startup: mkdir /home/pi/ .config/autostart nano /home/pi/ .config/autostart/clock.desktop edit file to say: [Desktop Entry] Type=Application Name=Boatstart Exec=/usr/bin/python3 /home/pi/boattimer.py then CTRL X the y then enter to save it the do sudo reboot The program must be called boattimer.py and be in the /home/pi folder also make sure that the racecalendar, boathandicaps and personalhandicaps CSV files are in that folder Development copies of the program can be in the /projects/boatstart folder with version numbers but should be copied without the version number into the /home/pi folder when ready to use them To do: after race end occurs put race ended message up in standard handicap race option people names start one row down from where they should - because of 4 start signals calculate positions from captured and corrected end times after the end of the race it does not continue to say Race end in 0 0 horn sounding for handicap times as well as slow & fast start Next start showing handicap time entries put lap counting in - but sort out what happens when log finishes is clicked on and a slow boat is a lap down In std pursuit - if the boatpynumbers file does not have the duty entry at the end the system does not put up race end or sound race end horn - it might be something to do with the number of entries set in boatend when py nos read in in crew pursuit no problems known in Personal handicaps not working - might be due to missing stuff in personalhandicaps0,csv file Check calculations on the personal handicaps for next week in code and on saved file error when you close the program with the x in top right corner or with the close button >>> invalid command name "56566048timesec" while executing "56566048timesec"("after" script) probably because of the 1/2 second interrupt call of timesec still being active. check that position entered is for a person that raced otherwise stop it being entered and give message if person who raced not given a position record them as DNF Calculate series current positions and results Check if memory stick inserted and if so, save to that as well as to the micro SD card If topper selected it shows that as next start rather then First Start - general issue of 2 boats or people with same time shows 2nd not 1st one - can we show both? Only 1 beep for first gun - as it is before the start of the race Put in Try and exceptions on file reads Separate setup program to change standard boat times -use Libre office calc Restart if needed Send out results and handicaps to another machine or email address or on the website Close down to shut down Pi Connect audio cable from HDMI/VGA adaptor to screen audio input and see if we can issue spoken commands using software from July 2020 PE magazine Close down instructions for closing system down after end off race Horn test message after boot up - check horn is connected first Display warning if core temperature getting too high Race course selector program takes 8 compass headings and wind strength to select and display suitable courses. Courses stored as CSV files. If more than 1 available, randomise the choice. Get courses from race officers store initials of race officer with course and show it v29 - delay button moves start back 5 minutes each time clicked and puts up message v30 - code for "other" and "no crew" boats to be able to be selected in crew pursuit and std handicap v31 - code for recording laps and calculating positions in crew pursuit v32 - buttons displayed to choose if std or crew pursuit when calendar says pursuit race. - last lap time shown next to lap no for laps 2 onwards - Undo last item, reduces lap number by 1 for last clicked on helm name - messes up last lap time v33 - tidy up race end times not showing correctly and fix display minor bugs - dash between boat & sail no - show when boat class started in std pursuit race - instead of lap numbers v35 - Fixed incorrect start time for undefined races on Wednesday & Saturday Calculate and show corrected end time for std handicap races to indicate position in race v36 - Updated boats and PY numbers for 2021 season. Fixed race end not showing after boat update Added ability to record and save race finish positions for crew pursuit races v37 - "Results Saved" message put on screen after results wrritten to file v38 - if multiple races in calendar on one day - e.g. Commodores Cup etc It now selects the first one with a start time equal to or after the hour of the current time Beeps added before First Gun. Europe added to handicap race type Other boats list.D-Zero added to boats lists. Fixed bug keeping horn on if tested before race start v39 - Display beep status on screen if running on PC v40 - Notes added about limitations and conditions of use """ pc=1 # running on a windows pC #pc=0 # running on a raspberry pi from tkinter import * from tkinter import ttk import tkinter as tk from functools import partial import time import tkinter.font as tkFont #from pygame import mixer # for playing sounds import csv # for reading and writing CSV foormat files import os # for reading temperature of core from datetime import date from datetime import datetime import calendar #if pc == 1 : #import winsound # GPIO setup - comment this section out when on PC if pc == 0: import RPi.GPIO as GPIO # Set up GPIO pinsusing BCM numbering notation GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) # set up the GPIO channels - input and two output GPIO.setup(23, GPIO.OUT) # signal out to sound the horn on physical pin 16 GPIO.setup(24, GPIO.IN) # signal back frorm horn relay to say horn is connected physical pin 18 GPIO.setup(17,GPIO.OUT) # output for Piezzo buzzer on pin 11 # output for piezzo buzzer p = GPIO.PWM(17,1000) #GPIO17 as PWM output for buzzer, with 1000Hz frequency # output to GPIO23 #GPIO.output(23, True) # input from GPIO24 hornreturn = GPIO.input(24) # Initialize pygame mixer for playing sounds and set up .wav files #mixer.init() #hornsound = mixer.Sound('horn.wav') #beepsound = mixer.Sound('beep.wav') hornon = 0 # horn switched off horncount = 2 # horn length also set in horn(n) function slowhandicap = 10 # time when slow boats start in handicap races fasthandicap = 15 # time when fast boats start in handicap races end = 22 # no of normally crewed boats including "First Gun and First Start entry and Race End helmend = 26 # end of list of helm only boats numboats = helmend + 2 crews = 26 # number of crew name entries on the crew pursuit screen numpeople = crews + 2 firstcol = 22 nummax = numboats if (numpeople > numboats): nummax = numpeople nummax = nummax + 4 # space for start and end gun entries listsize = nummax + 2 peopleend = 22 items = [" "] * listsize # holds boat or helm names times = [0] * listsize # times list for races nocrew = [0] * listsize # number of crew in the boat helmpyno = [0] * listsize # py number for boat being raced by a helm btimemins = [0] * listsize btimesecs = [0] * listsize boatstatus = [0] * listsize persboats = [" "] * listsize timetext = [" "] * listsize btime = [0] * listsize sailno = [0] * listsize normstmin = [0] * listsize normstsec = [0] * listsize seasonstart = [0] * listsize starthcap = [0] * listsize hcsec = [0] * listsize lastweek = [0] * listsize hcxlt = [0] *listsize lasthc = [0] * listsize nexthc = [0] *listsize nxtstart = [0] *listsize rndnxt = [0] * listsize prevhcap = [0] * listsize newprev = [0] * listsize delta = [0] * listsize raced = [0] * listsize posn = [0] * listsize normboat = [0] * listsize # holds the normal boat raced by a helm person normsail = [0] * listsize # holds the sail number of the normal boat raced by a helm boatclass = [0] * listsize # boattext = [0] * listsize # boattimemins = [0] * listsize # boattimesecs = [0] * listsize # endsecs = [0] * listsize # started = [0] * listsize # set to 1 when the boat has gone past its start time laps = [0] * listsize # no of laps completed lapendtime = [0] * listsize # time last lap completed calcs = [0] * listsize # used forcalculating the race positions during the race processed = [0] * listsize # used in calcyulating the positions slowfast = [0] * listsize # holds for ahelm name the start time - 10 or 15 # handicaps calculated from py numbers py = [0] * listsize pyhctext = [""] * listsize pyhcmins = [0] * listsize pyhcsecs = [0] * listsize timlab1 = [0] * listsize timlab2 = [0] * listsize pershdr = [0] * 16 # holds header names for personal handicaps file persfilenum = 1 # number to be appended to personal handicaps file name cphdr = [" "] * 3 # headers for crew pursuit results file highlap = 0 # highest number of laps completed so far nboat=" " other = "Other" # these boats listed below are in the other boats drop down menu other2 = "Other" comet = "Comet" cometduo = "Comet Duo" dzero = "D-Zero" laser = "Laser" topper = "Topper" supernova = "Supernova" enterprise = "Enterprise" gp14 = "GP14" moth = "British Moth" europe = "Europe" rs9 = "RS Aero 9" solution = "Solution" nat12 = "National 12" lightning = "Lightning 368" ent1 = "Enterprise (1)" gp141 = "GP14 (1)" nat121 = "National (1)" comduo1 = "Comet Duo (1)" duty = "Duty" hnum = 1 # used to count number of horn signals given when on PC pstart = 3 # normal number of hstart signals rtype = 0 # flag to say if race type has been selected yet dtype = 0 # flag to day if race day/ time has been selected yet tcheck = 0 #flag used to check if timesec finishes processing before being called again #Boat names and handicaps are now read from Handicaps.csv file in /home/pi/ folder n=2 b=["Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off", "Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off", "Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off","Off"] timesecs = 0 halfsec = 0 firststart = 10 # number of minutes for first start after first gun racestarthh = 11 racestartmm = 0 racestartss = 0 prevboat = " " nextstart = 0 beepon = 0 #timetogosecs = 0 # temp assignment until routine worked out #timetogomins = 30 timetogo = 0 timerun = 0 # flag to say if clock is running pausetime = 0 # flag to pause the timer from counting on logres = 0 # flag to say if logging results logendtime = 0 # flag to say if logging end times position = 1 filenumber = 0 # number to add to files written out boatselect = 0 # set to 1 if helm names shown and boats are to be selected by user undo = 0 # globl variables defined here to avoid warnings highpyno = 0 boatend = 0 fastslowno = " " racedate = " / / " closing = 0 # flag set when closing down """ This reads in the PY numbers and calculates the handicaps for the given race length It puts the boat name in items array, the handicap value as a decimal minutes in times array, the handicap as a text string of mins and seconds in timetext array, plus minutes in btimemins and seconds in btimesecs """ def readboatpynos(): global highpyno, boatend, length, firststart, fastslowno if pc==1: path = 'c:/home/pi/boatpynumbers.csv' if pc==0: path = '/home/pi/boatpynumbers.csv' with open(path) as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line = 0 for row in csv_reader: if line == 0: # fastslowno = row[1] # this is the py number that separates slow and fast handicap boats #print("0-fastslowno ", fastslowno) # headings row 1 is not used if line > 1: # bclass = row[0] # boat class name if boatselect == 0: items[line+1] = bclass boatclass[line+1] = bclass pyno = int(row[1]) # py number for boat class py[line+1] = pyno # save in PY numbers array nocrew[line+1] = int(row[2]) # number of crew in the boat if line == 2: highpyno = pyno # sets highpyno from first boat class py no in the list actlen = int(length) - firststart hcap = calchcap(pyno, actlen) times[line+1] = hcap hcmin = int(hcap) hcsec = int(60*(hcap - hcmin)) hcmins = str(hcmin) hcsecs = str(hcsec) if hcsecs == "0": hcsecs = "00" if boatselect == 0: timetext[line+1] = hcmins+":"+hcsecs btimemins[line+1] = hcmin btimesecs[line+1] = hcsec boattext[line+1] = hcmins+":"+hcsecs boattimemins[line+1] = hcmin boattimesecs[line+1] = hcsec #print("pynos & hcaps", pyno, highpyno, hcap, times[line+1], hcmin, hcsec, timetext[line+1]) line = line +1 boatend = line def getboathandicaps(): # only used in crew pursuit global highpyno, boatend, length, firststart, pyhcmins, pyhcsecs,pyhctext, fastslowno if pc==1: path = 'c:/home/pi/boatpynumbers.csv' if pc==0: path = '/home/pi/boatpynumbers.csv' with open(path) as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line = 0 hcap = 0 for row in csv_reader: if line == 0: # fastslowno = row[1] # this is the py number that separates slow and fast handicap boats #print ("1-fastslowno ", fastslowno) # headings row 1 is not used if line > 1: # boatclass[line+1] = row[0] # boat class name pyno = int(row[1]) # py number for boat class nocrew[line+1] = int(row[2]) # number of crew in the boat #print ("boatclass,pyno, no crew", boatclass[line+1], pyno, nocrew[line+1]) if line == 2: highpyno = pyno # sets highpyno from first boat class py no in the list actlen = int(length) - firststart hcap = calchcap(pyno, actlen) times[line+1] = hcap hcmin = int(hcap) hcsec = int(60*(hcap - hcmin)) hcmins = str(hcmin) hcsecs = str(hcsec) if hcsecs == "0": hcsecs = "00" hctemp = hcmins+":"+hcsecs pyhctext[line+1] = hctemp #print("hcmin, sec ", hcmin,hcsec) pyhcmins[line+1] = hcmin pyhcsecs[line+1] = hcsec #print("pynos & hcaps", pyno, highpyno, hcap, times[line+1], hcmin, hcsec, timetext[line+1]) #print ("pyhctext pyhcmins pyhcsecs ",pyhctext[line+1],pyhcmins[line+1],pyhcsecs[line+1]) line = line +1 boatend = line def readpershandicaps(): readfileno() # get latest file number to read filepath = ('/home/pi/personalhandicaps' + filenumber + ".csv") if pc == 1: filepath = "C:" + filepath with open(filepath) as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line_count = 0 for row in csv_reader: line_count += 1 if line_count == 1: #print(f'Column names are {", ".join(row)}') for n in range(0,15): pershdr[n] = row[n] # pershdr[10] is the file number #print (n, pershdr[n]) else: sailno[line_count+1] = row[0] # boat sail number persboats[line_count+1] = row[1] # boat class normstmin[line_count+1] = row[2] # normal start minutes normstsec[line_count+1] = row[3] # normal start seconds items[line_count+1] = row[4] # crew name(s) seasonstart[line_count+1] = row[5] #raced[line_count+1] = row[6] # if they raced posn[line_count+1] = row[7] # position if raced lasthc[line_count+1] = row[8] # last handicap in seconds put into next handicap field if (lasthc[line_count+1] == ""): lasthc[line_count+1] = 0 #print ("lasthc", lasthc[line_count+1]) nexthc[line_count+1] = lasthc[line_count+1] # next handicap in seconds nx = row[9] # put previous nextxhc into lasthc if (nx == ""): nx = 0 #print ("nx",nx) lasthc[line_count+1] = float(nx) btimemins[line_count+1] = int(row[10]) # start time for this race btimesecs[line_count+1] = int(row[11]) # start time for this race prevhcap[line_count+1] = btimemins[line_count+1] + (btimesecs[line_count+1]/60) #print ("phcap", prevhcap[line_count+1]) delta[line_count+1] = row[12] # difference in handicap def readhelmnames(): global highpyno, boatend, length, firststart,pstart with open('/home/pi/helmnames.csv') as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line = 0 offset = pstart -1 for row in csv_reader: #if line == 0: # headings row is not used if line > 0: # items[line + offset] = row[0] # helm name normsail[line + offset] = str(row[1]) # sail no of normal boat sailed normboat[line + offset] = row[2] # normal boat sailed #print("crew and normal boats",items[line+1], normsail[line+1], normboat[line+1]) line = line +1 boatend = line def writepershandicaps(): filepath = ("/home/pi/personalhandicaps" + str(filenumber) + ".csv") if pc == 1: filepath = "C:" + filepath with open(filepath, mode='w') as csvout_file: handicap_writer = csv.writer(csvout_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) handicap_writer.writerow([pershdr[0], pershdr[1], pershdr[2],pershdr[3], pershdr[4], pershdr[5],pershdr[6],pershdr[7], pershdr[8], pershdr[9],pershdr[10], pershdr[11], pershdr[12], pershdr[13], pershdr[14],pershdr[15]]) for i in range (3,37): handicap_writer.writerow([sailno[i], persboats[i], normstmin[i],normstsec[i],items[i],seasonstart[i],raced[i],posn[i],lasthc[i],nexthc[i],btimemins[i],btimesecs[i],newprev[i],delta[i]]) def writecpresult(): # write out file of results for crew pursut race global racedate, racetime, started, items, normboat, posn filepath = ("/home/pi/crewpursuit" + racedate + " " + racetime + ".csv")# if already there it overwrites it if pc == 1: filepath = "C:" + filepath cphdr[0] = "Helm" cphdr[1] = "Normal Boat" cphdr[2] = "Position" with open(filepath, mode='w') as csvout_file: handicap_writer = csv.writer(csvout_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) handicap_writer.writerow([cphdr[0], cphdr[1], cphdr[2]]) for i in range (3,crews+3): #print (i) if (started[i] == 1): #print (items[i], normboat[i], posn[i]) handicap_writer.writerow([items[i], normboat[i], posn[i]]) def readfileno(): global filenumber, racedate if pc == 1: path = "C:/home/pi/filenumber.csv" if pc == 0: path = "/home/pi/filenumber.csv" with open(path) as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line = 0 for row in csv_reader: if line == 0: filenumber = row[1] racedate = row[2] def writefileno(): global filenumber, racedate if pc == 1: path = "C:/home/pi/filenumber.csv" if pc == 0: path = "/home/pi/filenumber.csv" with open(path, mode='w') as csvout_file: fileno_writer = csv.writer(csvout_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) fileno_writer.writerow(["File number", filenumber,racedate]) #print("write fileno ",filenumber, racedate) def readracecalendar(): global calentry, racenum, racetype, rctype, length, starttime, racetime, racestarthh, racestartmm, racename global ddtext, mmtext, yyyytext, line, thours if pc==1: path = 'c:/home/pi/racecalendar.csv' if pc==0: path = '/home/pi/racecalendar.csv' racefound = 0 # set number of matching calendar entries to zero gettime() with open(path) as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line = 0 for row in csv_reader: if line == 0: #print(f'Column names are {", ".join(row)}') line = line + 1 else: if(row[4] == "End"): return caldd = int(row[0]) calmm = int(row[1]) calyyyy = int(row[2]) caltim = row[6] calhrs = int(caltim[0:2]) # start time hours passed = 0 if (calhrs < int(thours)): # too late for this instance of the race passed = 1 #print(caldd,calmm,calyyyy, calhrs, ddtext, mmtext, yyyytext) # test line #if(caldd == ddtext): #print("day match") #if(calmm == mmtext): #print("month match") if((caldd == ddtext) and (calmm == mmtext) and (calyyyy == yyyytext) and passed == 0 and racefound == 0): # if multiple races on one day, select the first one that starts after the current time racefound = racefound + 1 # add 1 to the number of calendar entries on that date #print("date match") racenum = row[3] racetype = row[4] rctype = 1 # default value if race type not reccognised if(racetype=="Pursuit"): rctype = 1 if(racetype=="Long Pursuit"): rctype = 1 if(racetype=="Crew Pursuit"): rctype = 2 if(racetype=="Personal Handicap"): rctype = 3 if(racetype=="Handicap"): rctype = 4 length = row[5] starttime = row[6] racetime = starttime raceshh = starttime[0:2] # extract hh from hh:mm string racesmm = starttime[-2:] # extract mm from hh:mm string #print (starttime, raceshh, racesmm) racestarthh = int(raceshh) racestartmm = int(racesmm) racename = row[7] calentry = 1 # say that calendar entry found line = line + 1 def calchcap(pynumber, rlen): # calculates handicap for a given PY no and race length global highpyno, firststart ratio = highpyno / rlen pnoverratio = pynumber / ratio diffby4 = (rlen - pnoverratio) * 4 rounded = round(diffby4) mins = rounded / 4 hand = mins + firststart #print("calchcap", pynumber, rlen, ratio, diffby4, rounded, hand) return (hand) def stdpursuit(): # std pursuit with just boat classes global length, btimemins, times, rtype, boatstatus, helmend if(rtype == 1): # race type has already been selected return startim() ttk.Label(mainframe, text="Boat Class",font=fontStyle1).grid(column=0, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Click on boat if racing",font=fontStyle1).grid(column=4, columnspan=2, row=1) ttk.Label(mainframe, text="Boat Class",font=fontStyle1).grid(column=4, row=13, sticky=(W, E)) ttk.Label(mainframe, text="Helm only",font=fontStyle1).grid(column=4, row=12, sticky=(W)) readboatpynos() # read py numbers and calculate boat handicaps boatclasses() boatstatus[boatend+1] = 0 # set end of race on by default boatstat (boatend+1) items[boatend+1] = "Race End" helmend = helmend + 1 #print (items) for n in range (3, end-1): ttk.Label(mainframe, text=timetext[n],font=fontStyle1).grid(column=2, row=n-1, sticky=W) timetext[helmend-1] = length btimemins[helmend-1] = length btimesecs[helmend-1] = 0 helmrow = 14 #print (end,helmend) for n in range (end-1, helmend-2): helmrow = helmrow + 1 ttk.Label(mainframe, text=timetext[n],font=fontStyle1).grid(column=6, row=helmrow-1, sticky=W) rtype = 1 # set to say race type has been selected def crewpursuit(): # pursuit race with crew details recorded so that laps and results can be logged global rtype, length, btimemins, times, btimemins, btimesecs, end, click, boatselect, nboat,timlab1,timlab2,selnorm,selothr,selsingle if(rtype == 1): # race type has already been selected return startim() boatselect = 1 # switch on boat selection by user ttk.Label(mainframe, text="Helm",font=fontStyle1).grid(column=0, row=1) click=ttk.Label(mainframe, text="Click on Helm if racing, then select boat",font=fontStyle1) click.grid(column=4, columnspan=3, row=1) ttk.Label(mainframe, text="Helm",font=fontStyle1).grid(column=4, row=13) #ttk.Label(mainframe, text="More",font=fontStyle1).grid(column=4, row=12) tk.Button(mainframe, text="Log results",command=getresults).grid(column=5, row=12, sticky=W) tk.Button(mainframe, text="Undo last item",command=undolast).grid(column=4, row=12, sticky=W) readhelmnames() nboat = "Normal boat Raced" selnorm = ttk.Button(mainframe, text=nboat,command= selectboat) # set up boat select buttons selnorm.grid(column=4, row=2, sticky=(W, E)) other = tk.StringVar() selothr = ttk.OptionMenu(mainframe, other, other2,comet,cometduo,dzero,laser,topper,moth,europe,supernova, enterprise,gp14,rs9,solution,nat12,lightning,duty,command=selectother) selothr.grid(column=5, row=2, sticky=(W, E)) single = tk.StringVar() single2 = "No Crew" selsingle = ttk.OptionMenu(mainframe, single, single2, ent1, gp141, nat121, comduo1,command=selectother) selsingle.grid(column=6, row=2, sticky=(W, E)) #print("line 416",btimemins,btimesecs) items[numpeople+1] = "Race end" peopleend = 22 people() # display people names and status labels btimemins[numpeople + 1] = length boatstatus[numpeople + 1] = 0 boatstat(numpeople + 1) # set end of race on by default end = numpeople + 4 for n in range (3, 22): #print(btimemins[n],btimesecs[n]) #print ("n", n) times[n] = str(btimemins[n]) +" : "+ str(btimesecs[n]) tmins = " " tsec1 = "" tsec2 = "" timetext[n] = str(tmins) + " : " + str(tsec1) + str(tsec2) timlab1[n] = ttk.Label(mainframe, text=timetext[n],font=fontStyle, width = 10) timlab1[n].grid(column=3, row=n-1, sticky=W) #print (tmins, tsec1, tsec2, timetext[n]) helmrow = 12 for n in range (22, numpeople+1): times[n] = str(btimemins[n]) +" : "+ str(btimesecs[n]) helmrow = helmrow + 1 tmins = " " tsec1 = "" tsec2 = "" timetext[n] = str(tmins) + " : " + str(tsec1) + str(tsec2) timlab2[n] = ttk.Label(mainframe, text=timetext[n],font=fontStyle) timlab2[n].grid(column=7, row=helmrow + 1, sticky=W) rtype = 1 # set to say race type has been selected getboathandicaps() # read in the handicaps for each boat class selnorm['text'] = "Normal Boat Raced" #print ("items, timemins, timetext ", items, btimemins, timetext) def closewindow(): if pc == 0: # if running on Raspberry Pi GPIO.cleanup() # clear GPIO port settings - use when closing timerun = 0 # switch off the timer closing = 1 # set closing down flag time.sleep(1) # wait 1 second for timer to stop root.destroy() # close main window def setrun(): global timerun timerun = 1 message(4) def horn(n): global hornicon, hnum if pc == 1: if(n == 1): hornicon['text']= "####" if(n == 0): hornicon['text'] = " " hornicon['text'] =str(hnum) hnum = hnum+ 1 return # dont try to sound horn if running on PC #hornsound.play() # play beep sound if(n == 1): GPIO.output(23, True) # switch on horn #print ("horn on") if(n == 0): GPIO.output(23, False) # switch off horn #print ("horn off") return def horntest(): global timerun, horncount, hornon if (timerun == 0): # clock not running yet horn(1) time.sleep(1) horn(0) if (timerun == 1): horncount = 2 # duration of horn horn(1) # switch on horn hornon = 1 # tell timesec() to count down then switch off horn def horncheck(): if pc == 1: return # dont check horn if running on pc hornreturn = GPIO.input(24) if(hornreturn == True): # return signal from horn connection present warning(0) # clear warning message return else: warning(1) # horn not connected def beep(): global halfsec, beepon, beepicon if pc == 1: if (beepon == 1): if (halfsec == 0): beepicon['text']= "beep" if (halfsec == 1): beepicon['text']= "boff" if (beepon == 0): beepicon['text']= "boff" #winsound.Beep(1000,250) # play beep for quarter of a second #winsound.PlaySound("SystemExit", winsound.SND_ALIAS) return # dont beep if running on pc if (beepon == 1): if (halfsec == 0): # this toggles halfsec variable between 0 and 1 #beepsound.play() # play beep sound # if using piezzo sounder, switch it on here p.start(50) # start buzzer with 50% duty cycle for frequency required if (halfsec == 1): # if using piezzo sounder, switch it off here p.stop() # stop pwm return else: p.stop() # stop beep def undolast(): global undo, y undo = 1 undoy = y boatstat(undoy) def boatstat(x): # does required actions when boat button pressed global boatstatus, b, position, normboat, boatselect, boatchosen, nboat, selnorm, selothr, boatracing, y global timehours, timemins, timesecs, hornon, endsecs, rctype, started, highlap, undo, slowfast global helmpyno, logres y = x # save & make vailable last called parameter #print("items + boatstatus arrays", items, boatstatus) if ((rctype == 2) or (rctype == 4)) & (logres == 0): # crew pursuit or std handicap race type #print("startedx", started[x], started, rctype) if started[x] ==1: # true if boat has gone past its start time prevend = lapendtime[x] lapendtime[x] = durnsecs # capture time when lap update is clicked laptime = lapendtime[x] - prevend # calculates the time for the lap if undo == 0: laps[x] = laps[x] + 1 #add 1 to laps for boat x if undo == 1: if laps[x] > 0: laps[x] = laps[x] - 1 #add 1 to laps for boat x laptime = 0 undo = 0 lapx = laps[x] if lapx < 2: # only display lap time for lap 2 onwards laptxt = "Lap " + str(lapx) else: laptxt = "Lap " + str(lapx) +"-" + str(laptime) b[x].set(laptxt) # write lap number if lapx > highlap: # this updates highlap with the highest lap completed so far highlap = lapx #print("call calcposns") calcposns() # calculate and display the current positions return # set status in col 2 to Yes or " " if (logres == 0) & (logendtime ==0): #print("boatstat",x,boatstatus[x]) # diagnostic code if (boatstatus[x] == 1): # if status is on then boat racing #print("x " ,x) b[x].set(" ") # delete the yes boatstatus[x] = 0 #and set status to not racing else: # if status wasn't on b[x].set("Yes") # display Yes boatstatus[x] = 1 # and set status to racing if logres == 1: # do this if result logging switched on positn = str(position) b[x].set(positn) posn[x] = position position = position + 1 message(7) # ask for boat that came in position return # temp code if logendtime == 1: # do this if capturing end times for handicap races gettime() endtime = str(timehours) +":"+ str(timemins) +":"+ str(timesecs) b[x].set(endtime) hornon = 1 horn(1) # switch on horn # calculate corrected time and display it endsecs[x] = timehours*3600 + timemins*60 + timesecs # get start time for race startsecs = racestarthh*3600 + racestartmm*60 + racestartss # adjust it for slow or fast handicap boat as appropriate startsecs = startsecs + (slowfast[x] * 60) # elapsed time = endsecs(x) - start time in seconds elapsedtime = endsecs[x] - startsecs # get PY number for boat from py table indexed by helmpyno # multiply elapsed time by 1000 and divide it by the boats PY number pynumb = py[helmpyno[x]] if pynumb > 0: # this is to catch a divide by zero if we click a finish for a boat that has not started corrected = round(elapsedtime * 1000 / pynumb) # round up if > .5 otherwise round down. As per RYA rules #print (elapsedtime, pynumb, corrected) # check RYA specified rounding is working correctly else: corrected = 0 if x < 22: timlab1[x]['text'] = corrected else: timlab2[x]['text'] = corrected #print ("x slowfast startsecs endsecs endtime elapsed corrected ", x,slowfast[x],startsecs,endsecs[x],endtime,elapsedtime,corrected) #print ("items[x] helmpyno[x] py[helmpyno] ", items[x], helmpyno[x], py[helmpyno[x]]) # more to add here if boatselect == 1: # Do this if we need to select the boat for a clicked on helm name nsail = normsail[x] nss = "-" + str(nsail) nboat = str(normboat[x]) + nss # set boat name & sail number boatchosen = x if (boatstatus[x] == 1): # boat just been selected #print("print nboat", nboat) selnorm['text'] = nboat # puts boat name & sail no in normal boat raced button """else: # remove boat from racing if rctype != 4: # don't remove boat class if std handicap boatracing['text'] = " " selnorm['text'] = "Normal boat raced" # clear boat name from normal boat raced button #print("x y ", x,y) if x < 21: timlab1[x]['text'] = " : " # clear the handicap time field if present else: timlab2[x]['text'] = " : " # clear the handicap time field if present """ def selectboat(): # select normally raced boat global boatchosen, nboat, selnorm, boatracing, y, firstcol boatracing = ttk.Label(mainframe, text= nboat,font=fontStyle1) #print ("boatchosen ","nboat" , boatchosen, nboat) upno = 8 if firstcol == 22: upno = 8 if boatchosen < firstcol: boatracing.grid(column=1, row=boatchosen-1, sticky=(W, E)) else: boatracing.grid(column=5, row=boatchosen-upno, sticky=(W, E)) selnorm['text'] = "Normal Boat raced" gethandicap(y) return def selectother(boatselected): # select boat not standard one global selothr,boatchosen,normboat, nboat,selsingle #print("boatselected ", boatselected) normboat[boatchosen] = boatselected nboat = boatselected selectboat() #print("selectother") selothr['text'] = "Other boat" selsingle['text'] = "No crew" return def gethandicap(x): # this looks up the handicap for a given boat class global timetext, timlab1, timlab2, nboat, btimemins, btimesecs, pyhctext, normboat, boatclass, fastslowno global pyhcmins, pyhcsecs, slowfast, helmpyno #and puts it in btimemins and btimesecs and timetext #print("x,normboat[x]" , x,normboat[x]) # x is the helm number and normboat[x] is that helm's boat for n in range (3, 28): #print(boatclass[n], pyhctext[n]) suffix = "" if normboat[x] == boatclass[n]: # this matches the helm's boat against the py table boats #print("n x normboat[x] boatclass[n] pyhctext[n]", n,x,normboat[x],boatclass[n],pyhctext[n]) if rctype == 4: # only do this for std handicap races #print ( " py[n] fastslowno ", py[n], fastslowno) if int(py[n]) > int(fastslowno): suffix = " Slow" slowfast[x] = slowhandicap else: suffix = " Fast " slowfast[x] = fasthandicap helmpyno[x] = n # stores the py number for the boat sailed against the helm number if x < 22: timlab1[x]['text'] = pyhctext[n] + suffix else: timlab2[x]['text'] = pyhctext[n] + suffix btimemins[x] = pyhcmins[n] btimesecs[x] = pyhcsecs[n] #print("normboat, boatclass, mins secs ", normboat[x], boatclass[n], pyhcmins[x],pyhcsecs[x]) return def calcposns(): # calculates and displays the current positions based on laps completed global listsize, laps, lapendtime, highlap, calcs, timlab1, timlab2, started, processed numstarted = 0 position = 1 for x in range (3, listsize): processed[x] = 0 # clear processed flags if started[x] == 1: numstarted = numstarted + 1 # count numver of boats currently started calcs[x] = ((laps[x] + 1) * 10000) - lapendtime[x] # calculate position value for all boats started #print("numstarted, x, calcs[x]",numstarted, x, calcs[x]) newhigh = 1 # initail value for while loop high = 200000 while (position <= numstarted): # while still positions to be allocated for x in range (3, listsize): #look at each helm's boat if started[x] == 1: #if ((calcs[x] > newhigh) and (calcs[x] <= high)): if ((calcs[x] > newhigh) and (processed[x] == 0)): newhigh = calcs[x] # save new highest newx = x # save boat no of new highest #print("newx newhigh",newx, newhigh) high = newhigh # save new highest one for next time round loop processed[newx] = 1 # if newx < 22: timlab1[newx]['text'] = position else: timlab2[newx]['text'] = position position = position + 1 #print("high, newhigh, newx, position",high,newhigh,newx,position) newhigh = 1 # reset value for new high to be found return def timesec(): # actions every half second - time, next start and countdown etc. global timehours, timemins, timesecs, halfsec, length global timetogomins, timetogosecs, timetogo global day, timerun, durnsecs, pausetime, prevboat global racestarthh, racestartmm, racestartss, nextstart,boatstarting global tcheck, boatstatus, hornon, horncount, beepon, started,laps, b if closing == 0: ttk.Label(mainframe).after(499, timesec) # this causes repeat every 1/2 second #print("timer called") #print("timer ticking") if (timerun == 0): # only run timesec if ready and time has been set return if (tcheck == 1): print ("timesec called before finished processing previous time") tcheck = 1 if (halfsec == 0): # this toggles halfsec variable between 0 and 1 halfsec = 1 horncheck() # check that horn is still connected else: halfsec = 0 #print("timer going") showtime() # get and display the current time temp = 0 # default value before being read if (pc==0): # only read temp if running on raspberry pi temp = os.popen("vcgencmd measure_temp").readline() # get core temp #print ( "core temp = ", temp) ctemp['text'] = temp # get time since start of race - negative if race not started yet timediff() # gets diff between current and start time in durnsecs if (durnsecs < 0): nextstart = 0 bsn['text'] = items[nextstart] timetogo = - durnsecs #print ("time to go", timetogo, durnsecs) timetogomins = timetogomm = int(timetogo / 60) timetogosecs = timetogoss = int(timetogo - (timetogomins * 60)) #print ("timeto go mins", timetogo, timetogomins, timetogosecs) #print ("time", timehours, timemins, timesecs) #print ("start", racestarthh, racestartmm, racestartss) #print("seconds", currsecs, startsecs, durnsecs) beepon = 0 if (timetogo < 11): # This beeps the pizzo sounder for 10 seconds before the first start beepon = 1 #print ("973 timetogo ", timetogo) if (timetogo < 67) and (timetogo > 61): # does 5 beeps with a minute to go beepon = 1 #print ("977 timetogo ", timetogo) beep() boatstarting = 0 #compare against times of racing boats if after start if (durnsecs >= 0): #print ("helmend", helmend) nextstart = helmend + 1 # default of end of std pursuit race if none still to start if (rctype == 2): # for crew pursuit use no of people nextstart = numpeople + 1 # default of end of race if none still to start if (rctype == 4): # for std handicap use no of people nextstart = numpeople + 1 # default of end of race if none still to start boatsecssave = 32500 timeminsave = 0 # default in case no boats set timesecsave = 0 # default in case no boats set #print("helmend and end", helmend, end) numcheck = helmend+1 # for boat classes race if (rctype == 2): # for crew pursuit use no of people numcheck = numpeople + 1 # if (rctype == 4): # for std handicap use no of people numcheck = numpeople + 1 # if (rctype == 3): # for personal handicap race numcheck = numpeople + 2 #print("numcheck", numcheck) for i in range (numcheck+1): # this looop finds the next boat starting #print ("boatstartcheck", i, boatstatus[i], durnsecs) if (boatstatus[i] == 1): # boat is racing #print ("boatstartsecs", i, btimemins[i], btimesecs[i], times[i]) btimemin = int(btimemins[i]) boatstartsecs = (btimemin *60) + btimesecs[i] #print ("boatstartsecs", i, boatstartsecs, btimemins, btimesecs) #if (rctype == 4): # if std handicap race #boatstartsecs = 600 # need to fix this to be 600 or 900 if slow or fast start boat if (boatstartsecs >= durnsecs): # has it not started yet timetogo = boatstartsecs - durnsecs #print ("to go secs",i, timetogo) if (boatstartsecs <= boatsecssave): #boat has not started yet boatsecssave = boatstartsecs nextstart = i #print("boatsecssave", i, boatsecssave) timetogomins = int(timetogo / 60) timetogosecs = int(timetogo - (timetogomins * 60)) timeminsave = timetogomins timesecsave = timetogosecs boatstarting = 1 #print("next 829", i, timetogomins, timetogosecs) if (boatstartsecs < durnsecs) & (boatstartsecs != 0): # boat has started # if boat has started we set the started flag and replace yes with the lap number (initially zero) #print ("i boatstartsecs durnsecs", i, boatstartsecs, durnsecs) started[i] = 1 if logres == 0: # don't do items below if logging results if rctype == 1: #std pursuit race - so dont count laps laptxt = "Started" b[i].set(laptxt) else: #if (laps[i] == 0) & (logres == 0): if (laps[i] == 0): lapx = 0 laptxt = "Lap " + str(lapx) b[i].set(laptxt) #b[i].set(lapx) timetogomm = timeminsave timetogoss = timesecsave if (durnsecs >= 0): beepon = 0 # beeps off by default if (boatstarting == 1): # if there are any boats still to start or end signal not sounded yet if (timetogomm == 0 and timetogoss < 10): beepon = 1 # enable beeps if (timetogomm == 0 and timetogoss == 0): horncount = 2 # duration of horn hornon = 1 horn(1) # switch on horn beep() # sound beep if enabled #print (durationhh, durationmm, durationss) #print ("timeto go to display", timetogomins, timetogosecs) # dignostic test line if (prevboat != items[nextstart]): bnext = items[nextstart] bsn['text'] = bnext prevboat = items[nextstart] # count down horn duration if being sounded if (hornon == 1): horncount = horncount - 1 if (horncount == 0): horn(0) # switch off horn hornon = 0 horncount = 2 # time to go to next start ttgm['text'] = timetogomm ttgs['text'] = timetogoss tcheck = 0 # return def gettime(): global timehours, timemins, timesecs, thours, tmins, tsecs now = datetime.now() thours = now.strftime("%H") tmins = now.strftime("%M") tsecs = now.strftime("%S") timehours = int(thours) timemins = int(tmins) timesecs = int(tsecs) def timediff(): # calculate time in seconds between current time and the race start time global timehours, timemins, timesecs, durnsecs, currsecs, startsecs global racestarthh, racestartmm, racestartss # gettime() # get current hours, mins & seconds - already have this from showtime currsecs = ((60 * timehours)+ timemins)*60 + timesecs startsecs = ((60 * racestarthh)+ racestartmm)*60 + racestartss durnsecs = currsecs - startsecs #print("diff", currsecs, startsecs, durnsecs) def showtime(): # put time on screen global timehours, timemins, timesecs global th,tm,ts if(pausetime == 0): gettime() # fill in the empty labels for current time th['text'] = timehours tm['text'] = timemins ts['text'] = timesecs def pauserun(): # moves the start time forward 5 minutes each time the delay button is presses global pausetime, racestarthh, racestartmm racestartmm = racestartmm + 5 if racestartmm > 59 : racestartmm = racestartmm - 60 racestarthh = racestarthh + 1 message(10) # put new start time on top line #print ("Pause", pausetime) # if (pausetime == 0): # 0 means not paused # pausetime = 1 # return # if (pausetime == 1): # 1 means time clock is stopped # pausetime = 0 def stdhcap():# Std Handicap race with crew details recorded so that laps and results can be logged global rtype, length, btimemins, times, btimemins, btimesecs, end, click, boatselect, nboat,selnorm, timlab1, fastslowno global timlab2, pstart, peopleend, firstcol, selothr, selsingle if(rtype == 1): # race type has already been selected return startim() boatselect = 1 # switch on boat selection by user firstcol=22 # used in selbat to determine no of people in first column ttk.Label(mainframe, text="Helm",font=fontStyle1).grid(column=0, row=1) click=ttk.Label(mainframe, text="Click on Helm if racing, then select boat",font=fontStyle1) click.grid(column=4, columnspan=3, row=1) ttk.Label(mainframe, text="Helm",font=fontStyle1).grid(column=4, row=13) ttk.Label(mainframe, text="More",font=fontStyle1).grid(column=4, row=12) tk.Button(mainframe, text="Log finishes",command=logtimes).grid(column=5, row=12, sticky=W) readboatpynos() # read py numbers and calculate boat handicaps pstart = 4 #no of start signals readhelmnames() handicapstarts() # put on 15 minute gun for fast handiacp start and rename 10 minute gun as slow hcap start nboat = "Normal boat Raced" selnorm = ttk.Button(mainframe, text=nboat,command= selectboat) # set up boat select buttons selnorm.grid(column=4, row=2, sticky=(W, E)) other = tk.StringVar() selothr = ttk.OptionMenu(mainframe, other, other2,comet,cometduo,dzero,laser,topper,moth,europe,supernova, enterprise,gp14,rs9,solution,nat12,lightning,duty,command=selectother) selothr.grid(column=5, row=2, sticky=(W, E)) single = tk.StringVar() single2 = "No Crew" selsingle = ttk.OptionMenu(mainframe, single, single2, ent1, gp141, nat121, comduo1,command=selectother) selsingle.grid(column=6, row=2, sticky=(W, E)) items[numpeople + 1] = "Race end" peopleend = 22 people() # display people names and status labels btimemins[numpeople + 1] = 0 # was length boatstat(numpeople + 1) # set end of race on by default end = numpeople + 2 for n in range (3, 22): #print(btimemins[n],btimesecs[n]) #print ("n", n) times[n] = str(btimemins[n]) +" : "+ str(btimesecs[n]) tmins = " " tsec1 = "" tsec2 = "" timetext[n] = str(tmins) + " : " + str(tsec1) + str(tsec2) timlab1[n] = ttk.Label(mainframe, text=timetext[n],font=fontStyle, width = 10) timlab1[n].grid(column=3, row=n-1, sticky=W) #print (tmins, tsec1, tsec2, timetext[n]) helmrow = 12 for n in range (22, numpeople+1): times[n] = str(btimemins[n]) +" : "+ str(btimesecs[n]) helmrow = helmrow + 1 tmins = " " tsec1 = "" tsec2 = "" timetext[n] = str(tmins) + " : " + str(tsec1) + str(tsec2) timlab2[n] = ttk.Label(mainframe, text=timetext[n],font=fontStyle) timlab2[n].grid(column=7, row=helmrow + 1, sticky=W) #print ("items, timetext ", items, timetext) rtype = 1 # set to say race type has been selected message(3) #getboathandicaps() # read in the handicaps for each boat class selnorm['text'] = "Normal Boat Raced" return def logtimes(): global logendtime, boatselect, rac, rac2, rac1, rac3 logendtime = 1 click["text"] = " " # remove click on crew to select message rac["text"] = "Finish Time" rac2["text"] = "Finish Time" rac1["text"] = "Corrected" rac3["text"] = "Corrected" message(9) # click on helm name to record finish time and sound horn boatselect = 0 def pershcap(): global length, btimemins, times, rtype, btimemins, btimesecs, end, click if(rtype == 1): # race type has already been selected return startim() ttk.Label(mainframe, text="Crew",font=fontStyle1).grid(column=0, row=1) click=ttk.Label(mainframe, text="Click on crew if racing",font=fontStyle1) click.grid(column=4, columnspan=2, row=1) ttk.Label(mainframe, text="Crew",font=fontStyle1).grid(column=4, row=16) ttk.Label(mainframe, text="More",font=fontStyle1).grid(column=4, row=15) tk.Button(mainframe, text="Log results",command=getresults).grid(column=5, row=13, sticky=W) readpershandicaps() #print("line 560",btimemins,btimesecs) items[numpeople+1] = "Race end" people() # display people names and status labels btimemins[numpeople+1] = length #btimemins[numpeople+1] = 90 #length = 90 boatstat(numpeople+1) # set end of race on by default end = numpeople+2 for n in range (3, 28): # us PY for boat to determine if fast or slow start time #print(btimemins[n],btimesecs[n]) times[n] = str(btimemins[n]) +" : "+ str(btimesecs[n]) tmins = int(btimemins[n]) tsec1 = int(btimesecs[n] / 10) tsec2 = int(btimesecs[n] - (tsec1 * 10)) timetext[n] = str(tmins) + " : " + str(tsec1) + str(tsec2) ttk.Label(mainframe, text=timetext[n],font=fontStyle, width = 10).grid(column=3, row=n-1, sticky=W) #print (tmins, tsec1, tsec2, timetext[n]) helmrow = 15 for n in range (28, numpeople + 2): times[n] = str(btimemins[n]) +" : "+ str(btimesecs[n]) helmrow = helmrow + 1 tmins = int(btimemins[n]) tsec1 = int(btimesecs[n] / 10) tsec2 = int(btimesecs[n] - (tsec1 * 10)) timetext[n] = str(tmins) + " : " + str(tsec1) + str(tsec2) ttk.Label(mainframe, text=timetext[n],font=fontStyle).grid(column=7, row=helmrow + 1, sticky=W) rtype = 1 # set to say race type has been selected return def people(): global peopleend, pstart # this sets up all the people buttons and their status # note that buttons and label use tk instead of ttk so as to be more configurable for font size etc #print (persboats) for n in range (pstart, peopleend): tk.Button(mainframe, text=items[n], command=partial(boatstat, n), font = fontStyle, width = 16).grid(column=0, row=n-1, sticky=W) tk.Label(mainframe, text=persboats[n],font=fontStyle).grid(column=1, row=n-1, sticky=(W, E)) tk.Label(mainframe, textvariable=b[n],font=fontStyle).grid(column=2, row=n-1, sticky=(W, E)) helmend = numpeople + 1 helmrow = 13 for n in range (peopleend, helmend): helmrow = helmrow + 1 # which row to put helm only boats tk.Button(mainframe, text=items[n],command=partial(boatstat, n), font = fontStyle, width = 16).grid(column=4, row=helmrow, sticky=W) tk.Label(mainframe, text=persboats[n],font=fontStyle).grid(column=5, row=helmrow, sticky=(W, E)) tk.Label(mainframe, textvariable=b[n],font=fontStyle).grid(column=6, row=helmrow, sticky=(W, E)) def boatclasses(): global b, end, helmend # this sets up all the boat class buttons and their status # note that buttons and label use tk instead of ttk so as to be more configurable for font size etc fontStyle5 = tkFont.Font(family="Lucida Grande", size=10) for n in range (3, end-1): tk.Button(mainframe, text=items[n], command=partial(boatstat, n), font = fontStyle5, width = 12).grid(column=0, row=n-1, sticky=W) tk.Label(mainframe, textvariable=b[n],font=fontStyle5).grid(column=1, row=n-1, sticky=(W, E)) helmrow = 13 for n in range (end-1, helmend-1): helmrow = helmrow + 1 # which row to put helm only boats tk.Button(mainframe, text=items[n],command=partial(boatstat, n), font = fontStyle5, width = 12).grid(column=4, row=helmrow, sticky=W) tk.Label(mainframe, textvariable=b[n],font=fontStyle5).grid(column=5, row=helmrow, sticky=(W, E)) def startsignals(): items[0] = "First Gun" items[1] = "Five Minute" items[2] = "First Start" times[0] = 0 times[1] = 5 times[2] = 10 btimemins[0] = 0 btimemins[1] = 5 btimemins[2] = 10 btimesecs[0] = btimesecs[1] = btimesecs[2] = 0 times[end-1] = length boatstatus[0] = 1 # set first gun "boat class" on by default boatstatus[1] = 1 # set five minute "boat class" on by default boatstatus[2] = 1 # set first start "boat class" on by default def handicapstarts(): items[2] = "Slow Hcap Start" items[3] = "Fast Hcap Start" times[3] = 15 btimemins[3] = 15 boatstatus[3] = 1 # set fast hcap start on by default def message(n): global rtypret, rtimret, rlenret, position, racestarthh, racestartmm if(n==1): ins['text'] = "Use dropdown buttons to select race type, start time and length, then click OK " if(n==2): ins['text'] = ("Selected: "+ rtypret + " race at " + rtimret + " for " + rlenret + " minutes") if(n==4): ins['text'] = "Instructions: 4) Click on boats to say that they are racing " if(n==5): ins['text'] = "Instructions: 5) Click on HORN to check it is connected and working ." if(n==6): ins['text'] = "Warning: HORN is not connected" if(n==7): ins['text'] = "Click on boat that came in position " + str(position) + " or click or Save Results if done" if(n==8): ins['text'] = "Race results have been saved in file named pershandicaps" + str(filenumber) + " and new handicaps generated" if(n==9): ins['text'] = "Click on helm name when boat finishes required no of laps to record finish time and sound horn" if(n==10): sthh = str(racestarthh) stmm = str(racestartmm) if stmm == "0": stmm = "00" if stmm == "5": stmm = "05" ins['text'] = "Race Start delayed to " + sthh + ":" + stmm #print("message 10") if (n==0): lenstr = str(length) ins['text'] = ("Scheduled race is: " + daytext + " "+ datetext+" at " + starttime + ". It is a " + lenstr +" minute " + racetype +" race for "+ racename) def warning(n): if(n==0): # reset warning message warn['text'] = "" if(n==1): warn['text'] = "Warning: HORN is not connected" def getresults(): global logres, rac, rac2, position, click, numinrace, raced numinrace = 0 # determine how many people were racing, before positions are entered for i in range(3,numpeople+1): if(boatstatus[i] == 1): raced[i] = "yes" numinrace = numinrace + 1 #print(i,boatstatus[i],numinrace) logres=1 # flag to say clicking on person logs result position = 1 # set first position to log click["text"] = " " # remove click on crew to select message message(7) # ask for boat that came in position # add buttons for reset position count and positions save tk.Button(mainframe, text="Reset to prev",command=resetposns).grid(column=5, row=11, sticky=W) if rctype == 2: # for crew pursuit race save race results tk.Button(mainframe, text="Save results",command=savecpresults).grid(column=4, row=11, sticky=W) if rctype == 3: # for personal handicaps save personal handicap details tk.Button(mainframe, text="Save results",command=savephresults).grid(column=4, row=11, sticky=W) rac['text'] = "Position" rac2['text'] = "Position" def resetposns(): global position if (position > 1): position = position - 1 message(7) # ask for boat that came in position def savephresults(): # save personal handicap results global filenumber, racedate readfileno() # get currenmt file number racedate = today # current date yyyy-mm-dd filenumber = int(filenumber) + 1 calcnew() # calculate new handicaps pershdr[14] = filenumber # file number in header persfilenum = filenumber # file no at end of filename pershdr[15] = racedate # current date yyyy-mm-dd writefileno() # update save file number count writepershandicaps() message(8) # return def savecpresults(): #save crew pursuit race results global racedate, racetime, rtypret, rtimret #racetype = rtypret racedate = str(today) # current date yyyy-mm-dd rtimret = racetime #print (racetype, " ", rtimret, " ") racetime = str(rtimret)[0:2] + str(rtimret)[3:5] #print (racetype, " ", racedate, " ", racetime) writecpresult() ttk.Label(mainframe, text="Results saved",font=fontStyle4).grid(column=6, columnspan = 3, row=7, rowspan=2, sticky=(E)) return def calcnew(): # calculate new handicaps for the next race global numinrace,hcsec,hcxlt,lasthc,nexthc,rndnxt,delta m1factor=2 m2factor=2 lengthmins = length - 10 # e.g. 80 or 110 lengthsecs = lengthmins *60 tfor25mins = 3300 # numinrace glogal item brought in from getresults function for i in range(3, numpeople+1): #print(i, raced[i], numinrace, posn[i],lengthsecs) if(raced[i] == "yes"): hcsec[i] = ((numinrace/2 - posn[i])*m1factor)+((50-(posn[i]/numinrace*100))*m2factor) scap = seasonstart[i] #print ("scap",scap, seasonstart[i]) starthcap[i] = int(scap) hcxlt[i] = hcsec[i]*(lengthsecs-starthcap[i]-hcsec[i])/tfor25mins nexthc[i] = float(lasthc[i]) + hcxlt[i] nxtstart[i] = starthcap[i] + nexthc[i] rndnxt[i] = round((nxtstart[i])/15,0)*15/60 newprev[i] = btimemins[i] + (btimesecs[i]/60) #set btimemins and btimesec from value in rndnxt btimemins[i] = int(rndnxt[i]) btimesecs[i] = int((rndnxt[i] - btimemins[i]) * 60) delta[i] = rndnxt[i] - prevhcap[i] #print(hcsec[i],hcxlt[i],starthcap[i],lasthc[i],nexthc[i],nxtstart[i],rndnxt[i],prevhcap[i],delta[i]) else: #lasthc just keeps previously read value posn[i] = "" nexthc[i] = lasthc[i] newprev[i] = prevhcap[i] delta[i] = 0 def plannedrace(): # get here if Ok button clicked for race details shown at start global timerun, racestarthh, racestartmm, rctype, hdok, hdnok, hdokb, hdokc if (rctype == 1): hdokb = ttk.Button(mainframe, text = "Std Pursuit (Boats)", command=stdselected) hdokb.grid(column=6, row=1, sticky=W) hdokc = ttk.Button(mainframe, text = "Crew Pursuit (Helms)", command=crewselected) hdokc.grid(column=7, row=1, sticky=W) #sethdrs1() #stdpursuit() if (rctype == 2): sethdrs5() crewpursuit() if (rctype == 3): sethdrs4() pershcap() if (rctype == 4): sethdrs6() stdhcap() def startim(): # global hdok, hdnok, timerun timerun = 1 # set clock running sethdrs2() sethdrs3() hdok.destroy() hdnok.destroy() def stdselected(): global rctype, hdokb, hdokc, hdok, hdnok rctype = 1 hdok.destroy() hdnok.destroy() hdokb.destroy() hdokc.destroy() sethdrs1() stdpursuit() def crewselected(): global rctype, hdokb, hdokc, hdok, hdnok rctype = 2 hdok.destroy() hdnok.destroy() hdokb.destroy() hdokc.destroy() sethdrs5() crewpursuit() def setgrid(): dash="--------------" ttk.Label(mainframe, text=dash).grid(column=0, row=0) ttk.Label(mainframe, text=dash).grid(column=1, row=0) ttk.Label(mainframe, text=dash).grid(column=2, row=0) ttk.Label(mainframe, text=dash).grid(column=3, row=0) ttk.Label(mainframe, text=dash).grid(column=4, row=0) ttk.Label(mainframe, text=dash).grid(column=5, row=0) ttk.Label(mainframe, text=dash).grid(column=6, row=0) ttk.Label(mainframe, text=dash).grid(column=7, row=0) ttk.Label(mainframe, text=dash).grid(column=8, row=0) ttk.Label(mainframe, text=dash).grid(column=9, row=0) ttk.Label(mainframe, text=dash).grid(column=10, row=0) ttk.Label(mainframe, text=dash).grid(column=11, row=0) ttk.Label(mainframe, text=dash).grid(column=12, row=0) ttk.Label(mainframe, text=dash).grid(column=13, row=0) ttk.Label(mainframe, text=dash).grid(column=14, row=0) def othermenus(): global rtypret, rtimret, rlenret, menu1, menu2, menu3, menu4 hdok.destroy() hdnok.destroy() label1 = tk.StringVar() label2 = tk.StringVar() label3 = tk.StringVar() rtypret = "Pursuit" # default values if not entered on other race drop down menus rtimret = "11:00" rlenret = "90" message(1) # select drop down items rtyp = " " rtyp1 = "Pursuit" rtyp2 = "Crew Pursuit" rtyp3 = "Personal Handicap" rtyp4 = "Std Handicap" menu1 = ttk.OptionMenu(mainframe, label1, rtyp, rtyp1, rtyp2, rtyp3, rtyp4, command=callback1) menu1.grid(column=1, row=4, columnspan=2) label1.set('type') gettime() thoursminus1 = str(timehours - 1) time0 = thoursminus1 +":45" time1 = thours +":00" time2 = thours +":15" time3 = thours +":30" time4 = thours +":45" thoursplus1 = str(timehours + 1) time5 = thoursplus1 +":00" time6 = thoursplus1 +":15" time7 = thoursplus1 +":30" time8 = thoursplus1 +":45" thoursplus2 = str(timehours + 2) time9 = thoursplus2 +":00" time10 = thoursplus2 +":15" time11 = thoursplus2 +":30" time12 = thoursplus2 +":45" thoursplus3 = str(timehours + 3) time13 = thoursplus3 +":00" menu2 = ttk.OptionMenu(mainframe, label2, time0,time0,time1,time2,time3,time4,time5,time6,time7,time8,time9,time10,time11,time12,time13, command=callback2) menu2.grid(column=3, row=4, columnspan=2) label2.set('time') rlen = " " rlen1 = "90" rlen2 = "120" rlen3 = "30" rlen4 = "45" rlen5 = "60" menu3 = ttk.OptionMenu(mainframe, label3, rlen, rlen1, rlen2, rlen3, rlen4, rlen5, command=callback3) menu3.grid(column=5, row=4, columnspan=2) label3.set('Minutes') menu4 = ttk.Button(mainframe, text="OK",command=done) menu4.grid(column=7, row=4) def callback1(rtyp): # race type global rtypret rtypret = rtyp #print(rtyp) def callback2(rtim): # start time global rtimret, racetime rtimret = rtim racetime = rtimret #print(rtim) def callback3(rlen): # race length global rlenret,length rlenret = rlen length = int(rlenret) #print(rlen) def done(): global rtypret, rtimret, rlenret, length, rctype, timerun global racestarthh, racestartmm, racestartss #print(rtypret, rtimret, rlenret) length = int(rlenret) # set race length if (rtimret[1] == ":"): rtimret = "0" + rtimret racestarthh = int(rtimret[0:2]) racestartmm = int(rtimret[3:5]) racestartss = 0 times[end-1] = length #print(rlenret, length) if(rtypret=="Pursuit"): rctype = 1 sethdrs1() stdpursuit() if(rtypret=="Crew Pursuit"): rctype = 2 sethdrs5() crewpursuit() if(rtypret=="Personal Handicap"): rctype = 3 sethdrs4() # headers with boat in it pershcap() if(rtypret=="Std Handicap"): rctype = 4 sethdrs6() stdhcap() sethdrs2() showtime() # display current time on screen timerun = 1 # set clock running sethdrs3() menu1.destroy() # remove the race type dropdown menu2.destroy() # remove the time dropdown menu3.destroy() # remove the length dropdown menu4.destroy() # remove the OK button message(2) # display returned or default values #print ("done finished") return def sethdrs1(): # for std boat class pursuits ttk.Label(mainframe, text="Racing?",font=fontStyle1).grid(column=1, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Start Time",font=fontStyle1).grid(column=2, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Racing?",font=fontStyle1).grid(column=5, row=13, sticky=(W, E)) ttk.Label(mainframe, text="Start Time",font=fontStyle1).grid(column=6, row=13, sticky=(W, E)) def sethdrs2(): global th,tm,ts # set up current and togo time empty fields ttk.Label(mainframe, text="Current Time",font=fontStyle1).grid(column=6, row=3, sticky=(E)) ttk.Label(mainframe, text="Hours",font=fontStyle1).grid(column=5, row=4, sticky=(E)) ttk.Label(mainframe, text="Mins",font=fontStyle1).grid(column=6, row=4, sticky=(E)) ttk.Label(mainframe, text="Secs",font=fontStyle1).grid(column=7, row=4, sticky=(E)) th = ttk.Label(mainframe, font=fontStyle4) # time hours th.grid(column=5, row=5, rowspan=2, sticky=E) tm = ttk.Label(mainframe, font=fontStyle4) # time minutes tm.grid(column=6, row=5, rowspan=2, sticky=E) ts = ttk.Label(mainframe, font=fontStyle4) # time seconds ts.grid(column=7, row=5, rowspan=2, sticky=E) def sethdrs3(): global ctemp, bsn, ttgm, ttgs, hornicon, beepicon # set up close and horn buttons ttk.Button(mainframe, text="Close",command=closewindow).grid(column=7, row=12, sticky=W) hstate=1 ttk.Button(mainframe, text="HORN",command=horntest).grid(column=8, row=12, sticky=W) ttk.Button(mainframe, text="Delay start",command=pauserun).grid(column=6, row=12, sticky=W) hornicon = ttk.Label(mainframe, font=fontStyle1) # time hours hornicon.grid(column=9, row=12, sticky=W) beepicon = ttk.Label(mainframe, font=fontStyle1) # time hours beepicon.grid(column=9, row=13, sticky=W) # This puts up the headers ttk.Label(mainframe, text="Next start",font=fontStyle1).grid(column=4, row=7, sticky=(E)) ttk.Label(mainframe, text="In:",font=fontStyle1).grid(column=5, row=9, sticky=(E)) ttk.Label(mainframe, text="Mins",font=fontStyle1).grid(column=6, row=9, sticky=(E)) ttk.Label(mainframe, text="Secs",font=fontStyle1).grid(column=7, row=9, sticky=(E)) # set up current and togo time empty fields th = ttk.Label(mainframe, font=fontStyle4) # time hours th.grid(column=5, row=5, rowspan=2, sticky=E) tm = ttk.Label(mainframe, font=fontStyle4) # time minutes tm.grid(column=6, row=5, rowspan=2, sticky=E) ts = ttk.Label(mainframe, font=fontStyle4) # time seconds ts.grid(column=7, row=5, rowspan=2, sticky=E) # set up time to go minutes and seconds empty fields ttgm = ttk.Label(mainframe, font=fontStyle4) # time to go minutes ttgm.grid(column=6, row=10, rowspan=2, sticky=E) ttgs = ttk.Label(mainframe, font=fontStyle4) # time to go seconds ttgs.grid(column=7, row=10, rowspan=2, sticky=E) # set up next boat to start empty field bsn = ttk.Label(mainframe,font=fontStyle4) bsn.grid(column=5, columnspan=5, row=7, rowspan=2) #set up core temperature label field #fontStyle1 = tkFont.Font(family="Lucida Grande", size=12) ctemp = ttk.Label(mainframe, font=fontStyle1) # time seconds ctemp.grid(column=8, row=1, sticky=W) def sethdrs4(): # headers just for personal handicaps global rac, rac2 ttk.Label(mainframe, text="Boat",font=fontStyle1).grid(column=1, row=1, sticky=(W, E)) rac = ttk.Label(mainframe, text="Racing?",font=fontStyle1) rac.grid(column=2, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Start Time",font=fontStyle1).grid(column=3, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Boat",font=fontStyle1).grid(column=5, row=13, sticky=(W, E)) rac2 = ttk.Label(mainframe, text="Racing?",font=fontStyle1) rac2.grid(column=6, row=13, sticky=(W, E)) ttk.Label(mainframe, text="Start Time",font=fontStyle1).grid(column=7, row=13, sticky=(W, E)) def sethdrs5(): # headers just for crew pursuit global rac, rac1, rac2, rac3 ttk.Label(mainframe, text="Boat & Sail no. ",font=fontStyle1).grid(column=1, row=1, sticky=(W, E)) rac = ttk.Label(mainframe, text="Racing?/Lap",font=fontStyle1) rac.grid(column=2, row=1, sticky=(W, E)) rac1 = ttk.Label(mainframe, text="Start/Posn",font=fontStyle1) rac1.grid(column=3, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Boat & Sail no",font=fontStyle1).grid(column=5, row=13, sticky=(W, E)) rac2 = ttk.Label(mainframe, text="Racing?/Lap",font=fontStyle1) rac2.grid(column=6, row=13, sticky=(W, E)) rac3 = ttk.Label(mainframe, text="Start/Posn",font=fontStyle1) rac3.grid(column=7, row=13, sticky=(W, E)) def sethdrs6(): # headers just for std handicap global rac, rac1, rac2, rac3 ttk.Label(mainframe, text="Boat & Sail no. ",font=fontStyle1).grid(column=1, row=1, sticky=(W, E)) rac = ttk.Label(mainframe, text="Racing?",font=fontStyle1) rac.grid(column=2, row=1, sticky=(W, E)) rac1 = ttk.Label(mainframe, text="Start",font=fontStyle1) rac1.grid(column=3, row=1, sticky=(W, E)) ttk.Label(mainframe, text="Boat & Sail no",font=fontStyle1).grid(column=5, row=13, sticky=(W, E)) rac2 = ttk.Label(mainframe, text="Racing?",font=fontStyle1) rac2.grid(column=6, row=13, sticky=(W, E)) rac3 = ttk.Label(mainframe, text="Start",font=fontStyle1) rac3.grid(column=7, row=13, sticky=(W, E)) # ************************************************************************* # Main program logic starts here root = Tk() root.geometry("1366x768") #Width x Height. Full screen is 1280 x 1280 pixels or widescreen 1366 x768 root.title("Boatstart") mainframe = ttk.Frame(root, padding="3 3 335 ") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) fontStyle = tkFont.Font(family="Lucida Grande", size=12) fontStyle1 = tkFont.Font(family="Lucida Grande", size=12) fontStyle2 = tkFont.Font(family="Lucida Grande", size=14) fontStyle3 = tkFont.Font(family="Lucida Grande", size=14) fontStyle4 = tkFont.Font(family="Lucida Grande", size=30) #This sets up the array b[] where the "Yes" or " " is returned from #the function "boatstat" after a button is pressed to say if a boat is racing # Also the timetext array that holds the text version of the boat start time for i in range (nummax+2): b[i] = StringVar() timetext[i] = float() setgrid() # predefines the columns # set up instructions panel at top of screen ins = tk.Label(mainframe,font=fontStyle1, anchor='w') ins.grid(column=0, columnspan=8, row=0, sticky=W) warn = tk.Label(mainframe,font=fontStyle1, anchor='w') warn.grid(column=7, columnspan=3, row=15, sticky=W) # get day, date and time today = date.today() # format is yyyy-mm-dd my_date = date.today() daytext = calendar.day_name[my_date.weekday()] # day of the week datetext = today.strftime("%d %B, %Y") # ddmmyyyy date1 = today.strftime("%d/%m/%Y") #print (datetext, date1) # This extracts DD, MM and YYYY here into ddtext, mmtext and yyyytext ddtext = int(date1[0:2]) # extracts first 2 digits mmtext = int(date1[3:5]) # extracts 4th and 5th digits yyyytext = int(date1[-4:]) # extracts last 4 digits #print (ddtext,mmtext, yyyytext) # default values calentry = 0 # set flag in case no calendar entry found racenum = 0 # default values in case not found in calendar racetype = "Pursuit" length = 90 starttime = "11:00" racename = "an undefined series" readracecalendar() # read race type, length and start time from calendar entry #print (ddtext, mmtext, yyyytext, calentry, line) #print (racenum, racetype, length, starttime, racename) #If no matching calendar entry found, default values used if (calentry == 0): racetype = "Pursuit" rctype = 1 if (daytext == "Wednesday"): starttime = "19:15" if (daytext == "Saturday"): starttime = "14:30" if (daytext == "Sunday"): starttime = "11:00" raceshh = starttime[0:2] # extract hh from hh:mm string racesmm = starttime[-2:] # extract mm from hh:mm string #print (starttime, raceshh, racesmm) racestarthh = int(raceshh) racestartmm = int(racesmm) racetime = starttime if (calentry ==1): # valid calendar entry read message(0) ttk.Label(mainframe, text="software v 39",font=fontStyle1).grid(column=10, row=0, sticky=(W, E)) hdok = ttk.Button(mainframe, text = "Select if OK", command=plannedrace) hdok.grid(column=6, row=1, sticky=W) hdok["state"] = ACTIVE hdnok = ttk.Button(mainframe, text = "Other race", command=othermenus) hdnok.grid(column=7, row=1, sticky=W) hdnok["state"] = ACTIVE #timemenu() message(0) # put expected race on message line day = 0 startsignals() # set up times and names for start and end signals #set up a timer for every 1/2 second gettime() timesec() # start the every half second repeat function #print ("timesec first call") for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) root.bind('', boatstat) root.mainloop()