forked from Trygve/otime
178 lines
9.0 KiB
Python
178 lines
9.0 KiB
Python
import argparse
|
|
import file_io
|
|
import datetime
|
|
import iof_xml
|
|
import serial
|
|
import subprocess
|
|
import os
|
|
import otime
|
|
import iof_xml
|
|
import pdf
|
|
from pdf import format_m_s
|
|
from rich import print
|
|
import search_tui
|
|
|
|
from rich.traceback import install
|
|
from rich import inspect
|
|
install(show_locals=True)
|
|
|
|
# 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')
|
|
parser_init = subparsers.add_parser('init', help='Create project files')
|
|
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')
|
|
|
|
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')
|
|
parser_init.add_argument('--xml', required=False, dest='xml_path', action='store', default=None, help='Where the xml result file should be saved')
|
|
|
|
parser_init = subparsers.add_parser('gen', help='Generate result files')
|
|
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')
|
|
|
|
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')
|
|
|
|
parser_init = subparsers.add_parser('mtr', help='run mtr commands')
|
|
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')
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
if not args.xml_path:
|
|
args.xml_path = args.dir + '/output'
|
|
except AttributeError:
|
|
pass
|
|
|
|
match args.command:
|
|
case 'init':
|
|
init_dir(args.dir, args.entries_file, args.courses_file)
|
|
case 'run':
|
|
run(args.port, args.dir, args.xml_path)
|
|
case 'view':
|
|
search_tui.main(args.dir)
|
|
case 'gen':
|
|
gen(args.dir, args.xml_path)
|
|
case 'mtr':
|
|
mtr = serial.Serial(port=args.port, baudrate=9600, timeout=40)
|
|
if args.mtr_spool:
|
|
mtr.write(b'/SA')
|
|
if args.mtr_status:
|
|
mtr.write(b'/ST')
|
|
case other:
|
|
parser.print_help()
|
|
|
|
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)
|
|
|
|
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')
|
|
|
|
file_io.write_runners_csv(event, csv_db_path)
|
|
file_io.write_config(event, config_path)
|
|
file_io.write_card_dumps(event, mtr_path)
|
|
os.makedirs(project_dir+'/output')
|
|
|
|
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/'):
|
|
mtr = serial.Serial(port=port, baudrate=9600, timeout=40)
|
|
config_path = project_dir + '/config.yaml'
|
|
mtr_path = project_dir + '/mtr.yaml'
|
|
csv_path = project_dir + '/runners.csv'
|
|
while True:
|
|
if mtr.in_waiting > 0:
|
|
block = mtr.read_until(expected=b'\xFF\xFF\xFF\xFF')
|
|
size = block[0]
|
|
if size == 230:
|
|
event = file_io.event_from_yaml_and_csv(config_path, mtr_path, csv_path)
|
|
message = b'\xFF\xFF\xFF\xFF' + block[:230]
|
|
if not is_checksum_valid(message):
|
|
print('[red]Checksum is not valid![red]')
|
|
try:
|
|
print(otime.CardDump.from_mtr_bytes(message))
|
|
print(runner_info(event, card_dump))
|
|
except Exception as error:
|
|
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')
|
|
|
|
elif size == 55:
|
|
message = b'\xFF\xFF\xFF\xFF' + block[:55]
|
|
|
|
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}')
|
|
else:
|
|
print('Data not found!')
|
|
print(block)
|
|
|
|
|
|
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')
|
|
|
|
def runner_info(event, card_dump):
|
|
runner = next((i for i in event.runners if str(i.card_id) == str(card_dump.card)), None)
|
|
if runner is None:
|
|
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}'
|
|
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':
|
|
result_text = f'[red]Missed control(s): {result.missed_controls}! Time: [bold][yellow]{format_m_s(result.total_time)}[/yellow][/bold][/red]'
|
|
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]'
|
|
|
|
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
|
|
|
|
if __name__ == '__main__':
|
|
main() |