From 20ce2f1d5f697a39fb57569d7dab41fe4e7bf7de Mon Sep 17 00:00:00 2001 From: NGnius Date: Sat, 30 Jul 2022 16:33:31 -0400 Subject: [PATCH] Create Rust back-end (WIP) --- .gitignore | 3 + main.old.py | 541 ++++++++++++ main.py | 541 +----------- powertools-rs/Cargo.lock | 1111 +++++++++++++++++++++++++ powertools-rs/Cargo.toml | 15 + powertools-rs/Cross.toml | 2 + powertools-rs/build.sh | 6 + powertools-rs/src/main.rs | 40 + powertools-rs/src/persist/cpu.rs | 27 + powertools-rs/src/persist/general.rs | 56 ++ powertools-rs/src/persist/gpu.rs | 19 + powertools-rs/src/persist/mod.rs | 7 + powertools-rs/src/settings/cpu.rs | 47 ++ powertools-rs/src/settings/general.rs | 43 + powertools-rs/src/settings/gpu.rs | 35 + powertools-rs/src/settings/mod.rs | 7 + 16 files changed, 1967 insertions(+), 533 deletions(-) create mode 100644 main.old.py create mode 100644 powertools-rs/Cargo.lock create mode 100644 powertools-rs/Cargo.toml create mode 100644 powertools-rs/Cross.toml create mode 100755 powertools-rs/build.sh create mode 100644 powertools-rs/src/main.rs create mode 100644 powertools-rs/src/persist/cpu.rs create mode 100644 powertools-rs/src/persist/general.rs create mode 100644 powertools-rs/src/persist/gpu.rs create mode 100644 powertools-rs/src/persist/mod.rs create mode 100644 powertools-rs/src/settings/cpu.rs create mode 100644 powertools-rs/src/settings/general.rs create mode 100644 powertools-rs/src/settings/gpu.rs create mode 100644 powertools-rs/src/settings/mod.rs diff --git a/.gitignore b/.gitignore index d6fed4e..bef27a7 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ __pycache__/ /.yalc yalc.lock + +# rust +/powertools-rs/target diff --git a/main.old.py b/main.old.py new file mode 100644 index 0000000..df431b0 --- /dev/null +++ b/main.old.py @@ -0,0 +1,541 @@ +import time +import os +import json +import asyncio +import pathlib +import subprocess + +VERSION = "0.7.0" +HOME_DIR = "/home/deck" +DEFAULT_SETTINGS_LOCATION = HOME_DIR + "/.config/powertools/default_settings.json" +LOG_LOCATION = "/tmp/powertools.log" +FANTASTIC_INSTALL_DIR = HOME_DIR + "/homebrew/plugins/Fantastic" + +import logging + +logging.basicConfig( + filename = LOG_LOCATION, + format = '%(asctime)s %(levelname)s %(message)s', + filemode = 'w', + force = True) + +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +logging.info(f"PowerTools v{VERSION} https://github.com/NGnius/PowerTools") +logging.debug(f"CWD: {os.getcwd()} HOME:{HOME_DIR}") + +import sys +#import pathlib +sys.path.append(str(pathlib.Path(__file__).parent.resolve())) +import server as pt_server + +startup_time = time.time() + +class CPU: + SCALING_FREQUENCIES = [1700000, 2400000, 2800000] + + def __init__(self, number, settings=None): + self.number = number + + if settings is not None: + self.set_max_boost(settings["max_boost"]) + if settings["online"]: + self.enable() + else: + self.disable() + # TODO governor + + if(self.status()): + self.max_boost = self._get_max_boost() + else: + self.max_boost = CPU.SCALING_FREQUENCIES[-1] + + def enable(self): + # CPU number 0 is special + if(self.number == 0): + return + + filepath = cpu_online_path(self.number) + write_to_sys(filepath, 1) + + # The user might have changed the maximum cpu clock while the cpu was offline + self._set_max_boost(self.max_boost) + + def disable(self): + # CPU number 0 is special + if(self.number == 0): + return + + filepath = cpu_online_path(self.number) + write_to_sys(filepath, 0) + + def set_max_boost(self, frequency): + self.max_boost = frequency + if(self.status()): + self._set_max_boost(frequency) + + def status(self) -> bool: + # cpu number 0 is always online + if(self.number == 0): + return True + + filepath = cpu_online_path(self.number) + return read_from_sys(filepath) == "1" + + def governor(self) -> str: + return self._read_scaling_governor() + + def settings(self) -> dict: + return { + "online": self.status(), + "max_boost": self.max_boost, + "governor": self.governor(), + } + + def _read_scaling_governor(self) -> str: + filepath = cpu_governor_scaling_path(self.number) + return read_from_sys(filepath, amount=-1).strip() + + def _write_scaling_governor(self, governor: str): + filepath = cpu_governor_scaling_path(self.number) + with open(filepath, mode="w") as f: + f.write(governor) + + def _set_max_boost(self, frequency): + if(frequency == CPU.SCALING_FREQUENCIES[-1]): + self._write_scaling_governor("schedutil") + return + + if(self._read_scaling_governor() != "userspace"): + self._write_scaling_governor("userspace") + else: + filepath = cpu_freq_scaling_path(self.number) + write_to_sys(filepath, frequency) + + def _get_max_boost(self) -> int: + filepath = cpu_freq_scaling_path(self.number) + freq_maybe = read_from_sys(filepath, amount=-1).strip() + + if(freq_maybe is None or len(freq_maybe) == 0 or freq_maybe == ""): + return CPU.SCALING_FREQUENCIES[-1] + + freq = int(freq_maybe) + return freq + + +class Plugin: + CPU_COUNT = 8 + FAN_SPEEDS = [0, 1000, 2000, 3000, 4000, 5000, 6000] + + gpu_power_values = [[-1, -1, -1], [1000000, 15000000, 29000000], [0, 15000000, 30000000]] + + auto_fan = True + persistent = True + modified_settings = False + current_gameid = None + old_gameid = None + ready = False + + async def get_version(self) -> str: + return VERSION + + # CPU stuff + + # call from main_view.html with setCPUs(count, smt) + async def set_cpus(self, count, smt=True): + logging.info(f"set_cpus({count}, {smt})") + self.modified_settings = True + cpu_count = len(self.cpus) + self.smt = smt + # print("Setting CPUs") + if smt: + count = min(int(count), cpu_count) + for cpu in self.cpus[: count]: + cpu.enable() + for cpu in self.cpus[count :: 1]: + cpu.disable() + else: + count = min(int(count), cpu_count / 2) + # never touch cpu0, since it's special + for cpu in self.cpus[1 : cpu_count : 2]: + cpu.disable() + for cpu in self.cpus[2 : cpu_count : 2]: + if(cpu.number / 2 + 1 > count): + cpu.disable() + else: + cpu.enable() + + async def get_cpus(self) -> int: + online_count = 0 + for cpu in self.cpus: + if(cpu.status()): + online_count += 1 + logging.info(f"get_cpus() -> {online_count}") + return online_count + + async def get_smt(self) -> bool: + logging.info(f"get_smt() -> {self.smt}") + return self.smt + + async def set_boost(self, enabled: bool) -> bool: + self.modified_settings = True + write_cpu_boost(enabled) + return True + + async def get_boost(self) -> bool: + return read_cpu_boost() + + async def set_max_boost(self, index): + self.modified_settings = True + if index < 0 or index >= len(CPU.SCALING_FREQUENCIES): + return 0 + + selected_freq = CPU.SCALING_FREQUENCIES[index] + + for cpu in self.cpus: + cpu.set_max_boost(selected_freq) + + return len(self.cpus) + + async def get_max_boost(self) -> int: + return CPU.SCALING_FREQUENCIES.index(self.cpus[0].max_boost) + + # GPU stuff + + async def set_gpu_power(self, value: int, power_number: int) -> bool: + self.modified_settings = True + write_gpu_ppt(power_number, value) + return True + + async def get_gpu_power(self, power_number: int) -> int: + return read_gpu_ppt(power_number) + + async def set_gpu_power_index(self, index: int, power_number: int) -> bool: + if index < 3 and index >= 0: + self.modified_settings = True + old_value = read_gpu_ppt(power_number) + if old_value not in self.gpu_power_values[power_number]: + self.gpu_power_values[power_number][1] = old_value + write_gpu_ppt(power_number, self.gpu_power_values[power_number][index]) + return True + return False + + async def get_gpu_power_index(self, power_number: int) -> int: + value = read_gpu_ppt(power_number) + if value not in self.gpu_power_values[power_number]: + #self.gpu_power_values[power_number][1] = value + return 1 + else: + return self.gpu_power_values[power_number].index(value) + + # Fan stuff + + async def set_fan_tick(self, tick: int): + self.modified_settings = True + if tick >= len(self.FAN_SPEEDS): + # automatic mode + self.enable_jupiter_fan_control(self) + self.auto_fan = True + write_to_sys("/sys/class/hwmon/hwmon5/recalculate", 0) + write_to_sys("/sys/class/hwmon/hwmon5/fan1_target", 4099) # 4099 is default + else: + # manual voltage + self.disable_jupiter_fan_control(self) + self.auto_fan = False + write_to_sys("/sys/class/hwmon/hwmon5/recalculate", 1) + write_to_sys("/sys/class/hwmon/hwmon5/fan1_target", self.FAN_SPEEDS[tick]) + + async def get_fan_tick(self) -> int: + fan_target = read_fan_target() + fan_input = int(read_from_sys("/sys/class/hwmon/hwmon5/fan1_input", amount=-1).strip()) + fan_target_v = float(fan_target) / 1000 + fan_input_v = float(fan_input) / 1000 + if self.auto_fan: + return len(self.FAN_SPEEDS) + elif fan_target == 4099 or (int(round(fan_target_v)) != int(round(fan_input_v)) and fan_target not in self.FAN_SPEEDS): + # cannot read /sys/class/hwmon/hwmon5/recalculate, so guess based on available fan info + # NOTE: the fan takes time to ramp up, so fan_target will never approximately equal fan_input + # when fan_target was changed recently (hence set RPM caching) + return len(self.FAN_SPEEDS) + else: + # quantize RPM to nearest tick (price is right rules; closest without going over) + for i in range(len(self.FAN_SPEEDS)-1): + if fan_target <= self.FAN_SPEEDS[i]: + return i + return len(self.FAN_SPEEDS)-1 # any higher value is considered as highest manual setting + + async def fantastic_installed(self) -> bool: + return os.path.exists(FANTASTIC_INSTALL_DIR) + + def disable_jupiter_fan_control(self): + active = subprocess.Popen(["systemctl", "is-active", "jupiter-fan-control.service"]).wait() == 0 + if active: + logging.info("Stopping jupiter-fan-control.service so it doesn't interfere") + # only disable if currently active + self.jupiter_fan_control_was_disabled = True + stop_p = subprocess.Popen(["systemctl", "stop", "jupiter-fan-control.service"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stop_p.wait() + logging.debug("systemctl stop jupiter-fan-control.service stdout:\n" + stop_p.stdout.read().decode()) + logging.debug("systemctl stop jupiter-fan-control.service stderr:\n" + stop_p.stderr.read().decode()) + + def enable_jupiter_fan_control(self): + if self.jupiter_fan_control_was_disabled: + logging.info("Starting jupiter-fan-control.service so it doesn't interfere") + # only re-enable if I disabled it + self.jupiter_fan_control_was_disabled = False + start_p = subprocess.Popen(["systemctl", "start", "jupiter-fan-control.service"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + start_p.wait() + logging.debug("systemctl start jupiter-fan-control.service stdout:\n" + start_p.stdout.read().decode()) + logging.debug("systemctl start jupiter-fan-control.service stderr:\n" + start_p.stderr.read().decode()) + + # Battery stuff + + async def get_charge_now(self) -> int: + return int(read_from_sys("/sys/class/hwmon/hwmon2/device/charge_now", amount=-1).strip()) + + async def get_charge_full(self) -> int: + return int(read_from_sys("/sys/class/hwmon/hwmon2/device/charge_full", amount=-1).strip()) + + async def get_charge_design(self) -> int: + return int(read_from_sys("/sys/class/hwmon/hwmon2/device/charge_full_design", amount=-1).strip()) + + # Asyncio-compatible long-running code, executed in a task when the plugin is loaded + async def _main(self): + # startup: load & apply settings + self.jupiter_fan_control_was_disabled = False + if os.path.exists(DEFAULT_SETTINGS_LOCATION): + settings = read_json(DEFAULT_SETTINGS_LOCATION) + logging.debug(f"Loaded settings from {DEFAULT_SETTINGS_LOCATION}: {settings}") + else: + settings = None + logging.debug(f"Settings {DEFAULT_SETTINGS_LOCATION} does not exist, skipped") + if settings is None or settings["persistent"] == False: + logging.debug("Ignoring settings from file") + self.persistent = False + self.guess_settings(self) + self.modified_settings = True + else: + # apply settings + logging.debug("Restoring settings from file") + self.persistent = True + self.apply_settings(self, settings) + # self.modified_settings = False + logging.info("Handled saved settings, back-end startup complete") + # server setup + await pt_server.start(VERSION) + # work loop + while True: + # persistence + if self.modified_settings and self.persistent: + self.save_settings(self) + self.modified_settings = False + #self.reload_current_settings(self) + + await asyncio.sleep(1) + await pt_server.shutdown() + + # called from main_view::onViewReady + async def on_ready(self): + delta = time.time() - startup_time + if self.ready: + logging.info(f"Front-end init called again {delta}s after startup") + return + logging.info(f"Front-end initialised {delta}s after startup") + + # persistence + + async def get_persistent(self) -> bool: + return self.persistent + + async def set_persistent(self, enabled: bool): + logging.debug(f"Persistence is now: {enabled}") + self.persistent = enabled + self.save_settings(self) + + def current_settings(self) -> dict: + settings = dict() + settings["cpu"] = self.current_cpu_settings(self) + settings["gpu"] = self.current_gpu_settings(self) + settings["fan"] = self.current_fan_settings(self) + settings["persistent"] = self.persistent + return settings + + def current_cpu_settings(self) -> dict: + settings = dict() + cpu_settings = [] + for cpu in self.cpus: + cpu_settings.append(cpu.settings()) + settings["threads"] = cpu_settings + settings["smt"] = self.smt + settings["boost"] = read_cpu_boost() + return settings + + def current_gpu_settings(self) -> dict: + settings = dict() + settings["slowppt"] = read_gpu_ppt(1) + settings["fastppt"] = read_gpu_ppt(2) + return settings + + def current_fan_settings(self) -> dict: + settings = dict() + settings["target"] = read_fan_target() + settings["auto"] = self.auto_fan + return settings + + def reload_current_settings(self): + logging.debug(f"gameid update: {self.old_gameid} -> {self.current_gameid}") + if self.persistent: + # per-game profiles + current_game = pt_server.http_server.game() + self.old_gameid = self.current_gameid + if current_game is not None and current_game.has_settings(): + self.current_gameid = current_game.gameid + if self.old_gameid != self.current_gameid: + logging.info(f"Applying custom settings for {current_game.name()} {current_game.appid()}") + # new game; apply settings + settings = current_game.load_settings() + if settings is not None: + self.apply_settings(self, settings) + else: + self.current_gameid = None + if self.old_gameid != None: + logging.info("Reapplying default settings; game without custom settings found") + self.old_gameid = None + # game without custom settings; apply defaults + settings = read_json(DEFAULT_SETTINGS_LOCATION) + self.apply_settings(self, settings) + + def save_settings(self): + settings = self.current_settings(self) + logging.debug(f"Saving settings to file: {settings}") + current_game = pt_server.http_server.game() + if current_game is not None and self.current_gameid is not None: + save_location = current_game.settings_path() + else: + save_location = DEFAULT_SETTINGS_LOCATION + write_json(save_location, settings) + logging.info(f"Saved settings to {save_location}") + + def apply_settings(self, settings: dict): + # CPU + self.cpus = [] + + for cpu_number in range(0, Plugin.CPU_COUNT): + self.cpus.append(CPU(cpu_number, settings=settings["cpu"]["threads"][cpu_number])) + self.smt = settings["cpu"]["smt"] + write_cpu_boost(settings["cpu"]["boost"]) + # GPU + write_gpu_ppt(1, settings["gpu"]["slowppt"]) + write_gpu_ppt(2, settings["gpu"]["fastppt"]) + # Fan + if not (os.path.exists(FANTASTIC_INSTALL_DIR) or settings["fan"]["auto"]): + self.disable_jupiter_fan_control(self) + write_to_sys("/sys/class/hwmon/hwmon5/recalculate", 1) + write_to_sys("/sys/class/hwmon/hwmon5/fan1_target", settings["fan"]["target"]) + elif settings["fan"]["auto"] and not os.path.exists(FANTASTIC_INSTALL_DIR): + self.enable_jupiter_fan_control(self) + + + def guess_settings(self): + self.cpus = [] + for cpu_number in range(0, Plugin.CPU_COUNT): + self.cpus.append(CPU(cpu_number)) + + # If any core has two threads, smt is True + self.smt = self.cpus[1].status() + if(not self.smt): + for cpu_number in range(2, len(self.cpus), 2): + if(self.cpus[cpu_number].status()): + self.smt = True + break + logging.info(f"SMT state is guessed to be {self.smt}") + + # per-game profiles + + async def get_current_game(self) -> str: + current_game = pt_server.http_server.game() + if current_game is None: + return "Menu (default)" + else: + return f"{current_game.name()} ({current_game.appid()})" + + async def set_per_game_profile(self, enabled: bool): + current_game = pt_server.http_server.game() + if enabled and self.persistent and current_game is not None: + self.current_gameid = current_game.gameid + self.modified_settings = True + else: + if not enabled and current_game is not None and current_game.has_settings(): + # delete settings; disable settings loading + os.remove(current_game.settings_path()) + self.current_gameid = None + + async def get_per_game_profile(self) -> bool: + current_game = pt_server.http_server.game() + return current_game is not None and current_game.has_settings() + + async def on_game_start(self, game_id: int, data) -> bool: + pt_server.http_server.set_game(game_id, data) + self.reload_current_settings(self) + return True + + async def on_game_stop(self, game_id: int) -> bool: + pt_server.http_server.unset_game(game_id) + self.reload_current_settings(self) + return True + + + +# these are stateless (well, the state is not saved internally) functions, so there's no need for these to be called like a class method + +def cpu_online_path(cpu_number: int) -> str: + return f"/sys/devices/system/cpu/cpu{cpu_number}/online" + +def cpu_freq_scaling_path(cpu_number: int) -> str: + return f"/sys/devices/system/cpu/cpu{cpu_number}/cpufreq/scaling_setspeed" + +def cpu_governor_scaling_path(cpu_number: int) -> str: + return f"/sys/devices/system/cpu/cpu{cpu_number}/cpufreq/scaling_governor" + +def gpu_power_path(power_number: int) -> str: + return f"/sys/class/hwmon/hwmon4/power{power_number}_cap" + +def read_cpu_boost() -> bool: + return read_from_sys("/sys/devices/system/cpu/cpufreq/boost") == "1" + +def write_cpu_boost(enable: bool): + write_to_sys("/sys/devices/system/cpu/cpufreq/boost", int(enable)) + +def read_gpu_ppt(power_number: int) -> int: + return read_sys_int(gpu_power_path(power_number)) + +def write_gpu_ppt(power_number:int, value: int): + write_to_sys(gpu_power_path(power_number), value) + +def read_fan_target() -> int: + return read_sys_int("/sys/class/hwmon/hwmon5/fan1_target") + +def write_to_sys(path, value: int): + with open(path, mode="w") as f: + f.write(str(value)) + logging.debug(f"Wrote `{value}` to {path}") + +def read_from_sys(path, amount=1): + with open(path, mode="r") as f: + value = f.read(amount) + logging.debug(f"Read `{value}` from {path}") + return value + +def read_sys_int(path) -> int: + return int(read_from_sys(path, amount=-1).strip()) + +def write_json(path, data): + with open(path, mode="w") as f: + json.dump(data, f) # I always guess which is which param and I hate it + +def read_json(path): + with open(path, mode="r") as f: + return json.load(f) + +os_release = read_from_sys("/etc/os-release", amount=-1).strip() +logging.info(f"/etc/os-release\n{os_release}") diff --git a/main.py b/main.py index df431b0..e7435f5 100644 --- a/main.py +++ b/main.py @@ -1,541 +1,16 @@ -import time -import os -import json -import asyncio import pathlib import subprocess +import asyncio +import os -VERSION = "0.7.0" -HOME_DIR = "/home/deck" -DEFAULT_SETTINGS_LOCATION = HOME_DIR + "/.config/powertools/default_settings.json" -LOG_LOCATION = "/tmp/powertools.log" -FANTASTIC_INSTALL_DIR = HOME_DIR + "/homebrew/plugins/Fantastic" - -import logging - -logging.basicConfig( - filename = LOG_LOCATION, - format = '%(asctime)s %(levelname)s %(message)s', - filemode = 'w', - force = True) - -logger = logging.getLogger() -logger.setLevel(logging.DEBUG) -logging.info(f"PowerTools v{VERSION} https://github.com/NGnius/PowerTools") -logging.debug(f"CWD: {os.getcwd()} HOME:{HOME_DIR}") - -import sys -#import pathlib -sys.path.append(str(pathlib.Path(__file__).parent.resolve())) -import server as pt_server - -startup_time = time.time() - -class CPU: - SCALING_FREQUENCIES = [1700000, 2400000, 2800000] - - def __init__(self, number, settings=None): - self.number = number - - if settings is not None: - self.set_max_boost(settings["max_boost"]) - if settings["online"]: - self.enable() - else: - self.disable() - # TODO governor - - if(self.status()): - self.max_boost = self._get_max_boost() - else: - self.max_boost = CPU.SCALING_FREQUENCIES[-1] - - def enable(self): - # CPU number 0 is special - if(self.number == 0): - return - - filepath = cpu_online_path(self.number) - write_to_sys(filepath, 1) - - # The user might have changed the maximum cpu clock while the cpu was offline - self._set_max_boost(self.max_boost) - - def disable(self): - # CPU number 0 is special - if(self.number == 0): - return - - filepath = cpu_online_path(self.number) - write_to_sys(filepath, 0) - - def set_max_boost(self, frequency): - self.max_boost = frequency - if(self.status()): - self._set_max_boost(frequency) - - def status(self) -> bool: - # cpu number 0 is always online - if(self.number == 0): - return True - - filepath = cpu_online_path(self.number) - return read_from_sys(filepath) == "1" - - def governor(self) -> str: - return self._read_scaling_governor() - - def settings(self) -> dict: - return { - "online": self.status(), - "max_boost": self.max_boost, - "governor": self.governor(), - } - - def _read_scaling_governor(self) -> str: - filepath = cpu_governor_scaling_path(self.number) - return read_from_sys(filepath, amount=-1).strip() - - def _write_scaling_governor(self, governor: str): - filepath = cpu_governor_scaling_path(self.number) - with open(filepath, mode="w") as f: - f.write(governor) - - def _set_max_boost(self, frequency): - if(frequency == CPU.SCALING_FREQUENCIES[-1]): - self._write_scaling_governor("schedutil") - return - - if(self._read_scaling_governor() != "userspace"): - self._write_scaling_governor("userspace") - else: - filepath = cpu_freq_scaling_path(self.number) - write_to_sys(filepath, frequency) - - def _get_max_boost(self) -> int: - filepath = cpu_freq_scaling_path(self.number) - freq_maybe = read_from_sys(filepath, amount=-1).strip() - - if(freq_maybe is None or len(freq_maybe) == 0 or freq_maybe == ""): - return CPU.SCALING_FREQUENCIES[-1] - - freq = int(freq_maybe) - return freq - +HOME_DIR = str(pathlib.Path(os.getcwd()).parent.parent.resolve()) +PARENT_DIR = str(pathlib.Path(__file__).parent.resolve()) class Plugin: - CPU_COUNT = 8 - FAN_SPEEDS = [0, 1000, 2000, 3000, 4000, 5000, 6000] - - gpu_power_values = [[-1, -1, -1], [1000000, 15000000, 29000000], [0, 15000000, 30000000]] - - auto_fan = True - persistent = True - modified_settings = False - current_gameid = None - old_gameid = None - ready = False - - async def get_version(self) -> str: - return VERSION - - # CPU stuff - - # call from main_view.html with setCPUs(count, smt) - async def set_cpus(self, count, smt=True): - logging.info(f"set_cpus({count}, {smt})") - self.modified_settings = True - cpu_count = len(self.cpus) - self.smt = smt - # print("Setting CPUs") - if smt: - count = min(int(count), cpu_count) - for cpu in self.cpus[: count]: - cpu.enable() - for cpu in self.cpus[count :: 1]: - cpu.disable() - else: - count = min(int(count), cpu_count / 2) - # never touch cpu0, since it's special - for cpu in self.cpus[1 : cpu_count : 2]: - cpu.disable() - for cpu in self.cpus[2 : cpu_count : 2]: - if(cpu.number / 2 + 1 > count): - cpu.disable() - else: - cpu.enable() - - async def get_cpus(self) -> int: - online_count = 0 - for cpu in self.cpus: - if(cpu.status()): - online_count += 1 - logging.info(f"get_cpus() -> {online_count}") - return online_count - - async def get_smt(self) -> bool: - logging.info(f"get_smt() -> {self.smt}") - return self.smt - - async def set_boost(self, enabled: bool) -> bool: - self.modified_settings = True - write_cpu_boost(enabled) - return True - - async def get_boost(self) -> bool: - return read_cpu_boost() - - async def set_max_boost(self, index): - self.modified_settings = True - if index < 0 or index >= len(CPU.SCALING_FREQUENCIES): - return 0 - - selected_freq = CPU.SCALING_FREQUENCIES[index] - - for cpu in self.cpus: - cpu.set_max_boost(selected_freq) - - return len(self.cpus) - - async def get_max_boost(self) -> int: - return CPU.SCALING_FREQUENCIES.index(self.cpus[0].max_boost) - - # GPU stuff - - async def set_gpu_power(self, value: int, power_number: int) -> bool: - self.modified_settings = True - write_gpu_ppt(power_number, value) - return True - - async def get_gpu_power(self, power_number: int) -> int: - return read_gpu_ppt(power_number) - - async def set_gpu_power_index(self, index: int, power_number: int) -> bool: - if index < 3 and index >= 0: - self.modified_settings = True - old_value = read_gpu_ppt(power_number) - if old_value not in self.gpu_power_values[power_number]: - self.gpu_power_values[power_number][1] = old_value - write_gpu_ppt(power_number, self.gpu_power_values[power_number][index]) - return True - return False - - async def get_gpu_power_index(self, power_number: int) -> int: - value = read_gpu_ppt(power_number) - if value not in self.gpu_power_values[power_number]: - #self.gpu_power_values[power_number][1] = value - return 1 - else: - return self.gpu_power_values[power_number].index(value) - - # Fan stuff - - async def set_fan_tick(self, tick: int): - self.modified_settings = True - if tick >= len(self.FAN_SPEEDS): - # automatic mode - self.enable_jupiter_fan_control(self) - self.auto_fan = True - write_to_sys("/sys/class/hwmon/hwmon5/recalculate", 0) - write_to_sys("/sys/class/hwmon/hwmon5/fan1_target", 4099) # 4099 is default - else: - # manual voltage - self.disable_jupiter_fan_control(self) - self.auto_fan = False - write_to_sys("/sys/class/hwmon/hwmon5/recalculate", 1) - write_to_sys("/sys/class/hwmon/hwmon5/fan1_target", self.FAN_SPEEDS[tick]) - - async def get_fan_tick(self) -> int: - fan_target = read_fan_target() - fan_input = int(read_from_sys("/sys/class/hwmon/hwmon5/fan1_input", amount=-1).strip()) - fan_target_v = float(fan_target) / 1000 - fan_input_v = float(fan_input) / 1000 - if self.auto_fan: - return len(self.FAN_SPEEDS) - elif fan_target == 4099 or (int(round(fan_target_v)) != int(round(fan_input_v)) and fan_target not in self.FAN_SPEEDS): - # cannot read /sys/class/hwmon/hwmon5/recalculate, so guess based on available fan info - # NOTE: the fan takes time to ramp up, so fan_target will never approximately equal fan_input - # when fan_target was changed recently (hence set RPM caching) - return len(self.FAN_SPEEDS) - else: - # quantize RPM to nearest tick (price is right rules; closest without going over) - for i in range(len(self.FAN_SPEEDS)-1): - if fan_target <= self.FAN_SPEEDS[i]: - return i - return len(self.FAN_SPEEDS)-1 # any higher value is considered as highest manual setting - - async def fantastic_installed(self) -> bool: - return os.path.exists(FANTASTIC_INSTALL_DIR) - - def disable_jupiter_fan_control(self): - active = subprocess.Popen(["systemctl", "is-active", "jupiter-fan-control.service"]).wait() == 0 - if active: - logging.info("Stopping jupiter-fan-control.service so it doesn't interfere") - # only disable if currently active - self.jupiter_fan_control_was_disabled = True - stop_p = subprocess.Popen(["systemctl", "stop", "jupiter-fan-control.service"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stop_p.wait() - logging.debug("systemctl stop jupiter-fan-control.service stdout:\n" + stop_p.stdout.read().decode()) - logging.debug("systemctl stop jupiter-fan-control.service stderr:\n" + stop_p.stderr.read().decode()) - - def enable_jupiter_fan_control(self): - if self.jupiter_fan_control_was_disabled: - logging.info("Starting jupiter-fan-control.service so it doesn't interfere") - # only re-enable if I disabled it - self.jupiter_fan_control_was_disabled = False - start_p = subprocess.Popen(["systemctl", "start", "jupiter-fan-control.service"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - start_p.wait() - logging.debug("systemctl start jupiter-fan-control.service stdout:\n" + start_p.stdout.read().decode()) - logging.debug("systemctl start jupiter-fan-control.service stderr:\n" + start_p.stderr.read().decode()) - - # Battery stuff - - async def get_charge_now(self) -> int: - return int(read_from_sys("/sys/class/hwmon/hwmon2/device/charge_now", amount=-1).strip()) - - async def get_charge_full(self) -> int: - return int(read_from_sys("/sys/class/hwmon/hwmon2/device/charge_full", amount=-1).strip()) - - async def get_charge_design(self) -> int: - return int(read_from_sys("/sys/class/hwmon/hwmon2/device/charge_full_design", amount=-1).strip()) - + backend_proc = None # Asyncio-compatible long-running code, executed in a task when the plugin is loaded async def _main(self): - # startup: load & apply settings - self.jupiter_fan_control_was_disabled = False - if os.path.exists(DEFAULT_SETTINGS_LOCATION): - settings = read_json(DEFAULT_SETTINGS_LOCATION) - logging.debug(f"Loaded settings from {DEFAULT_SETTINGS_LOCATION}: {settings}") - else: - settings = None - logging.debug(f"Settings {DEFAULT_SETTINGS_LOCATION} does not exist, skipped") - if settings is None or settings["persistent"] == False: - logging.debug("Ignoring settings from file") - self.persistent = False - self.guess_settings(self) - self.modified_settings = True - else: - # apply settings - logging.debug("Restoring settings from file") - self.persistent = True - self.apply_settings(self, settings) - # self.modified_settings = False - logging.info("Handled saved settings, back-end startup complete") - # server setup - await pt_server.start(VERSION) - # work loop + # startup + self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"]) while True: - # persistence - if self.modified_settings and self.persistent: - self.save_settings(self) - self.modified_settings = False - #self.reload_current_settings(self) - - await asyncio.sleep(1) - await pt_server.shutdown() - - # called from main_view::onViewReady - async def on_ready(self): - delta = time.time() - startup_time - if self.ready: - logging.info(f"Front-end init called again {delta}s after startup") - return - logging.info(f"Front-end initialised {delta}s after startup") - - # persistence - - async def get_persistent(self) -> bool: - return self.persistent - - async def set_persistent(self, enabled: bool): - logging.debug(f"Persistence is now: {enabled}") - self.persistent = enabled - self.save_settings(self) - - def current_settings(self) -> dict: - settings = dict() - settings["cpu"] = self.current_cpu_settings(self) - settings["gpu"] = self.current_gpu_settings(self) - settings["fan"] = self.current_fan_settings(self) - settings["persistent"] = self.persistent - return settings - - def current_cpu_settings(self) -> dict: - settings = dict() - cpu_settings = [] - for cpu in self.cpus: - cpu_settings.append(cpu.settings()) - settings["threads"] = cpu_settings - settings["smt"] = self.smt - settings["boost"] = read_cpu_boost() - return settings - - def current_gpu_settings(self) -> dict: - settings = dict() - settings["slowppt"] = read_gpu_ppt(1) - settings["fastppt"] = read_gpu_ppt(2) - return settings - - def current_fan_settings(self) -> dict: - settings = dict() - settings["target"] = read_fan_target() - settings["auto"] = self.auto_fan - return settings - - def reload_current_settings(self): - logging.debug(f"gameid update: {self.old_gameid} -> {self.current_gameid}") - if self.persistent: - # per-game profiles - current_game = pt_server.http_server.game() - self.old_gameid = self.current_gameid - if current_game is not None and current_game.has_settings(): - self.current_gameid = current_game.gameid - if self.old_gameid != self.current_gameid: - logging.info(f"Applying custom settings for {current_game.name()} {current_game.appid()}") - # new game; apply settings - settings = current_game.load_settings() - if settings is not None: - self.apply_settings(self, settings) - else: - self.current_gameid = None - if self.old_gameid != None: - logging.info("Reapplying default settings; game without custom settings found") - self.old_gameid = None - # game without custom settings; apply defaults - settings = read_json(DEFAULT_SETTINGS_LOCATION) - self.apply_settings(self, settings) - - def save_settings(self): - settings = self.current_settings(self) - logging.debug(f"Saving settings to file: {settings}") - current_game = pt_server.http_server.game() - if current_game is not None and self.current_gameid is not None: - save_location = current_game.settings_path() - else: - save_location = DEFAULT_SETTINGS_LOCATION - write_json(save_location, settings) - logging.info(f"Saved settings to {save_location}") - - def apply_settings(self, settings: dict): - # CPU - self.cpus = [] - - for cpu_number in range(0, Plugin.CPU_COUNT): - self.cpus.append(CPU(cpu_number, settings=settings["cpu"]["threads"][cpu_number])) - self.smt = settings["cpu"]["smt"] - write_cpu_boost(settings["cpu"]["boost"]) - # GPU - write_gpu_ppt(1, settings["gpu"]["slowppt"]) - write_gpu_ppt(2, settings["gpu"]["fastppt"]) - # Fan - if not (os.path.exists(FANTASTIC_INSTALL_DIR) or settings["fan"]["auto"]): - self.disable_jupiter_fan_control(self) - write_to_sys("/sys/class/hwmon/hwmon5/recalculate", 1) - write_to_sys("/sys/class/hwmon/hwmon5/fan1_target", settings["fan"]["target"]) - elif settings["fan"]["auto"] and not os.path.exists(FANTASTIC_INSTALL_DIR): - self.enable_jupiter_fan_control(self) - - - def guess_settings(self): - self.cpus = [] - for cpu_number in range(0, Plugin.CPU_COUNT): - self.cpus.append(CPU(cpu_number)) - - # If any core has two threads, smt is True - self.smt = self.cpus[1].status() - if(not self.smt): - for cpu_number in range(2, len(self.cpus), 2): - if(self.cpus[cpu_number].status()): - self.smt = True - break - logging.info(f"SMT state is guessed to be {self.smt}") - - # per-game profiles - - async def get_current_game(self) -> str: - current_game = pt_server.http_server.game() - if current_game is None: - return "Menu (default)" - else: - return f"{current_game.name()} ({current_game.appid()})" - - async def set_per_game_profile(self, enabled: bool): - current_game = pt_server.http_server.game() - if enabled and self.persistent and current_game is not None: - self.current_gameid = current_game.gameid - self.modified_settings = True - else: - if not enabled and current_game is not None and current_game.has_settings(): - # delete settings; disable settings loading - os.remove(current_game.settings_path()) - self.current_gameid = None - - async def get_per_game_profile(self) -> bool: - current_game = pt_server.http_server.game() - return current_game is not None and current_game.has_settings() - - async def on_game_start(self, game_id: int, data) -> bool: - pt_server.http_server.set_game(game_id, data) - self.reload_current_settings(self) - return True - - async def on_game_stop(self, game_id: int) -> bool: - pt_server.http_server.unset_game(game_id) - self.reload_current_settings(self) - return True - - - -# these are stateless (well, the state is not saved internally) functions, so there's no need for these to be called like a class method - -def cpu_online_path(cpu_number: int) -> str: - return f"/sys/devices/system/cpu/cpu{cpu_number}/online" - -def cpu_freq_scaling_path(cpu_number: int) -> str: - return f"/sys/devices/system/cpu/cpu{cpu_number}/cpufreq/scaling_setspeed" - -def cpu_governor_scaling_path(cpu_number: int) -> str: - return f"/sys/devices/system/cpu/cpu{cpu_number}/cpufreq/scaling_governor" - -def gpu_power_path(power_number: int) -> str: - return f"/sys/class/hwmon/hwmon4/power{power_number}_cap" - -def read_cpu_boost() -> bool: - return read_from_sys("/sys/devices/system/cpu/cpufreq/boost") == "1" - -def write_cpu_boost(enable: bool): - write_to_sys("/sys/devices/system/cpu/cpufreq/boost", int(enable)) - -def read_gpu_ppt(power_number: int) -> int: - return read_sys_int(gpu_power_path(power_number)) - -def write_gpu_ppt(power_number:int, value: int): - write_to_sys(gpu_power_path(power_number), value) - -def read_fan_target() -> int: - return read_sys_int("/sys/class/hwmon/hwmon5/fan1_target") - -def write_to_sys(path, value: int): - with open(path, mode="w") as f: - f.write(str(value)) - logging.debug(f"Wrote `{value}` to {path}") - -def read_from_sys(path, amount=1): - with open(path, mode="r") as f: - value = f.read(amount) - logging.debug(f"Read `{value}` from {path}") - return value - -def read_sys_int(path) -> int: - return int(read_from_sys(path, amount=-1).strip()) - -def write_json(path, data): - with open(path, mode="w") as f: - json.dump(data, f) # I always guess which is which param and I hate it - -def read_json(path): - with open(path, mode="r") as f: - return json.load(f) - -os_release = read_from_sys("/etc/os-release", amount=-1).strip() -logging.info(f"/etc/os-release\n{os_release}") + asyncio.sleep(1) diff --git a/powertools-rs/Cargo.lock b/powertools-rs/Cargo.lock new file mode 100644 index 0000000..f2a64e5 --- /dev/null +++ b/powertools-rs/Cargo.lock @@ -0,0 +1,1111 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.7.3", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1 0.10.0", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error", + "rand", + "safemem", + "tempfile", + "twoway", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "powertools-rs" +version = "0.1.0" +dependencies = [ + "log", + "serde", + "serde_json", + "simplelog", + "usdpl-back", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "serde" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "simplelog" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786" +dependencies = [ + "log", + "termcolor", + "time", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-stream" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand", + "sha-1 0.9.8", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "usdpl-back" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d237439986405621b9b6da350aefcfca9e2b808c10695f55f8b80ccc324b2c0" +dependencies = [ + "bytes", + "tokio", + "usdpl-core", + "warp", +] + +[[package]] +name = "usdpl-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd726b9f0121d4449082e3ce73586dea0a0448494031833b7b173e4476f0ea5" +dependencies = [ + "base64", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multipart", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tokio-util 0.6.10", + "tower-service", + "tracing", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/powertools-rs/Cargo.toml b/powertools-rs/Cargo.toml new file mode 100644 index 0000000..f8047fc --- /dev/null +++ b/powertools-rs/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "powertools-rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +usdpl-back = { version = "0.5.3", features = ["blocking"]} +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# logging +log = "0.4" +simplelog = "0.12" diff --git a/powertools-rs/Cross.toml b/powertools-rs/Cross.toml new file mode 100644 index 0000000..89ae1de --- /dev/null +++ b/powertools-rs/Cross.toml @@ -0,0 +1,2 @@ +[build] +default-target = "x86_64-unknown-linux-gnu" diff --git a/powertools-rs/build.sh b/powertools-rs/build.sh new file mode 100755 index 0000000..d9b898f --- /dev/null +++ b/powertools-rs/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cargo build --release +mkdir ../bin +# TODO replace "backend" \/ with binary name +cp ./target/release/backend ../bin/backend diff --git a/powertools-rs/src/main.rs b/powertools-rs/src/main.rs new file mode 100644 index 0000000..6f1edab --- /dev/null +++ b/powertools-rs/src/main.rs @@ -0,0 +1,40 @@ +mod persist; +mod settings; + +use simplelog::{WriteLogger, LevelFilter}; + +use usdpl_back::Instance; +use usdpl_back::core::serdes::Primitive; + +const PORT: u16 = 44443; + +const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME"); +const PACKAGE_VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +fn main() -> Result<(), ()> { + let log_filepath = format!("/tmp/{}.log", PACKAGE_NAME); + WriteLogger::init( + #[cfg(debug_assertions)]{LevelFilter::Debug}, + #[cfg(not(debug_assertions))]{LevelFilter::Info}, + Default::default(), + std::fs::File::create(&log_filepath).unwrap() + ).unwrap(); + log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION); + println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION); + + let default_settings: settings::Settings = persist::SettingsJson::open( + settings_dir().join("default_settings.json") + ).unwrap_or_default().into(); + + log::debug!("Settings: {:?}", default_settings); + + Instance::new(PORT) + .register("hello", |_: Vec| vec![format!("Hello {}", PACKAGE_NAME).into()]) + .run_blocking() +} + +fn settings_dir() -> std::path::PathBuf { + usdpl_back::api::dirs::home() + .unwrap_or_else(|| "/home/deck".into()) + .join(".config/powertools/") +} diff --git a/powertools-rs/src/persist/cpu.rs b/powertools-rs/src/persist/cpu.rs new file mode 100644 index 0000000..c2cb66f --- /dev/null +++ b/powertools-rs/src/persist/cpu.rs @@ -0,0 +1,27 @@ +use std::default::Default; +//use std::fmt::Display; + +use serde::{Serialize, Deserialize}; + +const SCALING_FREQUENCIES: &[u64] = &[1700000, 2400000, 2800000]; + +#[derive(Serialize, Deserialize)] +pub struct CpuJson { + pub online: bool, + pub max_boost: u64, + pub min_boost: u64, + pub governor: String, + pub boost: bool, +} + +impl Default for CpuJson { + fn default() -> Self { + Self { + online: true, + max_boost: SCALING_FREQUENCIES[SCALING_FREQUENCIES.len() - 1], + min_boost: SCALING_FREQUENCIES[0], + governor: "schedutil".to_owned(), + boost: true, + } + } +} diff --git a/powertools-rs/src/persist/general.rs b/powertools-rs/src/persist/general.rs new file mode 100644 index 0000000..aafc146 --- /dev/null +++ b/powertools-rs/src/persist/general.rs @@ -0,0 +1,56 @@ +use std::default::Default; +use std::fmt::Display; + +use serde::{Serialize, Deserialize}; + +use super::{CpuJson, GpuJson}; + +#[derive(Serialize, Deserialize)] +pub struct SettingsJson { + pub version: u64, + pub persistent: bool, + pub cpus: Vec, + pub gpu: GpuJson, +} + +impl Default for SettingsJson { + fn default() -> Self { + Self { + version: 0, + persistent: false, + cpus: Vec::with_capacity(8), + gpu: GpuJson::default(), + } + } +} + +impl SettingsJson { + pub fn save>(&self, path: P) -> Result<(), JsonError> { + let path = path.as_ref(); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent).map_err(JsonError::Io)?; + } + let mut file = std::fs::File::create(path).map_err(JsonError::Io)?; + serde_json::to_writer_pretty(&mut file, &self).map_err(JsonError::Serde) + } + + pub fn open>(path: P) -> Result { + let mut file = std::fs::File::open(path).map_err(JsonError::Io)?; + serde_json::from_reader(&mut file).map_err(JsonError::Serde) + } +} + +#[derive(Debug)] +pub enum JsonError { + Serde(serde_json::Error), + Io(std::io::Error), +} + +impl Display for JsonError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Serde(e) => (e as &dyn Display).fmt(f), + Self::Io(e) => (e as &dyn Display).fmt(f), + } + } +} diff --git a/powertools-rs/src/persist/gpu.rs b/powertools-rs/src/persist/gpu.rs new file mode 100644 index 0000000..41c3c84 --- /dev/null +++ b/powertools-rs/src/persist/gpu.rs @@ -0,0 +1,19 @@ +use std::default::Default; +//use std::fmt::Display; + +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize)] +pub struct GpuJson { + pub fast_ppt: Option, + pub slow_ppt: Option, +} + +impl Default for GpuJson { + fn default() -> Self { + Self { + fast_ppt: None, + slow_ppt: None, + } + } +} diff --git a/powertools-rs/src/persist/mod.rs b/powertools-rs/src/persist/mod.rs new file mode 100644 index 0000000..ab8b31b --- /dev/null +++ b/powertools-rs/src/persist/mod.rs @@ -0,0 +1,7 @@ +mod cpu; +mod general; +mod gpu; + +pub use cpu::CpuJson; +pub use general::SettingsJson; +pub use gpu::GpuJson; diff --git a/powertools-rs/src/settings/cpu.rs b/powertools-rs/src/settings/cpu.rs new file mode 100644 index 0000000..bf7c72a --- /dev/null +++ b/powertools-rs/src/settings/cpu.rs @@ -0,0 +1,47 @@ +use std::convert::Into; + +use crate::persist::CpuJson; + +#[derive(Debug, Clone)] +pub struct Cpu { + pub online: bool, + pub max_boost: u64, + pub min_boost: u64, + pub governor: String, + pub boost: bool, +} + +impl Cpu { + #[inline] + pub fn from_json(other: CpuJson, version: u64) -> Self { + match version { + 0 => Self { + online: other.online, + max_boost: other.max_boost, + min_boost: other.min_boost, + governor: other.governor, + boost: other.boost, + }, + _ => Self { + online: other.online, + max_boost: other.max_boost, + min_boost: other.min_boost, + governor: other.governor, + boost: other.boost, + } + } + } +} + +impl Into for Cpu { + #[inline] + fn into(self) -> CpuJson { + CpuJson { + online: self.online, + max_boost: self.max_boost, + min_boost: self.min_boost, + governor: self.governor, + boost: self.boost, + } + } +} diff --git a/powertools-rs/src/settings/general.rs b/powertools-rs/src/settings/general.rs new file mode 100644 index 0000000..c997159 --- /dev/null +++ b/powertools-rs/src/settings/general.rs @@ -0,0 +1,43 @@ +use std::convert::{Into, From}; + +use crate::persist::SettingsJson; +use super::{Cpu, Gpu}; + +const LATEST_VERSION: u64 = 0; + +#[derive(Debug, Clone)] +pub struct Settings { + pub persistent: bool, + pub cpus: Vec, + pub gpu: Gpu, +} + +impl From for Settings { + #[inline] + fn from(mut other: SettingsJson) -> Self { + match other.version { + 0 => Self { + persistent: other.persistent, + cpus: other.cpus.drain(..).map(|cpu| Cpu::from_json(cpu, other.version)).collect(), + gpu: Gpu::from_json(other.gpu, other.version), + }, + _ => Self { + persistent: other.persistent, + cpus: other.cpus.drain(..).map(|cpu| Cpu::from_json(cpu, other.version)).collect(), + gpu: Gpu::from_json(other.gpu, other.version), + } + } + } +} + +impl Into for Settings { + #[inline] + fn into(mut self) -> SettingsJson { + SettingsJson { + version: LATEST_VERSION, + persistent: self.persistent, + cpus: self.cpus.drain(..).map(|cpu| cpu.into()).collect(), + gpu: self.gpu.into() + } + } +} diff --git a/powertools-rs/src/settings/gpu.rs b/powertools-rs/src/settings/gpu.rs new file mode 100644 index 0000000..4432313 --- /dev/null +++ b/powertools-rs/src/settings/gpu.rs @@ -0,0 +1,35 @@ +use std::convert::Into; + +use crate::persist::GpuJson; + +#[derive(Debug, Clone)] +pub struct Gpu { + pub fast_ppt: Option, + pub slow_ppt: Option, +} + +impl Gpu { + #[inline] + pub fn from_json(other: GpuJson, version: u64) -> Self { + match version { + 0 => Self { + fast_ppt: other.fast_ppt, + slow_ppt: other.slow_ppt, + }, + _ => Self { + fast_ppt: other.fast_ppt, + slow_ppt: other.slow_ppt, + } + } + } +} + +impl Into for Gpu { + #[inline] + fn into(self) -> GpuJson { + GpuJson { + fast_ppt: self.fast_ppt, + slow_ppt: self.slow_ppt, + } + } +} diff --git a/powertools-rs/src/settings/mod.rs b/powertools-rs/src/settings/mod.rs new file mode 100644 index 0000000..adfb0d4 --- /dev/null +++ b/powertools-rs/src/settings/mod.rs @@ -0,0 +1,7 @@ +mod cpu; +mod general; +mod gpu; + +pub use cpu::Cpu; +pub use general::Settings; +pub use gpu::Gpu;