def get_rpm_upshift(self): config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("drivetrain.ini")) try: return float(config["AUTO_SHIFTER"]["UP"]) except: log("Failed to get rpm upshit value:") for info in exc_info(): log(info) raise
def get_rpm_limiter(self): config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("engine.ini")) try: return float(config["ENGINE_DATA"]["LIMITER"]) except: log("Failed to get rpm limiter value:") for info in exc_info(): log(info) raise
def __load_from_folder(self, path): """ Loads the car information by the data folder. """ for file_name in os.listdir(path): file_path = "{}/{}".format(path, file_name) if os.path.isfile(file_path): try: with open(file_path, "r") as r: self.set_file(r.read(), file_name) except: log("Failed to open file {}:".format(file_name)) for info in exc_info(): log(info)
def get_power_curve(self): """ Returns the rpm x power curve. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("engine.ini")) try: return self.get_file(config["HEADER"]["POWER_CURVE"]) except: log("Failed to get rpm power curve:") for info in exc_info(): log(info) raise
def get_temp_curve(self, compound, wheel): """ Returns the compound temperature grip curve. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("tyres.ini")) try: name = "THERMAL_{}".format(get_tire_name(compound, config, wheel)) return self.get_file(config[name]["PERFORMANCE_CURVE"]) except: log("Failed to get tire temperature curve {}:".format(compound)) for info in exc_info(): log(info) raise
def get_ideal_pressure(self, compound, wheel): """ Returns the compound ideal pressure. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("tyres.ini")) try: name = get_tire_name(compound, config, wheel) return float(config[name]["PRESSURE_IDEAL"]) except: log("Failed to get tire ideal pressure:") for info in exc_info(): log(info) raise
def get_wear_curve(self, compound, wheel): """ Returns the compound wear curve. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("tyres.ini")) try: name = get_tire_name(compound, config, wheel) return self.get_file(config[name]["WEAR_CURVE"]) except: log("Failed to get tire wear curve {}:".format(compound)) for info in exc_info(): log(info) raise
def get_rpm_damage(self): config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("engine.ini")) try: if config.has_option("DAMAGE", "RPM_THRESHOLD"): res = config["DAMAGE"]["RPM_THRESHOLD"] else: res = self.get_rpm_limiter() + 100 return float(res) except: log("Failed to get rpm damage value:") for info in exc_info(): log(info) raise
def get_power_curve(self): """ Returns the rpm x power curve. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("engine.ini")) try: # Need to filter this file, thanks RSS file_content = self.get_file(config.get("HEADER", "POWER_CURVE")) return self.filter_mod_file_content(file_content) except: log("Failed to get rpm power curve:") for info in exc_info(): log(info) raise
def get_wear_curve(self, compound, wheel): """ Returns the compound wear curve. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("tyres.ini")) try: name = get_tire_name(compound, config, wheel) # Need to filter this file, thanks RSS file_content = self.get_file(config.get(name, "WEAR_CURVE")) return self.filter_mod_file_content(file_content) except: log("Failed to get tire wear curve {}:".format(compound)) for info in exc_info(): log(info) raise
def get_abs_hz(self): """ Returns the ABS active rate in Hz. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("electronics.ini")) if config.has_option("ABS", "RATE_HZ"): try: return float(config.get("ABS", "RATE_HZ")) except: log("Failed to get ABS rate:") for info in exc_info(): log(info) raise else: return 0.0
def get_tire_name(compound, config, wheel): """ Returns the compound session name on the tire.ini configuration file. """ prefix = "FRONT{}" if wheel.is_front() else "REAR{}" for i in range(10): name = prefix.format("" if i == 0 else "_{}".format(i)) # Check if the SHORT_NAME index exists for tire backward compatibility. if config.has_option( name, "SHORT_NAME") and config[name]["SHORT_NAME"] == compound: return name try: i = int(config["COMPOUND_DEFAULT"]["INDEX"]) return prefix.format("" if i == 0 else "_{}".format(i)) except: log("Failed to get tire name {}:".format(compound)) for info in exc_info(): log(info) raise
def __init__(self, path): """ Default constructor receives the ACD file path. """ path_v = path.split("/") # Initiate the class fields. self.__car = path_v[-1] self.__content = bytearray() self.__content_size = len(self.__content) self.__files = OrderedDict() self.__key = generate_key(self.__car) # Verify if the data.acd exists to load car information. data_acd_path = "{}/data.acd".format(path) if os.path.isfile(data_acd_path): log("Loading from data.acd...") self.__load_from_file(data_acd_path) else: # If it don't, try to load from data folder. log("Loading from data folder...") self.__load_from_folder("{}/data".format(path))
def get_abs_slip_ratio_list(self): """ Returns the abs slip ratio list. """ config = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";", )) config.read_string(self.get_file("electronics.ini")) if config.has_option("ABS", "SLIP_RATIO_LIMIT") or config.has_option( "ABS", "SLIP_RATIO_LIMIT"): try: return self.get_file(config["ABS"]["CURVE"]) except: log("Failed to get ABS slip ratio curve:") for info in exc_info(): log(info) log("Trying to get ratio limit:") try: return "0|{}".format(config["ABS"]["SLIP_RATIO_LIMIT"]) except: log("Failed to get ABS slip ratio limit:") for info in exc_info(): log(info) return "" else: return ""
def acMain(ac_version: str) -> None: """ Initiates the program. """ global ACD_OBJ global CONFIGS global LT_VERSION log("Starting Live Telemetry {} on AC Python API version {}...".format( LT_VERSION, ac_version)) log("Loading configs...") CONFIGS = Config(LT_VERSION) log("Loading {} info...".format(ac.getCarName(0))) ACD_OBJ = ACD("content/cars/{}".format(ac.getCarName(0))) log("Loaded correctly") log("Loading options window...") global OPTIONS_INFO OPTIONS_INFO = OptionsInfo(CONFIGS) ac.addOnClickedListener( OPTIONS_INFO.get_button_id("Camber"), on_click_camber) ac.addOnClickedListener(OPTIONS_INFO.get_button_id("Dirt"), on_click_dirt) ac.addOnClickedListener( OPTIONS_INFO.get_button_id("Height"), on_click_height) ac.addOnClickedListener(OPTIONS_INFO.get_button_id("Load"), on_click_load) ac.addOnClickedListener(OPTIONS_INFO.get_button_id("Lock"), on_click_lock) ac.addOnClickedListener( OPTIONS_INFO.get_button_id("Logging"), on_click_logging) ac.addOnClickedListener(OPTIONS_INFO.get_button_id( "Pressure"), on_click_pressure) ac.addOnClickedListener( OPTIONS_INFO.get_button_id("RPMPower"), on_click_rpm) ac.addOnClickedListener(OPTIONS_INFO.get_button_id("Size"), on_click_size) ac.addOnClickedListener(OPTIONS_INFO.get_button_id( "Suspension"), on_click_suspension) ac.addOnClickedListener( OPTIONS_INFO.get_button_id("Temps"), on_click_temps) ac.addOnClickedListener(OPTIONS_INFO.get_button_id("Tire"), on_click_tire) ac.addOnClickedListener(OPTIONS_INFO.get_button_id("Wear"), on_click_wear) log("Loading engine window...") global ENGINE_INFO ENGINE_INFO = EngineInfo(ACD_OBJ, CONFIGS) window_id = ENGINE_INFO.get_window_id() ac.addOnAppActivatedListener(window_id, on_activation) ac.addOnAppDismissedListener(window_id, on_dismiss) ac.addRenderCallback(ENGINE_INFO.get_window_id(), on_render_engine) log("Loading wheel windows...") global WHEEL_INFOS for index in range(4): info = WheelInfo(ACD_OBJ, CONFIGS, index) window_id = info.get_window_id() ac.addOnAppActivatedListener(window_id, on_activation) ac.addOnAppDismissedListener(window_id, on_dismiss) WHEEL_INFOS[info.get_id()] = info ac.addRenderCallback(WHEEL_INFOS["FL"].get_window_id(), on_render_fl) ac.addRenderCallback(WHEEL_INFOS["FR"].get_window_id(), on_render_fr) ac.addRenderCallback(WHEEL_INFOS["RL"].get_window_id(), on_render_rl) ac.addRenderCallback(WHEEL_INFOS["RR"].get_window_id(), on_render_rr) log("Live Telemetry started.") return "Live Telemetry"
def acShutdown() -> None: """ Called when the session ends (or restarts). """ log("Shutting down Live Telemetry...") global CONFIGS global ENGINE_INFO global OPTIONS_INFO global WHEEL_INFOS log("Saving options configurations...") CONFIGS.set_option("Camber", OPTIONS_INFO.get_option("Camber")) CONFIGS.set_option("Dirt", OPTIONS_INFO.get_option("Dirt")) CONFIGS.set_option("Height", OPTIONS_INFO.get_option("Height")) CONFIGS.set_option("Load", OPTIONS_INFO.get_option("Load")) CONFIGS.set_option("Lock", OPTIONS_INFO.get_option("Lock")) CONFIGS.set_option("Logging", OPTIONS_INFO.get_option("Logging")) CONFIGS.set_option("Pressure", OPTIONS_INFO.get_option("Pressure")) CONFIGS.set_option("RPMPower", OPTIONS_INFO.get_option("RPMPower")) CONFIGS.set_option("Size", OPTIONS_INFO.get_option("Size")) CONFIGS.set_option("Suspension", OPTIONS_INFO.get_option("Suspension")) CONFIGS.set_option("Temps", OPTIONS_INFO.get_option("Temps")) CONFIGS.set_option("Tire", OPTIONS_INFO.get_option("Tire")) CONFIGS.set_option("Wear", OPTIONS_INFO.get_option("Wear")) log("Saving windows configurations...") CONFIGS.set_window_active("EN", ENGINE_INFO.is_active()) CONFIGS.set_window_position("EN", ENGINE_INFO.get_position()) ENGINE_INFO.set_active(False) CONFIGS.set_window_position("OP", OPTIONS_INFO.get_position()) for wheel_id in WHEEL_INFOS: info = WHEEL_INFOS[wheel_id] CONFIGS.set_window_active(wheel_id, info.is_active()) CONFIGS.set_window_position(wheel_id, info.get_position()) info.set_active(False) CONFIGS.save_config() if ENGINE_INFO.has_data_logged() or WHEEL_INFOS["FL"].has_data_logged() or WHEEL_INFOS["FL"].has_data_logged() or WHEEL_INFOS["RL"].has_data_logged() or WHEEL_INFOS["RR"].has_data_logged(): log("Saving csv data...") for wheel_id in WHEEL_INFOS: info = WHEEL_INFOS[wheel_id] export_saved_log(info.get_data_log(), wheel_id) export_saved_log(ENGINE_INFO.get_data_log(), "EN") else: log("Deleting old csv data...") clear_logs() log("Live Telemetry ended.")
def __load_from_file(self, path): """ Loads the car information by the data.acd encrypted file. """ # Read all the file into memory. try: with open(path, "rb") as rb: self.__content = bytearray(rb.read()) self.__content_size = len(self.__content) except: log("Failed to open file {}:".format(path)) for info in exc_info(): log(info) if self.__content_size > 8: # Verify the "version" of the file. offset = 0 dummy = unpack("l", self.__content[offset:offset + 4])[0] offset += 4 if dummy < 0: # New cars, just pass the first 8 bytes. dummy = unpack("L", self.__content[offset:offset + 4])[0] offset += 4 else: # Old cars don't have any version. offset = 0 # Parse each inner file. while offset < self.__content_size: # Size of the file name. name_size = unpack("L", self.__content[offset:offset + 4])[0] offset += 4 # File name. file_name = self.__content[offset:offset + name_size].decode("utf8") offset += name_size log(file_name) # File size. file_size = unpack("L", self.__content[offset:offset + 4])[0] offset += 4 # Get the content and slices each 4 bytes. packed_content = self.__content[offset:offset + file_size * 4][::4] offset += file_size * 4 # Decrypt the content of the file. decrypted_content = "" key_size = len(self.__key) for i in range(file_size): code = packed_content[i] - ord(self.__key[i % key_size]) if code < 0: log("Invalid unicode code '{}' in file: {} @ byte {}". format(code, file_name, i)) log("Using '33' (!) to continue parsing file.") decrypted_content += chr(33 if code < 0 else code) # Save the decrypted file. self.set_file(decrypted_content, file_name) elif self.__content_size > 0: log("File too small to decrypt: {} bytes.".format( self.__content_size))
def __init__(self, lt_version: str) -> None: """ Loads or creates the app configuration file. """ self.__configs = ConfigParser() if path.isfile("apps/python/LiveTelemetry/cfg.ini"): self.__configs.read("apps/python/LiveTelemetry/cfg.ini") # If the config does not have it's version or is invalid, create a new one. try: config_version = self.get_version() if config_version != lt_version: log("Old config file. Could make things crash!") raise Exception("Old config file.") except: log("Creating new config file.") self.__configs["About"] = {"Version": lt_version} self.__configs["Options"] = {} self.__configs["Windows"] = {} self.__configs["Windows Positions"] = {} self.set_option("Camber", True) self.set_option("Dirt", True) self.set_option("Height", True) self.set_option("Load", True) self.set_option("Lock", True) self.set_option("Logging", False) self.set_option("Pressure", True) self.set_option("RPMPower", True) self.set_option("Size", "FHD") self.set_option("Suspension", True) self.set_option("Temps", True) self.set_option("Tire", True) self.set_option("Wear", True) self.set_window_active("EN", False) self.set_window_active("FL", False) self.set_window_active("FR", False) self.set_window_active("RL", False) self.set_window_active("RR", False) h = 720 w = 1280 docs_path = get_docs_path() if len(docs_path) > 0: try: video = ConfigParser() video.read( "{}/Assetto Corsa/cfg/video.ini".format(docs_path)) h = int(video.get("VIDEO", "HEIGHT")) if video.has_option( "VIDEO", "HEIGHT") else 720 w = int(video.get("VIDEO", "WIDTH")) if video.has_option( "VIDEO", "WIDTH") else 1280 except: log("Could not get 'cfg/video.ini' video options, using default 1280x720 resolution:" ) for info in exc_info(): log(info) self.set_window_position("EN", [floor((w - 360) / 2), h - 51 - 160]) self.set_window_position("FL", [10, 80]) self.set_window_position("FR", [w - 360 - 10, 80]) self.set_window_position( "OP", [w - 395 - 50, floor((h - 195) / 2)]) self.set_window_position("RL", [10, h - 163 - 80]) self.set_window_position("RR", [w - 360 - 10, h - 163 - 80]) self.save_config()