LenovoLegionToolkit
267 строк · 11.3 Кб
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Threading.Tasks;5using LenovoLegionToolkit.Lib.Extensions;6using LenovoLegionToolkit.Lib.Settings;7using LenovoLegionToolkit.Lib.SoftwareDisabler;8using LenovoLegionToolkit.Lib.Utils;9
10namespace LenovoLegionToolkit.Lib.Controllers.GodMode;11
12public abstract class AbstractGodModeController : IGodModeController13{
14private readonly GodModeSettings _settings;15
16protected readonly VantageDisabler VantageDisabler;17protected readonly LegionZoneDisabler LegionZoneDisabler;18
19public event EventHandler<Guid>? PresetChanged;20
21protected AbstractGodModeController(GodModeSettings settings, VantageDisabler vantageDisabler, LegionZoneDisabler legionZoneDisabler)22{23_settings = settings ?? throw new ArgumentNullException(nameof(settings));24VantageDisabler = vantageDisabler ?? throw new ArgumentNullException(nameof(vantageDisabler));25LegionZoneDisabler = legionZoneDisabler ?? throw new ArgumentNullException(nameof(legionZoneDisabler));26}27
28public abstract Task<bool> NeedsVantageDisabledAsync();29
30public abstract Task<bool> NeedsLegionZoneDisabledAsync();31
32public Task<Guid> GetActivePresetIdAsync() => Task.FromResult(_settings.Store.ActivePresetId);33
34public Task<string?> GetActivePresetNameAsync()35{36var store = _settings.Store;37var name = store.Presets38.Where(p => p.Key == store.ActivePresetId)39.Select(p => p.Value.Name)40.FirstOrDefault();41return Task.FromResult(name);42}43
44public async Task<GodModeState> GetStateAsync()45{46if (Log.Instance.IsTraceEnabled)47Log.Instance.Trace($"Getting state...");48
49var store = _settings.Store;50var defaultState = await GetDefaultStateAsync().ConfigureAwait(false);51
52if (!IsValidStore(store))53{54if (Log.Instance.IsTraceEnabled)55Log.Instance.Trace($"Loading default state...");56
57var id = Guid.NewGuid();58return new GodModeState59{60ActivePresetId = id,61Presets = new Dictionary<Guid, GodModePreset> { { id, defaultState } }.AsReadOnlyDictionary()62};63}64
65if (Log.Instance.IsTraceEnabled)66Log.Instance.Trace($"Loading state from store...");67
68return await LoadStateFromStoreAsync(store, defaultState).ConfigureAwait(false);69}70
71public Task SetStateAsync(GodModeState state)72{73if (Log.Instance.IsTraceEnabled)74Log.Instance.Trace($"Settings state: {state}");75
76var activePresetId = state.ActivePresetId;77var presets = new Dictionary<Guid, GodModeSettings.GodModeSettingsStore.Preset>();78
79foreach (var (id, preset) in state.Presets)80{81presets.Add(id, new()82{83Name = preset.Name,84CPULongTermPowerLimit = preset.CPULongTermPowerLimit,85CPUShortTermPowerLimit = preset.CPUShortTermPowerLimit,86CPUPeakPowerLimit = preset.CPUPeakPowerLimit,87CPUCrossLoadingPowerLimit = preset.CPUCrossLoadingPowerLimit,88CPUPL1Tau = preset.CPUPL1Tau,89APUsPPTPowerLimit = preset.APUsPPTPowerLimit,90CPUTemperatureLimit = preset.CPUTemperatureLimit,91GPUPowerBoost = preset.GPUPowerBoost,92GPUConfigurableTGP = preset.GPUConfigurableTGP,93GPUTemperatureLimit = preset.GPUTemperatureLimit,94GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = preset.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline,95GPUToCPUDynamicBoost = preset.GPUToCPUDynamicBoost,96FanTable = preset.FanTableInfo?.Table,97FanFullSpeed = preset.FanFullSpeed,98MinValueOffset = preset.MinValueOffset,99MaxValueOffset = preset.MaxValueOffset,100});101}102
103_settings.Store.ActivePresetId = activePresetId;104_settings.Store.Presets = presets;105_settings.SynchronizeStore();106
107if (Log.Instance.IsTraceEnabled)108Log.Instance.Trace($"State saved.");109
110return Task.CompletedTask;111}112
113public abstract Task ApplyStateAsync();114
115public Task<FanTable> GetDefaultFanTableAsync()116{117var fanTable = new FanTable(new ushort[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });118return Task.FromResult(fanTable);119}120
121public abstract Task<FanTable> GetMinimumFanTableAsync();122
123public abstract Task<Dictionary<PowerModeState, GodModeDefaults>> GetDefaultsInOtherPowerModesAsync();124
125public abstract Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState state);126
127protected abstract Task<GodModePreset> GetDefaultStateAsync();128
129protected void RaisePresetChanged(Guid presetId) => PresetChanged?.Invoke(this, presetId);130
131protected async Task<(Guid, GodModeSettings.GodModeSettingsStore.Preset)> GetActivePresetAsync()132{133if (!IsValidStore(_settings.Store))134{135if (Log.Instance.IsTraceEnabled)136Log.Instance.Trace($"Invalid store, generating default one.");137
138var state = await GetStateAsync().ConfigureAwait(false);139await SetStateAsync(state).ConfigureAwait(false);140}141
142var activePresetId = _settings.Store.ActivePresetId;143var presets = _settings.Store.Presets;144
145if (presets.TryGetValue(activePresetId, out var activePreset))146return (activePresetId, activePreset);147
148throw new InvalidOperationException($"Preset with ID {activePresetId} not found.");149}150
151protected async Task<bool> IsValidFanTableAsync(FanTable fanTable)152{153var minimumFanTable = await GetMinimumFanTableAsync().ConfigureAwait(false);154var minimum = minimumFanTable.GetTable();155return fanTable.GetTable().Where((t, i) => t < minimum[i] || t > 10u).IsEmpty();156}157
158private static bool IsValidStore(GodModeSettings.GodModeSettingsStore store) => store.Presets.Any() && store.Presets.ContainsKey(store.ActivePresetId);159
160private async Task<GodModeState> LoadStateFromStoreAsync(GodModeSettings.GodModeSettingsStore store, GodModePreset defaultState)161{162var states = new Dictionary<Guid, GodModePreset>();163
164foreach (var (id, preset) in store.Presets)165{166states.Add(id, new GodModePreset167{168Name = preset.Name,169CPULongTermPowerLimit = CreateStepperValue(defaultState.CPULongTermPowerLimit, preset.CPULongTermPowerLimit, preset.MinValueOffset, preset.MaxValueOffset),170CPUShortTermPowerLimit = CreateStepperValue(defaultState.CPUShortTermPowerLimit, preset.CPUShortTermPowerLimit, preset.MinValueOffset, preset.MaxValueOffset),171CPUPeakPowerLimit = CreateStepperValue(defaultState.CPUPeakPowerLimit, preset.CPUPeakPowerLimit, preset.MinValueOffset, preset.MaxValueOffset),172CPUCrossLoadingPowerLimit = CreateStepperValue(defaultState.CPUCrossLoadingPowerLimit, preset.CPUCrossLoadingPowerLimit, preset.MinValueOffset, preset.MaxValueOffset),173CPUPL1Tau = CreateStepperValue(defaultState.CPUPL1Tau, preset.CPUPL1Tau, preset.MinValueOffset, preset.MaxValueOffset),174APUsPPTPowerLimit = CreateStepperValue(defaultState.APUsPPTPowerLimit, preset.APUsPPTPowerLimit, preset.MinValueOffset, preset.MaxValueOffset),175CPUTemperatureLimit = CreateStepperValue(defaultState.CPUTemperatureLimit, preset.CPUTemperatureLimit, preset.MinValueOffset, preset.MaxValueOffset),176GPUPowerBoost = CreateStepperValue(defaultState.GPUPowerBoost, preset.GPUPowerBoost, preset.MinValueOffset, preset.MaxValueOffset),177GPUConfigurableTGP = CreateStepperValue(defaultState.GPUConfigurableTGP, preset.GPUConfigurableTGP, preset.MinValueOffset, preset.MaxValueOffset),178GPUTemperatureLimit = CreateStepperValue(defaultState.GPUTemperatureLimit, preset.GPUTemperatureLimit, preset.MinValueOffset, preset.MaxValueOffset),179GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = CreateStepperValue(defaultState.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline,180preset.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline,181preset.MinValueOffset,182preset.MaxValueOffset),183GPUToCPUDynamicBoost = CreateStepperValue(defaultState.GPUToCPUDynamicBoost, preset.GPUToCPUDynamicBoost),184FanTableInfo = await GetFanTableInfoAsync(preset, defaultState.FanTableInfo?.Data).ConfigureAwait(false),185FanFullSpeed = preset.FanFullSpeed,186MinValueOffset = preset.MinValueOffset ?? defaultState.MinValueOffset,187MaxValueOffset = preset.MaxValueOffset ?? defaultState.MaxValueOffset188});189}190
191return new GodModeState192{193ActivePresetId = store.ActivePresetId,194Presets = states.AsReadOnlyDictionary()195};196}197
198private static StepperValue? CreateStepperValue(StepperValue? state, StepperValue? store = null, int? minValueOffset = 0, int? maxValueOffset = 0)199{200if (state is not { } stateValue)201return null;202
203if (stateValue.Steps.Length > 0)204{205var value = store?.Value ?? stateValue.Value;206var steps = stateValue.Steps;207var defaultValue = stateValue.DefaultValue;208
209if (!steps.Contains(value))210{211var valueTemp = value;212value = steps.MinBy(v => Math.Abs((long)v - valueTemp));213}214
215return new(value, 0, 0, 0, steps, defaultValue);216}217
218if (stateValue.Step > 0)219{220var value = store?.Value ?? stateValue.Value;221var min = Math.Max(0, stateValue.Min + (minValueOffset ?? 0));222var max = stateValue.Max + (maxValueOffset ?? 0);223var step = stateValue.Step;224var defaultValue = stateValue.DefaultValue;225
226value = MathExtensions.RoundNearest(value, step);227
228if (value < min || value > max)229value = defaultValue ?? Math.Clamp(value, min, max);230
231return new(value, min, max, step, Array.Empty<int>(), defaultValue);232}233
234return null;235}236
237private async Task<FanTableInfo?> GetFanTableInfoAsync(GodModeSettings.GodModeSettingsStore.Preset preset, FanTableData[]? fanTableData)238{239if (Log.Instance.IsTraceEnabled)240Log.Instance.Trace($"Getting fan table info...");241
242if (fanTableData is null)243{244if (Log.Instance.IsTraceEnabled)245Log.Instance.Trace($"Fan table data is null");246return null;247}248
249if (Log.Instance.IsTraceEnabled)250Log.Instance.Trace($"Fan table data retrieved: {fanTableData}");251
252var fanTable = preset.FanTable ?? await GetDefaultFanTableAsync().ConfigureAwait(false);253
254if (Log.Instance.IsTraceEnabled)255Log.Instance.Trace($"Fan table retrieved: {fanTable}");256
257if (!await IsValidFanTableAsync(fanTable).ConfigureAwait(false))258{259if (Log.Instance.IsTraceEnabled)260Log.Instance.Trace($"Fan table invalid, replacing with default...");261
262fanTable = await GetDefaultFanTableAsync().ConfigureAwait(false);263}264
265return new FanTableInfo(fanTableData, fanTable);266}267}
268