From 184eb0530970064cd8121215f0dacf2841496fda Mon Sep 17 00:00:00 2001 From: Dan Allongo Date: Mon, 30 May 2016 08:14:11 -0600 Subject: [PATCH] merge pyDashRF1 and pyDashR3E into single pyDash application --- README.md | 22 +- pyDash.py | 223 ++++++++ pyDashR3E.py | 514 +++++++---------- pyDashRF1.py | 523 +++++++----------- pyinstaller/README.md | 15 +- pyinstaller/{pyDashR3E.spec => pyDash.spec} | 6 +- ...DashR3E.version.txt => pyDash.version.txt} | 14 +- pyinstaller/pyDashRF1.spec | 28 - pyinstaller/pyDashRF1.version.txt | 45 -- 9 files changed, 675 insertions(+), 715 deletions(-) create mode 100644 pyDash.py rename pyinstaller/{pyDashR3E.spec => pyDash.spec} (81%) rename pyinstaller/{pyDashR3E.version.txt => pyDash.version.txt} (79%) delete mode 100644 pyinstaller/pyDashRF1.spec delete mode 100644 pyinstaller/pyDashRF1.version.txt diff --git a/README.md b/README.md index 16df384..f1b42fb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A Python library application for the Renovatio SRD-9c display (http://www.renovatio-dev.com/). -This makes use of the pywinusb module (https://github.com/rene-aguirre/pywinusb) to interface with the SRD-9c display. The display uses the HID protocol to provide an input interface as a joystick and an output interface for the nine 7-segment displays and 16 LEDs. +This makes use of the `pywinusb` module (https://github.com/rene-aguirre/pywinusb) to interface with the SRD-9c display. The display uses the HID protocol to provide an input interface as a joystick and an output interface for the nine 7-segment displays and 16 LEDs. The output report is structured as follows (41 bytes total): @@ -16,14 +16,26 @@ The output report is structured as follows (41 bytes total): Run by itself, `pySRD9c.py` will conduct a self-test on the display. -A sample application providing real-time telemetry data for RaceRoom Racing Experience is available in `pyDashR3E.py`. -It demonstrates custom mapping of RPM LEDs for use as push-to-pass/drs indicators as well as warnings that blink the status LEDs during a critical state. +A sample application providing real-time telemetry data for RaceRoom Racing Experience and rFactor 1/Stock Car Extreme/Automobilista is available in `pyDash.py`. It makes use of the `psutil` module (https://github.com/giampaolo/psutil) to detect which sim is running. +It demonstrates custom mapping of RPM LEDs for use as push-to-pass/DRS indicators as well as warnings that blink the status LEDs during a critical state. It also features live lap timing, lap split time, field position, and lap progession during a race. -The dash configuration is controlled via the `pyDashR3E.settings.json` file (created on start-up if not found, re-read if modified while running). +The dash configuration is controlled via the `pyDash.settings.json` file (created on start-up if not found, re-read if modified while running). -Also included is a sample application using telemetry data from rFactor 1 via the shared memory map plugin available at https://github.com/dallongo/rFactorSharedMemoryMap. +Support for rFactor 1 is provided by the shared memory map plugin available at https://github.com/dallongo/rFactorSharedMemoryMap. ### Releases +#### 2016-05-30 (v2.0.0.0) + +* Merged `pyDashR3E` and `pyDashRF1` into single application +* Improved session detection and clearing/resetting of session-specific variables +* Improved logging prints time stamp and information when new session is detected, average fuel use/temperature ranges and lap times +* Improved fuel use estimates and overheating detection by using weighted averages +* Display now cleared upon exiting sim +* Mulitple instance detection to protect against race condition that causes settings JSON to be infinitely rewritten to disk +* Automatic sim detection +* Tachometer range and shift light point can now be configured via settings +* Fuel and temperature warnings can now be disabled/tuned via settings + #### 2016-05-14 (v1.1.0.0) * Fix DRS LEDs for R3E DTM 2013-2016 cars diff --git a/pyDash.py b/pyDash.py new file mode 100644 index 0000000..fb88955 --- /dev/null +++ b/pyDash.py @@ -0,0 +1,223 @@ +""" +pyDash.py - Waits for sim to launch and then starts appropriate Dash app +by Dan Allongo (daniel.s.allongo@gmail.com) + +Release History: +2016-05-30: Add multiple instance detection +2016-05-29: Add timestamp to each log message +2016-05-28: Clear display after exiting sim +2016-05-27: Add settings for fuel, temperature, and RPM range to unified settings JSON +2016-05-26: Initial release +""" + +APP_NAME = 'pyDash' +APP_VER = '2.0.0.0' +APP_DESC = 'Python sim racing dashboard control' +APP_AUTHOR = 'Dan Allongo (daniel.s.allongo@gmail.com)' +APP_URL = 'https://github.com/dallongo/pySRD9c' + +if __name__ == '__main__': + from pyDashR3E import pyDashR3E + from pyDashRF1 import pyDashRF1 + from pySRD9c import srd9c + + from time import sleep + from psutil import process_iter, Process + from sys import exit + from distutils.util import strtobool + import json + from datetime import datetime + + print "{0} v.{1}".format(APP_NAME, APP_VER) + print APP_DESC + print APP_AUTHOR + print APP_URL + + # only one instance running at a time to avoid race condition + pid = None + for p in process_iter(): + # compiled exe with pyinstaller runs as child process during unpacking + if(p.pid in [Process().pid, Process().ppid()]): + continue + try: + if(p.cmdline() == Process().cmdline()): + pid = p.pid + break + except: + pass + if(pid): + print "\nERROR: Instance in PID {0} already running, exiting!".format(pid) + sleep(3) + exit(1) + # super basic logging func, echos to console + def log_print(s, lfn=APP_NAME + '.log'): + with open(lfn, 'a+') as lfh: + # append timestamp to start of each log message + s = '\t'.join([datetime.now().strftime('%Y-%m-%d %H:%M:%S'), s]) + print s + if(not s or s[-1] != '\n'): + s += '\n' + lfh.write(s) + # get and validate settings from json, write back out to disk + def read_settings(sfn=APP_NAME + '.settings.json'): + # verify options are valid + def check_option(option, val_type='float', default=0, val_range=[0, 1]): + x = None + try: + if(val_type=='float'): + x = float(str(option)) + if(x < min(val_range) or x > max(val_range)): + raise ValueError + elif(val_type=='bool'): + x = bool(strtobool(str(option))) + elif(val_type=='str'): + x = str(option) + if(x not in val_range): + raise ValueError + except ValueError: + log_print("Bad option value {0}, using default value {1}".format(option, default)) + return default + return x + # default settings + defaults = { + 'text_blink':{ + '_comment':"blink text for pit/overheat/fuel warnings. values 0.1-1.0", + 'enabled':True, + 'duration':0.5 + }, + 'led_blink':{ + '_comment':"blink indicators for DRS/PTP/pit/overheat/fuel warnings. values 0.1-1.0", + 'enabled':True, + 'duration':0.2 + }, + 'info_text':{ + 'sector_split':{ + '_comment':"options are 'self_previous', 'self_best', 'session_best'", + 'enabled':True, + 'compare_lap':'session_best' + }, + 'lap_split':{ + '_comment':"options are 'self_previous', 'self_best', 'session_best'", + 'enabled':True, + 'compare_lap':'self_previous' + }, + 'position':{ + '_comment':"show position in field at the beginning of each lap", + 'enabled':True + }, + 'remaining':{ + '_comment':"show laps/time remaining at the beginning of each lap", + 'enabled':True + }, + '_comment':"session timing info for each sector/lap. values 1.0-5.0", + 'duration':3 + }, + 'drs_ptp':{ + '_comment':"(R3E only) text and green RPM LEDs for DRS/PTP", + 'text':True, + 'led':True + }, + 'neutral':{ + '_comment':"options are '0', 'n', '-', '_', ' '", + 'symbol':"n" + }, + 'speed':{ + '_comment':"options are 'mph', 'km/h'", + 'units':"mph" + }, + 'fuel':{ + '_comment':"tune fuel warnings. 'samples' is how many laps to use for the moving average of fuel use (values 1.0-5.0). 'warning' is how many laps of fuel left to turn on LED (values 2.0-5.0). 'critical' is how many laps of fuel left to blink LED (values 0.5-2.0).", + 'warning':3, + 'critical':1, + 'samples':3, + 'enabled':True + }, + 'temperature':{ + '_comment':"tune temperature warnings. 'samples' is how many laps to use for the initial baseline (values 1.0-5.0). 'warning' is how many degrees C above baseline to turn on LED (values 2.0-10.0). 'critical' is how many degrees C above baseline to blink LED (values 10.0-20.0).", + 'warning':7, + 'critical':12, + 'samples':3, + 'enabled':True + }, + 'rpm':{ + '_comment':"change tach/shift points. 'range' is what fraction of the RPM range is represented by each group of 4 LEDs (values 0.05-0.33). 'shift' is what fraction of the RPM range to trigger the shift LED (values 0.85-1.0).", + 'range':0.13, + 'shift':0.95 + } + } + # get settings from json + with open(sfn, 'a+') as f: + try: + # merge with defaults to catch missing keys + settings = dict(defaults, **json.load(f)) + except ValueError: + log_print("Invalid or missing settings file, creating using defaults") + settings = defaults + if(settings != defaults): + # validate setting values + settings['text_blink']['enabled'] = check_option(settings['text_blink']['enabled'], 'bool', defaults['text_blink']['enabled']) + settings['text_blink']['duration'] = check_option(settings['text_blink']['duration'], 'float', defaults['text_blink']['duration'], [0.1, 1]) + + settings['led_blink']['enabled'] = check_option(settings['led_blink']['enabled'], 'bool', defaults['led_blink']['enabled']) + settings['led_blink']['duration'] = check_option(settings['led_blink']['duration'], 'float', defaults['led_blink']['duration'], [0.1, 1]) + + settings['info_text']['sector_split']['enabled'] = check_option(settings['info_text']['sector_split']['enabled'], 'bool', defaults['info_text']['sector_split']['enabled']) + settings['info_text']['sector_split']['compare_lap'] = check_option(settings['info_text']['sector_split']['compare_lap'], 'str', defaults['info_text']['sector_split']['compare_lap'], ['self_previous', 'self_best', 'session_best']) + settings['info_text']['lap_split']['enabled'] = check_option(settings['info_text']['lap_split']['enabled'], 'bool', defaults['info_text']['lap_split']['enabled']) + settings['info_text']['lap_split']['compare_lap'] = check_option(settings['info_text']['lap_split']['compare_lap'], 'str', defaults['info_text']['lap_split']['compare_lap'], ['self_previous', 'self_best', 'session_best']) + settings['info_text']['position']['enabled'] = check_option(settings['info_text']['position']['enabled'], 'bool', defaults['info_text']['position']['enabled']) + settings['info_text']['remaining']['enabled'] = check_option(settings['info_text']['remaining']['enabled'], 'bool', defaults['info_text']['remaining']['enabled']) + settings['info_text']['duration'] = check_option(settings['info_text']['duration'], 'float', defaults['info_text']['duration'], [1, 5]) + + settings['neutral']['symbol'] = check_option(settings['neutral']['symbol'], 'str', defaults['neutral']['symbol'], ['0', 'n', '-', '_', ' ']) + settings['speed']['units'] = check_option(settings['speed']['units'], 'str', defaults['speed']['units'], ['mph', 'km/h']) + + settings['drs_ptp']['text'] = check_option(settings['drs_ptp']['text'], 'bool', defaults['drs_ptp']['text']) + settings['drs_ptp']['led'] = check_option(settings['drs_ptp']['led'], 'bool', defaults['drs_ptp']['led']) + + settings['fuel']['enabled'] = check_option(settings['fuel']['enabled'], 'bool', defaults['fuel']['enabled']) + settings['fuel']['samples'] = check_option(settings['fuel']['samples'], 'float', defaults['fuel']['samples'], [1, 5]) + settings['fuel']['warning'] = check_option(settings['fuel']['warning'], 'float', defaults['fuel']['warning'], [2, 5]) + settings['fuel']['critical'] = check_option(settings['fuel']['critical'], 'float', defaults['fuel']['critical'], [0.5, 2]) + + settings['temperature']['enabled'] = check_option(settings['temperature']['enabled'], 'bool', defaults['temperature']['enabled']) + settings['temperature']['samples'] = check_option(settings['temperature']['samples'], 'float', defaults['temperature']['samples'], [1, 5]) + settings['temperature']['warning'] = check_option(settings['temperature']['warning'], 'float', defaults['temperature']['warning'], [2, 10]) + settings['temperature']['critical'] = check_option(settings['temperature']['critical'], 'float', defaults['temperature']['critical'], [10, 20]) + + settings['rpm']['range'] = check_option(settings['rpm']['range'], 'float', defaults['rpm']['range'], [0.05, 0.33]) + settings['rpm']['shift'] = check_option(settings['rpm']['shift'], 'float', defaults['rpm']['shift'], [0.85, 1.0]) + # write out validated settings + with open(sfn, 'w') as f: + json.dump(settings, f, indent=4, separators=(',',': '), sort_keys=True) + return settings, sfn + log_print("-"*16 + " pyDash INIT " + "-"*16) + settings, settings_fn = read_settings() + log_print("Waiting for SRD-9c...") + dash = srd9c() + log_print("Connected!") + while(True): + sleep(1) + try: + for p in process_iter(): + if(p.name().lower() in ['rrre.exe', 'gsc.exe', 'ams.exe', 'rfactor.exe']): + log_print("Found {0}".format(p.name())) + if(p.name().lower() == 'rrre.exe'): + pyDashR3E(p.pid, log_print, read_settings, dash) + else: + pyDashRF1(p.pid, log_print, read_settings, dash) + # clear display after exiting sim + dash.gear = ' ' + dash.left = ' '*4 + dash.right = ' '*4 + dash.rpm['value'] = 0 + dash.rpm['green'] = '0'*4 + dash.rpm['red'] = '0'*4 + dash.rpm['blue'] = '0'*4 + dash.status = '0'*4 + dash.update() + break + except: + log_print("Unhandled exception!") + log_print(format_exc()) + log_print("-"*16 + " pyDash SHUTDOWN " + "-"*16) diff --git a/pyDashR3E.py b/pyDashR3E.py index 5d0f7d9..ace10c2 100644 --- a/pyDashR3E.py +++ b/pyDashR3E.py @@ -9,6 +9,12 @@ It uses mmap to read from a shared memory handle. Release History: +2016-05-30: Weighted moving average used for fuel estimates and temperature averages +2016-05-29: Information messages printed to log +2016-05-28: Improved session detection +2016-05-27: RPM range and shift lights now configurable via settings + Fuel and temperature warnings can now be tuned/disabled via settings +2016-05-26: Integrate with pyDash 2016-05-12: Fix DRS LEDs for DTM 2013-2016 classes 2016-05-09: Add missing sanity check for 'drs_ptp' settings Fix errors in fuel and sector split calculations (again) @@ -32,315 +38,218 @@ 2016-05-04: Initial release """ -APP_NAME = 'pyDashR3E' -APP_VER = '1.1.0.0' -APP_DESC = 'Python sim racing dashboard control' -APP_AUTHOR = 'Dan Allongo (daniel.s.allongo@gmail.com)' -APP_URL = 'https://github.com/dallongo/pySRD9c' +from traceback import format_exc +from time import sleep, time +from mmap import mmap +from os.path import getmtime +from pyR3E import * +from psutil import pid_exists -if __name__ == '__main__': - from traceback import format_exc - from time import sleep, time - from sys import exit - from distutils.util import strtobool - from mmap import mmap - from os.path import getmtime - import json - from pyR3E import * - from pySRD9c import srd9c - - print "{0} v.{1}".format(APP_NAME, APP_VER) - print APP_DESC - print APP_AUTHOR - print APP_URL - - with open(APP_NAME + '.log', 'a+') as lfh: +def pyDashR3E(pid, log_print, read_settings, dash): + try: + log_print("-"*16 + " R3E INIT " + "-"*16) + settings, settings_fn = read_settings() + settings_mtime = getmtime(settings_fn) + # variables + blink_time = {'led':0, 'text':0} + compare_lap = 0 + compare_sector = 0 + info_text_time = 0 + current_sector = 0 + samples = {'water':[], 'oil':[], 'fuel':[], + 'avg_water':None, 'avg_oil':None, 'avg_fuel':None} + compare_fuel = 0 + current_session = [] + print_info = True try: - # super basic logging func, echos to console - def log_print(s): - print s - if(s[-1] != '\n'): - s += '\n' - lfh.write(s) - # get and validate settings from json, write back out to disk - def read_settings(sfn): - # verify options are valid - def check_option(option, val_type='float', default=0, val_range=[0, 1]): - x = None - try: - if(val_type=='float'): - x = float(str(option)) - if(x < min(val_range) or x > max(val_range)): - raise ValueError - elif(val_type=='bool'): - x = bool(strtobool(str(option))) - elif(val_type=='str'): - x = str(option) - if(x not in val_range): - raise ValueError - except ValueError: - log_print("Bad option value {0}, using default value {1}".format(option, default)) - return default - return x - # default settings - defaults = { - 'text_blink':{ - '_comment':"blink text for pit/overheat/fuel warnings. values 0.1-1.0", - 'enabled':True, - 'duration':0.5 - }, - 'led_blink':{ - '_comment':"blink indicators for DRS/PTP/pit/overheat/fuel warnings. values 0.1-1.0", - 'enabled':True, - 'duration':0.2 - }, - 'info_text':{ - 'sector_split':{ - '_comment':"options are 'self_previous', 'self_best', 'session_best'", - 'enabled':True, - 'compare_lap':'session_best' - }, - 'lap_split':{ - '_comment':"options are 'self_previous', 'self_best', 'session_best'", - 'enabled':True, - 'compare_lap':'self_previous' - }, - 'position':{ - '_comment':"show position in field at the beginning of each lap", - 'enabled':True - }, - 'remaining':{ - '_comment':"show laps/time remaining at the beginning of each lap", - 'enabled':True - }, - '_comment':"session timing info for each sector/lap. values 1.0-5.0", - 'duration':3 - }, - 'drs_ptp':{ - '_comment':"text and green RPM LEDs for DRS/PTP", - 'text':True, - 'led':True - }, - 'neutral':{ - '_comment':"options are '0', 'n', '-', '_', ' '", - 'symbol':"n" - }, - 'speed':{ - '_comment':"options are 'mph', 'km/h'", - 'units':"mph" - } - } - # get settings from json - with open(sfn, 'a+') as f: - try: - # merge with defaults to catch missing keys - settings = dict(defaults, **json.load(f)) - except ValueError: - log_print("Invalid or missing settings file, creating using defaults") - settings = defaults - if(settings != defaults): - # validate setting values - settings['text_blink']['enabled'] = check_option(settings['text_blink']['enabled'], 'bool', defaults['text_blink']['enabled']) - settings['led_blink']['enabled'] = check_option(settings['led_blink']['enabled'], 'bool', defaults['led_blink']['enabled']) - settings['info_text']['sector_split']['enabled'] = check_option(settings['info_text']['sector_split']['enabled'], 'bool', defaults['info_text']['sector_split']['enabled']) - settings['info_text']['lap_split']['enabled'] = check_option(settings['info_text']['lap_split']['enabled'], 'bool', defaults['info_text']['lap_split']['enabled']) - settings['info_text']['position']['enabled'] = check_option(settings['info_text']['position']['enabled'], 'bool', defaults['info_text']['position']['enabled']) - settings['info_text']['remaining']['enabled'] = check_option(settings['info_text']['remaining']['enabled'], 'bool', defaults['info_text']['remaining']['enabled']) - settings['text_blink']['duration'] = check_option(settings['text_blink']['duration'], 'float', defaults['text_blink']['duration'], [0.1, 1]) - settings['led_blink']['duration'] = check_option(settings['led_blink']['duration'], 'float', defaults['led_blink']['duration'], [0.1, 1]) - settings['info_text']['duration'] = check_option(settings['info_text']['duration'], 'float', defaults['info_text']['duration'], [1, 5]) - settings['info_text']['sector_split']['compare_lap'] = check_option(settings['info_text']['sector_split']['compare_lap'], 'str', defaults['info_text']['sector_split']['compare_lap'], ['self_previous', 'self_best', 'session_best']) - settings['info_text']['lap_split']['compare_lap'] = check_option(settings['info_text']['lap_split']['compare_lap'], 'str', defaults['info_text']['lap_split']['compare_lap'], ['self_previous', 'self_best', 'session_best']) - settings['neutral']['symbol'] = check_option(settings['neutral']['symbol'], 'str', defaults['neutral']['symbol'], ['0', 'n', '-', '_', ' ']) - settings['speed']['units'] = check_option(settings['speed']['units'], 'str', defaults['speed']['units'], ['mph', 'km/h']) - settings['drs_ptp']['text'] = check_option(settings['drs_ptp']['text'], 'bool', defaults['drs_ptp']['text']) - settings['drs_ptp']['led'] = check_option(settings['drs_ptp']['led'], 'bool', defaults['drs_ptp']['led']) - # write out validated settings - with open(sfn, 'w') as f: - json.dump(settings, f, indent=4, separators=(',',': '), sort_keys=True) - return settings - log_print("-"*16 + " INIT " + "-"*16) - settings_fn = APP_NAME + '.settings.json' - settings_mtime = 0 - settings = None - # variables - blink_time = {'led':0, 'text':0} - compare_lap = 0 - compare_sector = 0 - info_text_time = 0 - current_sector = 0 - samples = {'water':[], 'oil':[], 'fuel':[], - 'avg_water':None, 'avg_oil':None, 'avg_fuel':None, - 'warn_temp':None, 'warn_fuel':3, - 'critical_temp':None, 'critical_fuel':1, 'size':7} - compare_fuel = 0 - log_print("Waiting for SRD-9c...") - dash = srd9c() - log_print("Connected!") - try: - r3e_smm_handle = mmap(fileno=0, length=sizeof(r3e_shared), tagname=r3e_smm_tag) - except: - log_print("Unable to open shared memory map") - log_print(format_exc()) - if(r3e_smm_handle): - log_print("Shared memory mapped!") - else: - log_print("Shared memory not available, exiting!") - exit(1) - while(True): - sleep(0.01) - # get settings if file has changed - if(not settings or getmtime(settings_fn) > settings_mtime): - log_print("Reading settings from {0}".format(settings_fn)) - settings = read_settings(settings_fn) - settings_mtime = getmtime(settings_fn) - # read shared memory block - r3e_smm_handle.seek(0) - smm = r3e_shared.from_buffer_copy(r3e_smm_handle) - # get driver data - dd = None - if(smm.num_cars > 0): + r3e_smm_handle = mmap(fileno=0, length=sizeof(r3e_shared), tagname=r3e_smm_tag) + except: + log_print("Unable to open shared memory map") + log_print(format_exc()) + if(r3e_smm_handle): + log_print("Shared memory mapped!") + else: + log_print("Shared memory not available, exiting!") + return + while(pid_exists(pid)): + sleep(0.01) + # get settings if file has changed + if(not settings or getmtime(settings_fn) > settings_mtime): + log_print("Reading settings from {0}".format(settings_fn)) + settings = read_settings()[0] + settings_mtime = getmtime(settings_fn) + # read shared memory block + r3e_smm_handle.seek(0) + smm = r3e_shared.from_buffer_copy(r3e_smm_handle) + # get driver data + dd = None + if(smm.num_cars > 0): + if([smm.session_type, smm.track_info.track_id, smm.track_info.layout_id] == current_session): for d in smm.all_drivers_data_1: if(d.driver_info.slot_id == smm.slot_id): dd = d break else: + log_print("New session detected!") # clear session variables on exiting session compare_lap = 0 compare_sector = 0 info_text_time = 0 current_sector = 0 samples = {'water':[], 'oil':[], 'fuel':[], - 'avg_water':None, 'avg_oil':None, 'avg_fuel':None, - 'warn_temp':None, 'warn_fuel':3, - 'critical_temp':None, 'critical_fuel':1, 'size':7} + 'avg_water':None, 'avg_oil':None, 'avg_fuel':None} compare_fuel = 0 - if(dd): - # use green RPM LEDs for PTP when available - if((smm.push_to_pass.amount_left > 0 or smm.push_to_pass.engaged > -1 or smm.drs_engaged > 0 or - # DTM 2013, 2014, 2015, 2016 - (smm.drs_available == 1 and dd.driver_info.class_id in [1921, 3086, 4260, 5262])) and settings['drs_ptp']['led']): - dash.rpm['use_green'] = False - elif((smm.push_to_pass.available < 1 and smm.push_to_pass.engaged < 1) or (smm.drs_engaged == 0 and smm.drs_available == 0)): - dash.rpm['use_green'] = True - # used by the blink timers (all things that blink do so in unison) - if(time() - blink_time['led'] >= settings['led_blink']['duration']*2): - blink_time['led'] = time() - if(time() - blink_time['text'] >= settings['text_blink']['duration']*2): - blink_time['text'] = time() - rpm = 0 - status = ['0']*4 - if(smm.max_engine_rps > 0): - rpm = smm.engine_rps/smm.max_engine_rps - rpm -= (1 - (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*0.13) - rpm /= (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*0.13 - if(rpm < 0): - rpm = 0 - # blue status LED shift light at 95% of full RPM range - if(smm.engine_rps/smm.max_engine_rps >= 0.95): - status[2] = '1' - dash.rpm['value'] = rpm - dash.gear = dict({'-2':'-', '-1':'r', '0':settings['neutral']['symbol']}, **{str(i):str(i) for i in range(1, 8)})[str(smm.gear)] - if(settings['speed']['units'] == 'mph'): - dash.right = '{0}'.format(int(mps_to_mph(smm.car_speed))) - elif(settings['speed']['units'] == 'km/h'): - dash.right = '{0}'.format(int(mps_to_kph(smm.car_speed))) - # no running clock on invalid/out laps - if(smm.lap_time_current_self > 0): - dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(smm.lap_time_current_self, 60)) - else: - dash.left = '-.--.-' - # info text timer starts upon entering each sector - if(current_sector != dd.track_sector): - info_text_time = time() - current_sector = dd.track_sector - # calculate fuel use average continuously (dimishes over time) and ignore first sector after refuel - if(smm.fuel_use_active == 1): - if(compare_fuel > 0 and compare_fuel > smm.fuel_left): - samples['fuel'].append(compare_fuel - smm.fuel_left) - if(len(samples['fuel']) > samples['size']): - samples['fuel'] = samples['fuel'][-samples['size']:] - samples['avg_fuel'] = sum(samples['fuel'])*3/len(samples['fuel']) - compare_fuel = smm.fuel_left - # calculate temps for first few laps as baseline - if(len(samples['water']) < samples['size']): + current_session = [smm.session_type, smm.track_info.track_id, smm.track_info.layout_id] + print_info = True + else: + current_session = [] + if(dd): + # use green RPM LEDs for PTP when available + if((smm.push_to_pass.amount_left > 0 or smm.push_to_pass.engaged > -1 or smm.drs_engaged > 0 or + # DTM 2013, 2014, 2015, 2016 + (smm.drs_available == 1 and dd.driver_info.class_id in [1921, 3086, 4260, 5262])) and settings['drs_ptp']['led']): + dash.rpm['use_green'] = False + elif((smm.push_to_pass.available < 1 and smm.push_to_pass.engaged < 1) or (smm.drs_engaged == 0 and smm.drs_available == 0) or not settings['drs_ptp']['led']): + dash.rpm['use_green'] = True + # used by the blink timers (all things that blink do so in unison) + if(time() - blink_time['led'] >= settings['led_blink']['duration']*2): + blink_time['led'] = time() + if(time() - blink_time['text'] >= settings['text_blink']['duration']*2): + blink_time['text'] = time() + rpm = 0 + status = ['0']*4 + if(smm.max_engine_rps > 0): + rpm = smm.engine_rps/smm.max_engine_rps + rpm -= (1 - (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*settings['rpm']['range']) + rpm /= (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*settings['rpm']['range'] + if(rpm < 0): + rpm = 0 + # blue status LED shift light at 95% of full RPM range + if(smm.engine_rps/smm.max_engine_rps >= settings['rpm']['shift']): + status[2] = '1' + dash.rpm['value'] = rpm + dash.gear = dict({'-2':'-', '-1':'r', '0':settings['neutral']['symbol']}, **{str(i):str(i) for i in range(1, 8)})[str(smm.gear)] + if(settings['speed']['units'] == 'mph'): + dash.right = '{0}'.format(int(mps_to_mph(smm.car_speed))) + elif(settings['speed']['units'] == 'km/h'): + dash.right = '{0}'.format(int(mps_to_kph(smm.car_speed))) + # no running clock on invalid/out laps + if(smm.lap_time_current_self > 0): + dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(smm.lap_time_current_self, 60)) + else: + dash.left = '-.--.-' + # info text timer starts upon entering each sector + if(current_sector != dd.track_sector): + info_text_time = time() + current_sector = dd.track_sector + print_info = True + # calculate fuel use average continuously (dimishes over time) and ignore first sector after refuel + if(settings['fuel']['enabled'] and smm.fuel_use_active == 1): + if(compare_fuel > 0 and compare_fuel > smm.fuel_left): + samples['fuel'].append(compare_fuel - smm.fuel_left) + if(len(samples['fuel']) > 3*settings['fuel']['samples']): + samples['fuel'] = samples['fuel'][-3*settings['fuel']['samples']:] + wn = 0 + wd = 0 + for i in xrange(0,len(samples['fuel'])): + wn += samples['fuel'][i]*(i+1) + wd += (i+1) + samples['avg_fuel'] = wn*3/wd + log_print("Average fuel use: {0:4.2f} L per lap".format(samples['avg_fuel'])) + compare_fuel = smm.fuel_left + # calculate temps for first few laps as baseline + if(settings['temperature']['enabled']): + if(len(samples['water']) < 3*settings['temperature']['samples']): samples['water'].append(smm.engine_water_temp) elif(not samples['avg_water']): - samples['avg_water'] = sum(samples['water'][1:])/len(samples['water'][1:]) - samples['warn_temp'] = max(samples['water']) - min(samples['water']) - samples['critical_temp'] = samples['warn_temp']*1.5 - if(len(samples['oil']) < samples['size']): + wn = 0 + wd = 0 + for i in xrange(0,len(samples['water'])): + wn += samples['water'][i]*(i+1) + wd += (i+1) + samples['avg_water'] = wn/wd + log_print("Average water temperature: {0:4.2f} C".format(samples['avg_water'])) + if(len(samples['oil']) < 3*settings['temperature']['samples']): samples['oil'].append(smm.engine_oil_temp) elif(not samples['avg_oil']): - samples['avg_oil'] = sum(samples['oil'][1:])/len(samples['oil'][1:]) - if(current_sector == 1): - # show lap time compared to last/best/session best lap - et = time() - info_text_time - et_min = 0 - et_max = int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] - if(et >= et_min and et < et_max and settings['info_text']['lap_split']['enabled']): - if(smm.lap_time_previous_self > 0): - dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(smm.lap_time_previous_self, 60)) - else: - dash.left = '-.--.-' - if(compare_lap > 0 and smm.lap_time_previous_self > 0): - dash.right = '{0:04.2f}'.format(smm.lap_time_previous_self - compare_lap) - else: - dash.right = '--.--' - else: - # update comparison lap after lap display is done - if(smm.lap_time_previous_self > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_previous'): - compare_lap = smm.lap_time_previous_self - elif(smm.lap_time_best_self > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_best'): - compare_lap = smm.lap_time_best_self - elif(smm.lap_time_best_leader > 0 and settings['info_text']['lap_split']['compare_lap'] == 'session_best'): - compare_lap = smm.lap_time_best_leader - else: - compare_lap = 0 - # show position and number of cars in field - et_min += int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] - et_max += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] - if(et >= et_min and et < et_max and settings['info_text']['position']['enabled']): - dash.left = 'P{0}'.format(str(smm.position).rjust(3)) - dash.right = ' {0}'.format(str(smm.num_cars).ljust(3)) - # show completed laps and laps/time remaining - et_min += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] - et_max += int(settings['info_text']['remaining']['enabled'])*settings['info_text']['duration'] - if(et >= et_min and et < et_max and settings['info_text']['remaining']['enabled']): - dash.left = 'L{0}'.format(str(smm.completed_laps).rjust(3)) - if(smm.number_of_laps > 0): - dash.right = ' {0}'.format(str(smm.number_of_laps).ljust(3)) - elif(smm.session_time_remaining > 0): - dash.right = '{0:02.0f}.{1:04.1f}'.format(*divmod(smm.session_time_remaining, 60)) - else: - dash.right = ' '*4 - elif(current_sector in [2, 3] and settings['info_text']['sector_split']['enabled'] and time() - info_text_time <= settings['info_text']['duration']): - # show sectors 1 and 2 splits - if(smm.lap_time_previous_self > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_previous'): - compare_sector = dd.sector_time_previous_self[current_sector - 2] - if(current_sector == 3): - compare_sector -= dd.sector_time_previous_self[0] - elif(dd.sector_time_best_self[current_sector - 2] > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_best'): - compare_sector = dd.sector_time_best_self[current_sector - 2] - if(current_sector == 3): - compare_sector -= dd.sector_time_best_self[0] - elif(smm.session_best_lap_sector_times[current_sector - 2] > 0 and settings['info_text']['sector_split']['compare_lap'] == 'session_best'): - compare_sector = smm.session_best_lap_sector_times[current_sector - 2] - if(current_sector == 3): - compare_sector -= smm.session_best_lap_sector_times[0] + wn = 0 + wd = 0 + for i in xrange(0,len(samples['oil'])): + wn += samples['oil'][i]*(i+1) + wd += (i+1) + samples['avg_oil'] = wn/wd + log_print("Average oil temperature: {0:4.2f} C".format(samples['avg_oil'])) + if(current_sector == 1): + # show lap time compared to last/best/session best lap + et = time() - info_text_time + et_min = 0 + et_max = int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] + if(et >= et_min and et < et_max and settings['info_text']['lap_split']['enabled']): + if(smm.lap_time_previous_self > 0): + dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(smm.lap_time_previous_self, 60)) else: - compare_sector = 0 - if(compare_sector > 0 and smm.lap_time_current_self > 0): - sector_delta = dd.sector_time_current_self[current_sector - 2] - compare_sector - if(current_sector == 3): - sector_delta -= dd.sector_time_current_self[0] - dash.right = '{0:04.2f}'.format(sector_delta) + dash.left = '-.--.-' + if(compare_lap > 0 and smm.lap_time_previous_self > 0): + dash.right = '{0:04.2f}'.format(smm.lap_time_previous_self - compare_lap) else: dash.right = '--.--' + if(print_info): + log_print("Lap time (split): {0} ({1})".format(dash.left, dash.right)) + print_info = False + else: + # update comparison lap after lap display is done + if(smm.lap_time_previous_self > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_previous'): + compare_lap = smm.lap_time_previous_self + elif(smm.lap_time_best_self > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_best'): + compare_lap = smm.lap_time_best_self + elif(smm.lap_time_best_leader > 0 and settings['info_text']['lap_split']['compare_lap'] == 'session_best'): + compare_lap = smm.lap_time_best_leader + else: + compare_lap = 0 + # show position and number of cars in field + et_min += int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] + et_max += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] + if(et >= et_min and et < et_max and settings['info_text']['position']['enabled']): + dash.left = 'P{0}'.format(str(smm.position).rjust(3)) + dash.right = ' {0}'.format(str(smm.num_cars).ljust(3)) + # show completed laps and laps/time remaining + et_min += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] + et_max += int(settings['info_text']['remaining']['enabled'])*settings['info_text']['duration'] + if(et >= et_min and et < et_max and settings['info_text']['remaining']['enabled']): + dash.left = 'L{0}'.format(str(smm.completed_laps).rjust(3)) + if(smm.number_of_laps > 0): + dash.right = ' {0}'.format(str(smm.number_of_laps).ljust(3)) + elif(smm.session_time_remaining > 0): + dash.right = '{0:02.0f}.{1:04.1f}'.format(*divmod(smm.session_time_remaining, 60)) + else: + dash.right = ' '*4 + elif(current_sector in [2, 3] and settings['info_text']['sector_split']['enabled'] and time() - info_text_time <= settings['info_text']['duration']): + # show sectors 1 and 2 splits + if(smm.lap_time_previous_self > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_previous'): + compare_sector = dd.sector_time_previous_self[current_sector - 2] + if(current_sector == 3): + compare_sector -= dd.sector_time_previous_self[0] + elif(dd.sector_time_best_self[current_sector - 2] > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_best'): + compare_sector = dd.sector_time_best_self[current_sector - 2] + if(current_sector == 3): + compare_sector -= dd.sector_time_best_self[0] + elif(smm.session_best_lap_sector_times[current_sector - 2] > 0 and settings['info_text']['sector_split']['compare_lap'] == 'session_best'): + compare_sector = smm.session_best_lap_sector_times[current_sector - 2] + if(current_sector == 3): + compare_sector -= smm.session_best_lap_sector_times[0] + else: + compare_sector = 0 + if(compare_sector > 0 and smm.lap_time_current_self > 0): + sector_delta = dd.sector_time_current_self[current_sector - 2] - compare_sector + if(current_sector == 3): + sector_delta -= dd.sector_time_current_self[0] + dash.right = '{0:04.2f}'.format(sector_delta) + else: + dash.right = '--.--' # blink red status LED at critical fuel level - if(samples['avg_fuel'] and smm.fuel_left/samples['avg_fuel'] <= samples['warn_fuel']): + if(settings['fuel']['enabled'] and samples['avg_fuel'] and smm.fuel_left/samples['avg_fuel'] <= settings['fuel']['warning']): status[0] = '1' - if(smm.fuel_left/samples['avg_fuel'] < samples['critical_fuel']): + if(smm.fuel_left/samples['avg_fuel'] < settings['fuel']['critical']): if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): status[0] = '0' else: @@ -348,11 +257,11 @@ def check_option(option, val_type='float', default=0, val_range=[0, 1]): if(settings['text_blink']['enabled'] and time() - blink_time['text'] <= settings['text_blink']['duration']): dash.left = 'fuel' # blink yellow status LED at critical oil/coolant temp - if((samples['avg_water'] and smm.engine_water_temp - samples['avg_water'] >= samples['warn_temp']) or - (samples['avg_oil'] and smm.engine_oil_temp - samples['avg_oil'] >= samples['warn_temp'])): + if(settings['temperature']['enabled'] and ((samples['avg_water'] and smm.engine_water_temp - samples['avg_water'] >= settings['temperature']['warning']) or + (samples['avg_oil'] and smm.engine_oil_temp - samples['avg_oil'] >= settings['temperature']['warning']))): status[1] = '1' - if((smm.engine_water_temp - samples['avg_water'] > samples['critical_temp']) or - (smm.engine_oil_temp - samples['avg_oil'] > samples['critical_temp'])): + if((smm.engine_water_temp - samples['avg_water'] > settings['temperature']['critical']) or + (smm.engine_oil_temp - samples['avg_oil'] > settings['temperature']['critical'])): if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): status[1] = '0' else: @@ -394,15 +303,16 @@ def check_option(option, val_type='float', default=0, val_range=[0, 1]): if(smm.drs_engaged == 1): dash.left = 'drs ' dash.right = ' on ' - # make sure engine is running - if(rps_to_rpm(smm.engine_rps) > 1): - dash.status = ''.join(status) - dash.update() - else: - dash.reset() - # never gets here - log_print("Closing shared memory map...") - r3e_smm_handle.close() - except: - log_print("Unhandled exception!") - log_print(format_exc()) + # make sure engine is running + if(dd and rps_to_rpm(smm.engine_rps) > 1): + dash.status = ''.join(status) + dash.update() + else: + dash.reset() + except: + log_print("Unhandled exception!") + log_print(format_exc()) + finally: + log_print("Closing shared memory map...") + r3e_smm_handle.close() + log_print("-"*16 + " R3E SHUTDOWN " + "-"*16) diff --git a/pyDashRF1.py b/pyDashRF1.py index e078ced..683d306 100644 --- a/pyDashRF1.py +++ b/pyDashRF1.py @@ -9,167 +9,87 @@ It uses mmap to read from a shared memory handle. Release History: +2016-05-30: Weighted moving average used for fuel estimates +2016-05-29: Information messages printed to log +2016-05-28: Improved session detection +2016-05-27: RPM range and shift lights now configurable via settings + Fuel and temperature warnings can now be tuned/disabled via settings +2016-05-26: Integrate with pyDash 2016-05-09: Initial release """ -APP_NAME = 'pyDashRF1' -APP_VER = '0.9.0.0' -APP_DESC = 'Python sim racing dashboard control' -APP_AUTHOR = 'Dan Allongo (daniel.s.allongo@gmail.com)' -APP_URL = 'https://github.com/dallongo/pySRD9c' +from traceback import format_exc +from time import sleep, time +from mmap import mmap +from os.path import getmtime +from pyRF1 import * +from psutil import pid_exists -if __name__ == '__main__': - from traceback import format_exc - from time import sleep, time - from sys import exit - from distutils.util import strtobool - from mmap import mmap - from os.path import getmtime - import json - from pyRF1 import * - from pySRD9c import srd9c - - print "{0} v.{1}".format(APP_NAME, APP_VER) - print APP_DESC - print APP_AUTHOR - print APP_URL - - with open(APP_NAME + '.log', 'a+') as lfh: +def pyDashRF1(pid, log_print, read_settings, dash): + try: + log_print("-"*16 + " RF1 INIT " + "-"*16) + settings, settings_fn = read_settings() + settings_mtime = getmtime(settings_fn) + # variables + blink_time = {'led':0, 'text':0} + info_text_time = 0 + compare_lap = 0 + compare_sector = 0 + current_sector = 1 + samples = {'fuel':[], 'avg_fuel':None} + compare_fuel = 0 + current_session = [] + print_info = True try: - # super basic logging func, echos to console - def log_print(s): - print s - if(s[-1] != '\n'): - s += '\n' - lfh.write(s) - # get and validate settings from json, write back out to disk - def read_settings(sfn): - # verify options are valid - def check_option(option, val_type='float', default=0, val_range=[0, 1]): - x = None - try: - if(val_type=='float'): - x = float(str(option)) - if(x < min(val_range) or x > max(val_range)): - raise ValueError - elif(val_type=='bool'): - x = bool(strtobool(str(option))) - elif(val_type=='str'): - x = str(option) - if(x not in val_range): - raise ValueError - except ValueError: - log_print("Bad option value {0}, using default value {1}".format(option, default)) - return default - return x - # default settings - defaults = { - 'text_blink':{ - '_comment':"blink text for pit/overheat/fuel warnings. values 0.1-1.0", - 'enabled':True, - 'duration':0.5 - }, - 'led_blink':{ - '_comment':"blink indicators for DRS/PTP/pit/overheat/fuel warnings. values 0.1-1.0", - 'enabled':True, - 'duration':0.2 - }, - 'info_text':{ - 'sector_split':{ - '_comment':"options are 'self_previous', 'self_best', 'session_best'", - 'enabled':True, - 'compare_lap':'self_best' - }, - 'lap_split':{ - '_comment':"options are 'self_previous', 'self_best', 'session_best'", - 'enabled':True, - 'compare_lap':'self_best' - }, - 'position':{ - '_comment':"show position in field at the beginning of each lap", - 'enabled':True - }, - 'remaining':{ - '_comment':"show laps/time remaining at the beginning of each lap", - 'enabled':True - }, - '_comment':"session timing info for each sector/lap. values 1.0-5.0", - 'duration':3 - }, - 'neutral':{ - '_comment':"options are '0', 'n', '-', '_', ' '", - 'symbol':"n" - }, - 'speed':{ - '_comment':"options are 'mph', 'km/h'", - 'units':"mph" - } - } - # get settings from json - with open(sfn, 'a+') as f: - try: - # merge with defaults to catch missing keys - settings = dict(defaults, **json.load(f)) - except ValueError: - log_print("Invalid or missing settings file, creating using defaults") - settings = defaults - if(settings != defaults): - # validate setting values - settings['text_blink']['enabled'] = check_option(settings['text_blink']['enabled'], 'bool', defaults['text_blink']['enabled']) - settings['led_blink']['enabled'] = check_option(settings['led_blink']['enabled'], 'bool', defaults['led_blink']['enabled']) - settings['info_text']['sector_split']['enabled'] = check_option(settings['info_text']['sector_split']['enabled'], 'bool', defaults['info_text']['sector_split']['enabled']) - settings['info_text']['lap_split']['enabled'] = check_option(settings['info_text']['lap_split']['enabled'], 'bool', defaults['info_text']['lap_split']['enabled']) - settings['info_text']['position']['enabled'] = check_option(settings['info_text']['position']['enabled'], 'bool', defaults['info_text']['position']['enabled']) - settings['info_text']['remaining']['enabled'] = check_option(settings['info_text']['remaining']['enabled'], 'bool', defaults['info_text']['remaining']['enabled']) - settings['text_blink']['duration'] = check_option(settings['text_blink']['duration'], 'float', defaults['text_blink']['duration'], [0.1, 1]) - settings['led_blink']['duration'] = check_option(settings['led_blink']['duration'], 'float', defaults['led_blink']['duration'], [0.1, 1]) - settings['info_text']['duration'] = check_option(settings['info_text']['duration'], 'float', defaults['info_text']['duration'], [1, 5]) - settings['info_text']['sector_split']['compare_lap'] = check_option(settings['info_text']['sector_split']['compare_lap'], 'str', defaults['info_text']['sector_split']['compare_lap'], ['self_previous', 'self_best', 'session_best']) - settings['info_text']['lap_split']['compare_lap'] = check_option(settings['info_text']['lap_split']['compare_lap'], 'str', defaults['info_text']['lap_split']['compare_lap'], ['self_previous', 'self_best', 'session_best']) - settings['neutral']['symbol'] = check_option(settings['neutral']['symbol'], 'str', defaults['neutral']['symbol'], ['0', 'n', '-', '_', ' ']) - settings['speed']['units'] = check_option(settings['speed']['units'], 'str', defaults['speed']['units'], ['mph', 'km/h']) - # write out validated settings - with open(sfn, 'w') as f: - json.dump(settings, f, indent=4, separators=(',',': '), sort_keys=True) - return settings - log_print("-"*16 + " INIT " + "-"*16) - settings_fn = APP_NAME + '.settings.json' - settings_mtime = 0 - settings = None - # variables - blink_time = {'led':0, 'text':0} - info_text_time = 0 - compare_lap = 0 - compare_sector = 0 - current_sector = 1 - samples = {'water':[], 'oil':[], 'fuel':[], - 'avg_water':None, 'avg_oil':None, 'avg_fuel':None, - 'warn_temp':None, 'warn_fuel':3, - 'critical_temp':None, 'critical_fuel':1, 'size':7} - compare_fuel = 0 - log_print("Waiting for SRD-9c...") - dash = srd9c() - log_print("Connected!") - try: - rfMapHandle = mmap(fileno=0, length=sizeof(rfShared), tagname=rfMapTag) - except: - log_print("Unable to open shared memory map") - log_print(format_exc()) - if(rfMapHandle): - log_print("Shared memory mapped!") + rfMapHandle = mmap(fileno=0, length=sizeof(rfShared), tagname=rfMapTag) + except: + log_print("Unable to open shared memory map") + log_print(format_exc()) + if(rfMapHandle): + log_print("Shared memory mapped!") + else: + log_print("Shared memory not available, exiting!") + return + while(pid_exists(pid)): + sleep(0.01) + # get settings if file has changed + if(not settings or getmtime(settings_fn) > settings_mtime): + log_print("Reading settings from {0}".format(settings_fn)) + settings = read_settings()[0] + settings_mtime = getmtime(settings_fn) + # read shared memory block + rfMapHandle.seek(0) + smm = rfShared.from_buffer_copy(rfMapHandle) + # get driver data + dd = None + bestLapTimeSession = 0 + bestSector1Session = 0 + bestSector2Session = 0 + if(smm.numVehicles > 0): + if([smm.session, smm.trackName, smm.vehicleName] == current_session): + for d in smm.vehicle: + if(d.isPlayer): + dd = d + if(d.bestLapTime > 0 and (bestLapTimeSession == 0 or d.bestLapTime < bestLapTimeSession)): + bestLapTimeSession = d.bestLapTime + if(d.bestSector1 > 0 and (bestSector1Session == 0 or d.bestSector1 < bestSector1Session)): + bestSector1Session = d.bestSector1 + if(d.bestSector2 > 0 and (bestSector2Session == 0 or d.bestSector2 < bestSector2Session)): + bestSector2Session = d.bestSector2 + else: + log_print("New session detected!") + # clear session variables on exiting session + compare_lap = 0 + compare_sector = 0 + info_text_time = 0 + current_sector = 1 + samples = {'fuel':[], 'avg_fuel':None} + compare_fuel = 0 + current_session = [smm.session, smm.trackName, smm.vehicleName] + print_info = True else: - log_print("Shared memory not available, exiting!") - exit(1) - while(True): - sleep(0.01) - # get settings if file has changed - if(not settings or getmtime(settings_fn) > settings_mtime): - log_print("Reading settings from {0}".format(settings_fn)) - settings = read_settings(settings_fn) - settings_mtime = getmtime(settings_fn) - # read shared memory block - rfMapHandle.seek(0) - smm = rfShared.from_buffer_copy(rfMapHandle) + current_session = [] + if(dd): # used by the blink timers (all things that blink do so in unison) if(time() - blink_time['led'] >= settings['led_blink']['duration']*2): blink_time['led'] = time() @@ -179,12 +99,12 @@ def check_option(option, val_type='float', default=0, val_range=[0, 1]): status = ['0']*4 if(smm.engineMaxRPM > 0): rpm = smm.engineRPM/smm.engineMaxRPM - rpm -= (1 - (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*0.13) - rpm /= (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*0.13 + rpm -= (1 - (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*settings['rpm']['range']) + rpm /= (int(dash.rpm['use_green']) + int(dash.rpm['use_red']) + int(dash.rpm['use_blue']))*settings['rpm']['range'] if(rpm < 0): rpm = 0 # blue status LED shift light at 95% of full RPM range - if(smm.engineRPM/smm.engineMaxRPM >= 0.95): + if(smm.engineRPM/smm.engineMaxRPM >= settings['rpm']['shift']): status[2] = '1' dash.rpm['value'] = rpm dash.gear = dict({'-2':'-', '-1':'r', '0':settings['neutral']['symbol']}, **{str(i):str(i) for i in range(1, 8)})[str(smm.gear)] @@ -192,169 +112,138 @@ def check_option(option, val_type='float', default=0, val_range=[0, 1]): dash.right = '{0}'.format(int(mps_to_mph(smm.speed))) elif(settings['speed']['units'] == 'km/h'): dash.right = '{0}'.format(int(mps_to_kph(smm.speed))) - # get driver data - dd = None - bestLapTimeSession = 0 - bestSector1Session = 0 - bestSector2Session = 0 - if(smm.numVehicles > 0): - for d in smm.vehicle: - if(d.isPlayer): - dd = d - if(d.bestLapTime > 0 and (bestLapTimeSession == 0 or d.bestLapTime < bestLapTimeSession)): - bestLapTimeSession = d.bestLapTime - if(d.bestSector1 > 0 and (bestSector1Session == 0 or d.bestSector1 < bestSector1Session)): - bestSector1Session = d.bestSector1 - if(d.bestSector2 > 0 and (bestSector2Session == 0 or d.bestSector2 < bestSector2Session)): - bestSector2Session = d.bestSector2 + if(smm.currentET > 0 and smm.lapStartET > 0 and smm.lapNumber > 0): + currentLapTime = smm.currentET - smm.lapStartET else: - # clear session variables on exiting session - compare_lap = 0 - compare_sector = 0 - info_text_time = 0 - current_sector = 1 - samples = {'water':[], 'oil':[], 'fuel':[], - 'avg_water':None, 'avg_oil':None, 'avg_fuel':None, - 'warn_temp':None, 'warn_fuel':3, - 'critical_temp':None, 'critical_fuel':1, 'size':7} - compare_fuel = 0 - if(dd): - if(smm.currentET > 0 and smm.lapStartET > 0 and smm.lapNumber > 0): - currentLapTime = smm.currentET - smm.lapStartET - else: - currentLapTime = 0 - # no running clock on invalid/out laps - if(currentLapTime > 0): - dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(currentLapTime, 60)) - else: - dash.left = '-.--.-' - # info text timer starts upon entering each sector - if(current_sector != dd.sector): - info_text_time = time() - current_sector = dd.sector - # calculate fuel use average continuously (dimishes over time) and ignore first sector after refuel - if(compare_fuel > 0 and compare_fuel > smm.fuel): - samples['fuel'].append(compare_fuel - smm.fuel) - if(len(samples['fuel']) > samples['size']): - samples['fuel'] = samples['fuel'][-samples['size']:] - samples['avg_fuel'] = sum(samples['fuel'])*3/len(samples['fuel']) - compare_fuel = smm.fuel - # calculate temps for first few laps as baseline - if(len(samples['water']) < samples['size']): - samples['water'].append(smm.engineWaterTemp) - elif(not samples['avg_water']): - samples['avg_water'] = sum(samples['water'][1:])/len(samples['water'][1:]) - samples['warn_temp'] = max(samples['water']) - min(samples['water']) - samples['critical_temp'] = samples['warn_temp']*1.5 - if(len(samples['oil']) < samples['size']): - samples['oil'].append(smm.engineOilTemp) - elif(not samples['avg_oil']): - samples['avg_oil'] = sum(samples['oil'][1:])/len(samples['oil'][1:]) - if(current_sector == 1): - # show lap time compared to last/best/session best lap - et = time() - info_text_time - et_min = 0 - et_max = int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] - if(et >= et_min and et < et_max and settings['info_text']['lap_split']['enabled']): - if(dd.lastLapTime > 0): - dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(dd.lastLapTime, 60)) - else: - dash.left = '-.--.-' - if(compare_lap > 0 and dd.lastLapTime > 0): - dash.right = '{0:04.2f}'.format(dd.lastLapTime - compare_lap) - else: - dash.right = '--.--' - else: - # update comparison lap after lap display is done - if(dd.lastLapTime > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_previous'): - compare_lap = dd.lastLapTime - elif(dd.bestLapTime > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_best'): - compare_lap = dd.bestLapTime - elif(bestLapTimeSession > 0 and settings['info_text']['lap_split']['compare_lap'] == 'session_best'): - compare_lap = bestLapTimeSession - else: - compare_lap = 0 - # show position and number of cars in field - et_min += int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] - et_max += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] - if(et >= et_min and et < et_max and settings['info_text']['position']['enabled']): - dash.left = 'P{0}'.format(str(dd.place).rjust(3)) - dash.right = ' {0}'.format(str(smm.numVehicles).ljust(3)) - # show completed laps and laps/time remaining - et_min += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] - et_max += int(settings['info_text']['remaining']['enabled'])*settings['info_text']['duration'] - if(et >= et_min and et < et_max and settings['info_text']['remaining']['enabled']): - dash.left = 'L{0}'.format(str(dd.totalLaps).rjust(3)) - if(smm.maxLaps > 0 and smm.maxLaps < 200): - dash.right = ' {0}'.format(str(smm.maxLaps).ljust(3)) - elif(smm.endET > 0): - dash.right = '{0:02.0f}.{1:04.1f}'.format(*divmod(smm.endET - smm.currentET, 60)) - else: - dash.right = ' '*4 - elif(current_sector in [2, 0] and settings['info_text']['sector_split']['enabled'] and time() - info_text_time <= settings['info_text']['duration']): - # show sectors 1 and 2 splits - if(dd.lastSector1 > 0 and dd.lastSector2 > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_previous'): - compare_sector = dd.lastSector1 - if(current_sector == 0): - compare_sector = dd.lastSector2 - dd.lastSector1 - elif(dd.bestSector1 > 0 and dd.bestSector2 > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_best'): - compare_sector = dd.bestSector1 - if(current_sector == 0): - compare_sector = dd.bestSector2 - dd.bestSector1 - elif(bestSector1Session > 0 and bestSector2Session > 0 and settings['info_text']['sector_split']['compare_lap'] == 'session_best'): - compare_sector = bestSector1Session - if(current_sector == 0): - compare_sector = bestSector2Session - bestSector1Session + currentLapTime = 0 + # no running clock on invalid/out laps + if(currentLapTime > 0): + dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(currentLapTime, 60)) + else: + dash.left = '-.--.-' + # info text timer starts upon entering each sector + if(current_sector != dd.sector): + info_text_time = time() + current_sector = dd.sector + print_info = True + # calculate fuel use average continuously (dimishes over time) and ignore first sector after refuel + if(settings['fuel']['enabled'] and compare_fuel > 0 and compare_fuel > smm.fuel): + samples['fuel'].append(compare_fuel - smm.fuel) + if(len(samples['fuel']) > 3*settings['fuel']['samples']): + samples['fuel'] = samples['fuel'][-3*settings['fuel']['samples']:] + wn = 0 + wd = 0 + for i in xrange(0,len(samples['fuel'])): + wn += samples['fuel'][i]*(i+1) + wd += (i+1) + samples['avg_fuel'] = wn*3/wd + log_print("Average fuel use: {0:4.2f} L per lap".format(samples['avg_fuel'])) + compare_fuel = smm.fuel + if(current_sector == 1): + # show lap time compared to last/best/session best lap + et = time() - info_text_time + et_min = 0 + et_max = int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] + if(et >= et_min and et < et_max and settings['info_text']['lap_split']['enabled']): + if(dd.lastLapTime > 0): + dash.left = '{0:01.0f}.{1:04.1f}'.format(*divmod(dd.lastLapTime, 60)) else: - compare_sector = 0 - if(compare_sector > 0 and currentLapTime > 0): - sector_delta = dd.curSector1 - compare_sector - if(current_sector == 0): - sector_delta = (dd.curSector2 - dd.curSector1) - compare_sector - dash.right = '{0:04.2f}'.format(sector_delta) + dash.left = '-.--.-' + if(compare_lap > 0 and dd.lastLapTime > 0): + dash.right = '{0:04.2f}'.format(dd.lastLapTime - compare_lap) else: dash.right = '--.--' - # blink red status LED at critical fuel level - if(samples['avg_fuel'] > 0 and smm.fuel/samples['avg_fuel'] <= samples['warn_fuel']): - status[0] = '1' - if(smm.fuel/samples['avg_fuel'] < samples['critical_fuel']): - if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): - status[0] = '0' - else: - status[0] = '1' - if(settings['text_blink']['enabled'] and time() - blink_time['text'] <= settings['text_blink']['duration']): - dash.left = 'fuel' - # blink yellow status LED at critical oil/coolant temp - if((samples['avg_water'] and smm.engineWaterTemp - samples['avg_water'] >= samples['warn_temp']) or - (samples['avg_oil'] and smm.engineOilTemp - samples['avg_oil'] >= samples['warn_temp']) or smm.overheating): - status[1] = '1' - if((smm.engineWaterTemp - samples['avg_water'] > samples['critical_temp']) or - (smm.engineOilTemp - samples['avg_oil'] > samples['critical_temp']) or smm.overheating): - if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): - status[1] = '0' - else: - status[1] = '1' - if(settings['text_blink']['enabled'] and time() - blink_time['text'] <= settings['text_blink']['duration']): - dash.left = 'heat' - # blink green status LED while in pit/limiter active - if(smm.yellowFlagState == rfYellowFlagState.pitOpen or rfYellowFlagState.pitOpen in smm.sectorFlag): - status[3] = '1' - if(dd.inPits): + if(print_info): + log_print("Lap time (split): {0} ({1})".format(dash.left, dash.right)) + print_info = False + else: + # update comparison lap after lap display is done + if(dd.lastLapTime > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_previous'): + compare_lap = dd.lastLapTime + elif(dd.bestLapTime > 0 and settings['info_text']['lap_split']['compare_lap'] == 'self_best'): + compare_lap = dd.bestLapTime + elif(bestLapTimeSession > 0 and settings['info_text']['lap_split']['compare_lap'] == 'session_best'): + compare_lap = bestLapTimeSession + else: + compare_lap = 0 + # show position and number of cars in field + et_min += int(settings['info_text']['lap_split']['enabled'])*settings['info_text']['duration'] + et_max += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] + if(et >= et_min and et < et_max and settings['info_text']['position']['enabled']): + dash.left = 'P{0}'.format(str(dd.place).rjust(3)) + dash.right = ' {0}'.format(str(smm.numVehicles).ljust(3)) + # show completed laps and laps/time remaining + et_min += int(settings['info_text']['position']['enabled'])*settings['info_text']['duration'] + et_max += int(settings['info_text']['remaining']['enabled'])*settings['info_text']['duration'] + if(et >= et_min and et < et_max and settings['info_text']['remaining']['enabled']): + dash.left = 'L{0}'.format(str(dd.totalLaps).rjust(3)) + if(smm.maxLaps > 0 and smm.maxLaps < 200): + dash.right = ' {0}'.format(str(smm.maxLaps).ljust(3)) + elif(smm.endET > 0): + dash.right = '{0:02.0f}.{1:04.1f}'.format(*divmod(smm.endET - smm.currentET, 60)) + else: + dash.right = ' '*4 + elif(current_sector in [2, 0] and settings['info_text']['sector_split']['enabled'] and time() - info_text_time <= settings['info_text']['duration']): + # show sectors 1 and 2 splits + if(dd.lastSector1 > 0 and dd.lastSector2 > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_previous'): + compare_sector = dd.lastSector1 + if(current_sector == 0): + compare_sector = dd.lastSector2 - dd.lastSector1 + elif(dd.bestSector1 > 0 and dd.bestSector2 > 0 and settings['info_text']['sector_split']['compare_lap'] == 'self_best'): + compare_sector = dd.bestSector1 + if(current_sector == 0): + compare_sector = dd.bestSector2 - dd.bestSector1 + elif(bestSector1Session > 0 and bestSector2Session > 0 and settings['info_text']['sector_split']['compare_lap'] == 'session_best'): + compare_sector = bestSector1Session + if(current_sector == 0): + compare_sector = bestSector2Session - bestSector1Session + else: + compare_sector = 0 + if(compare_sector > 0 and currentLapTime > 0): + sector_delta = dd.curSector1 - compare_sector + if(current_sector == 0): + sector_delta = (dd.curSector2 - dd.curSector1) - compare_sector + dash.right = '{0:04.2f}'.format(sector_delta) + else: + dash.right = '--.--' + # blink red status LED at critical fuel level + if(settings['fuel']['enabled'] and samples['avg_fuel'] > 0 and smm.fuel/samples['avg_fuel'] <= settings['fuel']['warning']): + status[0] = '1' + if(smm.fuel/samples['avg_fuel'] < settings['fuel']['critical']): if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): - status[3] = '0' + status[0] = '0' else: - status[3] = '1' + status[0] = '1' if(settings['text_blink']['enabled'] and time() - blink_time['text'] <= settings['text_blink']['duration']): - dash.right = 'pit ' - # make sure engine is running - if(smm.engineRPM > 1): - dash.status = ''.join(status) - dash.update() - else: - dash.reset() - # never gets here - log_print("Closing shared memory map...") - rfMapHandle.close() - except: - log_print("Unhandled exception!") - log_print(format_exc()) + dash.left = 'fuel' + # blink yellow status LED at critical oil/coolant temp + if(settings['temperature']['enabled'] and smm.overheating): + if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): + status[1] = '0' + else: + status[1] = '1' + if(settings['text_blink']['enabled'] and time() - blink_time['text'] <= settings['text_blink']['duration']): + dash.left = 'heat' + # blink green status LED while in pit/limiter active + if(smm.yellowFlagState == rfYellowFlagState.pitOpen or rfYellowFlagState.pitOpen in smm.sectorFlag): + status[3] = '1' + if(dd.inPits): + if(settings['led_blink']['enabled'] and time() - blink_time['led'] <= settings['led_blink']['duration']): + status[3] = '0' + else: + status[3] = '1' + if(settings['text_blink']['enabled'] and time() - blink_time['text'] <= settings['text_blink']['duration']): + dash.right = 'pit ' + # make sure engine is running + if(dd and smm.engineRPM > 1): + dash.status = ''.join(status) + dash.update() + else: + dash.reset() + except: + log_print("Unhandled exception!") + log_print(format_exc()) + finally: + log_print("Closing shared memory map...") + rfMapHandle.close() + log_print("-"*16 + " RF1 SHUTDOWN " + "-"*16) diff --git a/pyinstaller/README.md b/pyinstaller/README.md index 4b90aee..490e5f9 100644 --- a/pyinstaller/README.md +++ b/pyinstaller/README.md @@ -1,12 +1,12 @@ -# pyDashR3E/pyDashRF1 +# pyDash by Dan Allongo (daniel.s.allongo@gmail.com) -Python applications that drive a Renovatio SRD-9c display (http://www.renovatio-dev.com/) using telemetry data from RaceRoom Racing Experience or rFactor 1. A gameplay video of `pyDashR3E` in use is available at https://youtu.be/A4aYvwhkx8I. +Python application that drives a Renovatio SRD-9c display (http://www.renovatio-dev.com/) using telemetry data from RaceRoom Racing Experience or rFactor 1. A gameplay video of `pyDash` in use is available at https://youtu.be/A4aYvwhkx8I. ### Usage -Simply double-click to run, plug in the display, and then start R3E or rF1/SCE/AMS. For titles based on rFactor 1, copy the included `rFactorSharedMemoryMap.dll` to the sim `Plugins` directory first. The application requires read and write access in its current working directory for logging and settings file operations. Changes made to the settings file are detected while the application is running - no need to restart. That's it! (Please note that if you run rF1/SCE/AMS as Administrator then you must run `pyDashRF1` as Administrator as well. Administrator privilege is not required otherwise.) +For titles based on rFactor 1, copy the included `rFactorSharedMemoryMap.dll` to the sim `Plugins` directory first. Simply plug in the display, double-click `pyDash.exe` to run, and then start R3E or rF1/SCE/AMS. The application requires read and write access in its current working directory for logging and settings file operations. Changes made to the settings file are detected while the application is running - no need to restart. That's it! (Please note that if you run rF1/SCE/AMS as Administrator then you must run `pyDash` as Administrator as well. Administrator privilege is not required otherwise.) ### Functions @@ -29,7 +29,7 @@ Simply double-click to run, plug in the display, and then start R3E or rF1/SCE/A ### Configuration -The file `pyDashR3E.settings.json` (or `pyDashR3E.settings.json`) is created on first run. +The file `pyDash.settings.json` is created on first run. Each of the functions can be enabled or disabled via the settings file. It also includes additional comments on how each setting changes the display behavior. Configuration changes made on the fly are detected as the application will re-read the settings file if it is modified while running. @@ -37,8 +37,8 @@ Configuration changes made on the fly are detected as the application will re-re ### Known Issues * Functions can be enabled or disabled but cannot be reassigned to different displays (ie, the speedometer and lap time displays cannot be switched, etc). The ability to fully customize the display and indicator locations is present in Renovatio's first-party software solution, so it's unnecessary to duplicate that functionality here. The main purpose of this application is to showcase capabilities not otherwise present in the first-party software. -* The RPM ranges for the LEDs are always linear and cannot be altered. This by design to maintain simplicity in the code. -* Fuel consumption and high temperature warnings are dynamic, being calculated based on the first 2 laps of the session (excluding the out lap). It's important to drive consistently in order for these functions to make appropriate estimates. As the application is storing this per-session calculation internally, restarting the application during an active session will result in incorrect estimates/unexpected behavior. +* The RPM ranges for the LEDs are always linear. This by design to maintain simplicity in the code. +* Fuel consumption and high temperature warnings are dynamic, being calculated based on the first 3 laps of the session (excluding the out lap). It's important to drive consistently in order for these functions to make appropriate estimates. As the application is storing this per-session calculation internally, restarting the application during an active session will result in incorrect estimates/unexpected behavior. * Lap and sector time comparisons for 'self last lap' will not show if the immediately previous or current lap are invalidated or if the application is restarted during an active session. It will take 2 clean laps in succession to resync the session lap comparison. * Sector 3 split times are never shown due to other important information being displayed upon crossing start/finish on each lap. * The order of the information displays at the start of each lap cannot be changed (ie, lap time will always be first and laps/time remaining will always be last). This is by design to maintain simplicity in the code. @@ -47,8 +47,7 @@ Configuration changes made on the fly are detected as the application will re-re ### Notes * Support for rFactor 2 is planned in the near future and should be released sometime in June 2016. -* Once rFactor 2 support is complete, all three utilities will be merged into a single `pyDash` application. -* Plans for a GUI to manage settings will depend on how the merged application will handle the settings JSON. +* Plans for a GUI to manage settings will depend on how the merged application will handle rFactor 2. * There are currently no plans to support Assetto Corsa, Project CARS, or iRacing since I don't own any of those sims. ### Licensing and Distribution diff --git a/pyinstaller/pyDashR3E.spec b/pyinstaller/pyDash.spec similarity index 81% rename from pyinstaller/pyDashR3E.spec rename to pyinstaller/pyDash.spec index 792935b..a521676 100644 --- a/pyinstaller/pyDashR3E.spec +++ b/pyinstaller/pyDash.spec @@ -3,7 +3,7 @@ block_cipher = None -a = Analysis(['pyDashR3E.py'], +a = Analysis(['pyDash.py'], pathex=['C:\\'], binaries=None, datas=None, @@ -21,8 +21,8 @@ exe = EXE(pyz, a.binaries, a.zipfiles, a.datas, - name='pyDashR3E', + name='pyDash', debug=False, strip=False, upx=True, - console=True , version='pyDashR3E.version.txt', icon='pyDash.ico') + console=True , version='pyDash.version.txt', icon='pyDash.ico') diff --git a/pyinstaller/pyDashR3E.version.txt b/pyinstaller/pyDash.version.txt similarity index 79% rename from pyinstaller/pyDashR3E.version.txt rename to pyinstaller/pyDash.version.txt index 533abdd..eed8463 100644 --- a/pyinstaller/pyDashR3E.version.txt +++ b/pyinstaller/pyDash.version.txt @@ -7,8 +7,8 @@ VSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. - filevers=(1, 1, 0, 0), - prodvers=(1, 1, 0, 0), + filevers=(2, 0, 0, 0), + prodvers=(2, 0, 0, 0), # Contains a bitmask that specifies the valid bits 'flags' mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. @@ -31,11 +31,11 @@ VSVersionInfo( StringTable( u'040904b0', [StringStruct(u'CompanyName', u'Dan Allongo (daniel.s.allongo@gmail.com)'), - StringStruct(u'ProductName', u'pyDashR3E'), - StringStruct(u'ProductVersion', u'1, 1, 0, 0'), - StringStruct(u'InternalName', u'pyDashR3E'), - StringStruct(u'OriginalFilename', u'pyDashR3E.exe'), - StringStruct(u'FileVersion', u'1, 0, 0, 0'), + StringStruct(u'ProductName', u'pyDash'), + StringStruct(u'ProductVersion', u'2, 0, 0, 0'), + StringStruct(u'InternalName', u'pyDash'), + StringStruct(u'OriginalFilename', u'pyDash.exe'), + StringStruct(u'FileVersion', u'2, 0, 0, 0'), StringStruct(u'FileDescription', u'Python sim racing dashboard'), StringStruct(u'LegalCopyright', u'GPL v2'), StringStruct(u'LegalTrademarks', u'https://github.com/dallongo/pySRD9c'),]) diff --git a/pyinstaller/pyDashRF1.spec b/pyinstaller/pyDashRF1.spec deleted file mode 100644 index f952e3a..0000000 --- a/pyinstaller/pyDashRF1.spec +++ /dev/null @@ -1,28 +0,0 @@ -# -*- mode: python -*- - -block_cipher = None - - -a = Analysis(['pyDashRF1.py'], - pathex=['C:\\'], - binaries=None, - datas=None, - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher) -pyz = PYZ(a.pure, a.zipped_data, - cipher=block_cipher) -exe = EXE(pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - name='pyDashRF1', - debug=False, - strip=False, - upx=True, - console=True , version='pyDashRF1.version.txt', icon='pyDash.ico') diff --git a/pyinstaller/pyDashRF1.version.txt b/pyinstaller/pyDashRF1.version.txt deleted file mode 100644 index 9484307..0000000 --- a/pyinstaller/pyDashRF1.version.txt +++ /dev/null @@ -1,45 +0,0 @@ - -# UTF-8 -# -# For more details about fixed file info 'ffi' see: -# http://msdn.microsoft.com/en-us/library/ms646997.aspx -VSVersionInfo( - ffi=FixedFileInfo( - # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) - # Set not needed items to zero 0. - filevers=(0, 9, 0, 0), - prodvers=(0, 9, 0, 0), - # Contains a bitmask that specifies the valid bits 'flags' - mask=0x3f, - # Contains a bitmask that specifies the Boolean attributes of the file. - flags=0x0, - # The operating system for which this file was designed. - # 0x4 - NT and there is no need to change it. - OS=0x4, - # The general type of file. - # 0x1 - the file is an application. - fileType=0x1, - # The function of the file. - # 0x0 - the function is not defined for this fileType - subtype=0x0, - # Creation date and time stamp. - date=(0, 0) - ), - kids=[ - StringFileInfo( - [ - StringTable( - u'040904b0', - [StringStruct(u'CompanyName', u'Dan Allongo (daniel.s.allongo@gmail.com)'), - StringStruct(u'ProductName', u'pyDashR3E'), - StringStruct(u'ProductVersion', u'0, 9, 0, 0'), - StringStruct(u'InternalName', u'pyDashRF1'), - StringStruct(u'OriginalFilename', u'pyDashRF1.exe'), - StringStruct(u'FileVersion', u'1, 0, 0, 0'), - StringStruct(u'FileDescription', u'Python sim racing dashboard'), - StringStruct(u'LegalCopyright', u'GPL v2'), - StringStruct(u'LegalTrademarks', u'https://github.com/dallongo/pySRD9c'),]) - ]), - VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) - ] -) \ No newline at end of file