2023-11-17 20:52:44 +00:00
import argparse
import file_io
2023-12-05 21:39:12 +00:00
import datetime
2023-11-17 20:52:44 +00:00
import iof_xml
2023-11-19 16:54:27 +00:00
import serial
2023-11-21 21:32:14 +00:00
import subprocess
import os
2023-11-19 16:54:27 +00:00
import otime
2023-11-21 21:32:14 +00:00
import iof_xml
import pdf
from pdf import format_m_s
from rich import print
2023-11-23 23:42:02 +00:00
import search_tui
2023-11-17 20:52:44 +00:00
2023-12-02 12:25:17 +00:00
from rich . traceback import install
2023-12-05 21:39:12 +00:00
from rich import inspect
2023-12-02 12:25:17 +00:00
install ( show_locals = True )
2023-11-17 20:52:44 +00:00
# Main entrypoint for now. Cli with two options; init will make the files needed and run will start the program from the specified directory
def main ( ) :
parser = argparse . ArgumentParser ( description = ' Otime very alpha version 😁 ' )
subparsers = parser . add_subparsers ( dest = ' command ' )
2023-11-21 21:32:14 +00:00
parser_init = subparsers . add_parser ( ' init ' , help = ' Create project files ' )
2023-11-17 20:52:44 +00:00
parser_init . add_argument ( ' --entries ' , required = True , dest = ' entries_file ' , action = ' store ' , default = ' ./ ' ,
help = ' The xml file with entries ' )
parser_init . add_argument ( ' --courses ' , required = True , dest = ' courses_file ' , action = ' store ' , default = ' ./ ' ,
help = ' The xml with courses ' )
parser_init . add_argument ( ' --dir ' , required = False , dest = ' dir ' , action = ' store ' , default = ' ./ ' ,
help = ' Specify a directort, if not set the files are created in the current directory ' )
2023-11-19 16:54:27 +00:00
parser_init = subparsers . add_parser ( ' run ' , help = ' run otime ' )
parser_init . add_argument ( ' --dir ' , required = False , dest = ' dir ' , action = ' store ' , default = ' ./ ' , help = ' specify a directory ' )
parser_init . add_argument ( ' --port ' , required = False , dest = ' port ' , action = ' store ' , help = ' specify a serial port ' )
2023-11-21 21:32:14 +00:00
parser_init . add_argument ( ' --xml ' , required = False , dest = ' xml_path ' , action = ' store ' , default = None , help = ' Where the xml result file should be saved ' )
2023-11-19 16:54:27 +00:00
2023-11-28 00:33:25 +00:00
parser_init = subparsers . add_parser ( ' gen ' , help = ' Generate result files ' )
2023-11-23 23:42:02 +00:00
parser_init . add_argument ( ' --dir ' , required = False , dest = ' dir ' , action = ' store ' , default = ' ./ ' , help = ' specify a directory ' )
parser_init . add_argument ( ' --xml ' , required = False , dest = ' xml_path ' , action = ' store ' , default = None , help = ' Where the xml result file should be saved ' )
2023-11-28 00:33:25 +00:00
parser_init = subparsers . add_parser ( ' view ' , help = ' View otime data ' )
parser_init . add_argument ( ' --dir ' , required = False , dest = ' dir ' , action = ' store ' , default = ' ./ ' , help = ' specify a directory ' )
parser_init . add_argument ( ' --xml ' , required = False , dest = ' xml_path ' , action = ' store ' , default = None , help = ' Where the xml result file should be saved ' )
2023-12-02 12:25:17 +00:00
parser_init = subparsers . add_parser ( ' mtr ' , help = ' run mtr commands ' )
2023-12-05 21:39:12 +00:00
parser_init . add_argument ( ' --port ' , required = True , dest = ' port ' , action = ' store ' , help = ' specify a serial port ' )
parser_init . add_argument ( ' --spool ' , required = False , dest = ' mtr_spool ' , action = ' store_true ' , help = ' Spool all mtr data ' )
parser_init . add_argument ( ' --status ' , required = False , dest = ' mtr_status ' , action = ' store_true ' , help = ' Spool all mtr data ' )
2023-11-28 00:33:25 +00:00
2023-11-17 20:52:44 +00:00
args = parser . parse_args ( )
2023-11-21 21:32:14 +00:00
2023-11-28 00:33:25 +00:00
try :
if not args . xml_path :
args . xml_path = args . dir + ' /output '
except AttributeError :
pass
2023-11-21 21:32:14 +00:00
2023-11-19 16:54:27 +00:00
match args . command :
case ' init ' :
init_dir ( args . dir , args . entries_file , args . courses_file )
case ' run ' :
2023-11-21 21:32:14 +00:00
run ( args . port , args . dir , args . xml_path )
2023-11-28 00:33:25 +00:00
case ' view ' :
2023-11-23 23:42:02 +00:00
search_tui . main ( args . dir )
2023-11-28 00:33:25 +00:00
case ' gen ' :
gen ( args . dir , args . xml_path )
2023-12-02 12:25:17 +00:00
case ' mtr ' :
mtr = serial . Serial ( port = args . port , baudrate = 9600 , timeout = 40 )
2023-12-05 21:39:12 +00:00
if args . mtr_spool :
mtr . write ( b ' /SA ' )
if args . mtr_status :
mtr . write ( b ' /ST ' )
case other :
parser . print_help ( )
2023-11-17 20:52:44 +00:00
def init_dir ( project_dir , entries_xml_file , courses_xml_file ) :
# Lager mappe med en config fil, en csv fil med løpere og en fil med mtr data
csv_db_path = project_dir + ' /runners.csv '
config_path = project_dir + ' /config.yaml '
mtr_path = project_dir + ' mtr.yaml '
event = iof_xml . event_from_xml_entries ( entries_xml_file )
event . courses = iof_xml . courses_from_xml ( courses_xml_file )
2023-11-21 21:32:14 +00:00
print ( f ' Read { len ( event . runners ) } runners, { len ( event . card_dumps ) } ecards, { len ( event . courses ) } courses and { len ( event . o_classes ) } classes ' )
print ( ' Remember to manually link the courses and classes in the config file ' )
2023-11-17 20:52:44 +00:00
file_io . write_runners_csv ( event , csv_db_path )
file_io . write_config ( event , config_path )
file_io . write_card_dumps ( event , mtr_path )
2023-11-21 21:32:14 +00:00
os . makedirs ( project_dir + ' /output ' )
2023-11-17 20:52:44 +00:00
2023-11-21 21:32:14 +00:00
subprocess . run ( [ ' git ' , ' init ' , project_dir ] )
subprocess . run ( [ ' git ' , ' add ' , ' ./* ' ] , cwd = project_dir )
subprocess . run ( [ ' git ' , ' commit ' , ' -m ' , f ' Project initiated by otime ' ] , cwd = project_dir )
def run ( port = ' /dev/ttyUSB0 ' , project_dir = ' ./ ' , xml_path = ' ./output/ ' ) :
2023-11-19 16:54:27 +00:00
mtr = serial . Serial ( port = port , baudrate = 9600 , timeout = 40 )
2023-11-21 21:32:14 +00:00
config_path = project_dir + ' /config.yaml '
mtr_path = project_dir + ' /mtr.yaml '
csv_path = project_dir + ' /runners.csv '
2023-11-19 16:54:27 +00:00
while True :
if mtr . in_waiting > 0 :
2023-12-06 22:04:41 +00:00
block = mtr . read_until ( expected = b ' \xFF \xFF \xFF \xFF ' )
size = block [ 0 ]
if size == 230 :
2023-11-19 16:54:27 +00:00
event = file_io . event_from_yaml_and_csv ( config_path , mtr_path , csv_path )
2023-12-06 22:04:41 +00:00
message = b ' \xFF \xFF \xFF \xFF ' + block [ : 230 ]
2023-12-05 21:39:12 +00:00
if not is_checksum_valid ( message ) :
print ( ' [red]Checksum is not valid![red] ' )
2023-12-06 22:04:41 +00:00
try :
print ( otime . CardDump . from_mtr_bytes ( message ) )
print ( runner_info ( event , card_dump ) )
2023-12-06 23:00:08 +00:00
except Exception as error :
2023-12-06 22:04:41 +00:00
print ( error )
else :
card_dump = otime . CardDump . from_mtr_bytes ( message )
event . card_dumps . append ( card_dump )
file_io . write_card_dumps ( event , mtr_path )
print ( runner_info ( event , card_dump ) )
subprocess . run ( [ ' git ' , ' add ' , ' ./* ' ] , cwd = project_dir , stdout = subprocess . DEVNULL )
subprocess . run ( [ ' git ' , ' commit ' , ' -m ' , f ' Added { card_dump . card } ' ] , cwd = project_dir , stdout = subprocess . DEVNULL )
iof_xml . create_result_file ( event , xml_path + ' /results.xml ' )
pdf . create_result_list ( event , project_dir + ' /output/results.pdf ' )
2023-11-21 21:32:14 +00:00
2023-12-06 22:04:41 +00:00
elif size == 55 :
message = b ' \xFF \xFF \xFF \xFF ' + block [ : 55 ]
2023-12-05 21:39:12 +00:00
mtr_id = int . from_bytes ( message [ 6 : 8 ] , ' little ' )
year = int . from_bytes ( message [ 8 : 9 ] , ' little ' )
month = int . from_bytes ( message [ 9 : 10 ] , ' little ' )
day = int . from_bytes ( message [ 10 : 11 ] , ' little ' )
hours = int . from_bytes ( message [ 11 : 12 ] , ' little ' )
minutes = int . from_bytes ( message [ 12 : 13 ] , ' little ' )
seconds = int . from_bytes ( message [ 13 : 14 ] , ' little ' )
milliseconds = int . from_bytes ( message [ 14 : 16 ] , ' little ' )
battery_status = int . from_bytes ( message [ 16 : 17 ] , ' little ' )
time = datetime . datetime ( year , month , day , hours , minutes , seconds , milliseconds )
print ( f ' MTR status message: id: { mtr_id } , time: { time } , battery: { battery_status } ' )
2023-12-06 22:04:41 +00:00
else :
print ( ' Data not found! ' )
print ( block )
2023-12-05 21:39:12 +00:00
2023-11-19 16:54:27 +00:00
2023-11-28 00:33:25 +00:00
def gen ( project_dir = ' ./ ' , xml_path = ' ./output/ ' ) :
config_path = project_dir + ' /config.yaml '
mtr_path = project_dir + ' /mtr.yaml '
csv_path = project_dir + ' /runners.csv '
event = file_io . event_from_yaml_and_csv ( config_path , mtr_path , csv_path )
subprocess . run ( [ ' git ' , ' add ' , ' ./* ' ] , cwd = project_dir , stdout = subprocess . DEVNULL )
subprocess . run ( [ ' git ' , ' commit ' , ' -m ' , f ' Manually run ' ] , cwd = project_dir , stdout = subprocess . DEVNULL )
iof_xml . create_result_file ( event , xml_path + ' /results.xml ' )
pdf . create_result_list ( event , project_dir + ' /output/results.pdf ' )
2023-11-19 16:54:27 +00:00
def runner_info ( event , card_dump ) :
runner = next ( ( i for i in event . runners if str ( i . card_id ) == str ( card_dump . card ) ) , None )
2023-11-21 21:32:14 +00:00
if runner is None :
2023-12-06 23:00:08 +00:00
return f ' [orange_red1]No runner with ecard { card_dump . card } ! It matches these courses: { otime . find_courses_matching_controls ( card_dump . controls , event . courses ) } [/orange_red1] { card_dump } '
2023-11-21 21:32:14 +00:00
result = event . get_runner_result ( runner . id )
if result is None :
return ' 🧐 Dette skal ikke skje... '
if result . status == ' OK ' :
result_text = f ' [green]Place: { result . place } . Time: [bold][yellow] { format_m_s ( result . total_time ) } [/yellow][/bold][/green] '
elif result . status == ' MissingPunch ' :
2023-12-06 22:04:41 +00:00
result_text = f ' [red]Missed control(s): { result . missed_controls } ! Time: [bold][yellow] { format_m_s ( result . total_time ) } [/yellow][/bold][/red] '
2023-11-21 21:32:14 +00:00
else :
result_text = f ' [blue] { result . status } , Time: [bold][yellow] { result . total_time } [/yellow][/bold][/blue] '
return result_text + f ' [blue] { result . fullname ( ) } [/blue], { result . o_class } , { result . club } , [italic]Card: { result . card_id } [/italic] '
2023-11-19 16:54:27 +00:00
2023-12-05 21:39:12 +00:00
def is_checksum_valid ( message ) :
# Hentet fra https://github.com/knutsiem/mtr-log-extractor
checksum = int . from_bytes ( message [ 232 : 233 ] , ' little ' )
# calculate checksum for message bytes up until checksum
calculated_checksum = sum ( message [ : 232 ] ) % 256
return checksum == calculated_checksum
2023-11-17 20:52:44 +00:00
if __name__ == ' __main__ ' :
main ( )