LenovoLegionToolkit
417 строк · 15.2 Кб
1using System;2using System.Collections.Generic;3using System.Linq;4using System.Threading;5using System.Threading.Tasks;6using LenovoLegionToolkit.Lib.AutoListeners;7using LenovoLegionToolkit.Lib.Automation.Pipeline;8using LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers;9using LenovoLegionToolkit.Lib.Automation.Utils;10using LenovoLegionToolkit.Lib.Controllers.GodMode;11using LenovoLegionToolkit.Lib.Listeners;12using LenovoLegionToolkit.Lib.Utils;13using NeoSmart.AsyncLock;14
15namespace LenovoLegionToolkit.Lib.Automation;16
17public class AutomationProcessor18{
19private readonly AutomationSettings _settings;20private readonly NativeWindowsMessageListener _nativeWindowsMessageListener;21private readonly PowerStateListener _powerStateListener;22private readonly PowerModeListener _powerModeListener;23private readonly GodModeController _godModeController;24private readonly GameAutoListener _gameAutoListener;25private readonly ProcessAutoListener _processAutoListener;26private readonly TimeAutoListener _timeAutoListener;27private readonly UserInactivityAutoListener _userInactivityAutoListener;28private readonly WiFiAutoListener _wifiAutoListener;29
30private readonly AsyncLock _ioLock = new();31private readonly AsyncLock _runLock = new();32
33private List<AutomationPipeline> _pipelines = new();34private CancellationTokenSource? _cts;35
36public bool IsEnabled => _settings.Store.IsEnabled;37
38public event EventHandler<List<AutomationPipeline>>? PipelinesChanged;39
40public AutomationProcessor(AutomationSettings settings,41NativeWindowsMessageListener nativeWindowsMessageListener,42PowerStateListener powerStateListener,43PowerModeListener powerModeListener,44GodModeController godModeController,45GameAutoListener gameAutoListener,46ProcessAutoListener processAutoListener,47TimeAutoListener timeAutoListener,48UserInactivityAutoListener userInactivityAutoListener,49WiFiAutoListener wifiAutoListener)50{51_settings = settings ?? throw new ArgumentNullException(nameof(settings));52_nativeWindowsMessageListener = nativeWindowsMessageListener ?? throw new ArgumentNullException(nameof(nativeWindowsMessageListener));53_powerStateListener = powerStateListener ?? throw new ArgumentNullException(nameof(powerStateListener));54_powerModeListener = powerModeListener ?? throw new ArgumentNullException(nameof(powerModeListener));55_godModeController = godModeController ?? throw new ArgumentNullException(nameof(godModeController));56_gameAutoListener = gameAutoListener ?? throw new ArgumentNullException(nameof(gameAutoListener));57_processAutoListener = processAutoListener ?? throw new ArgumentNullException(nameof(processAutoListener));58_timeAutoListener = timeAutoListener ?? throw new ArgumentNullException(nameof(timeAutoListener));59_userInactivityAutoListener = userInactivityAutoListener ?? throw new ArgumentNullException(nameof(userInactivityAutoListener));60_wifiAutoListener = wifiAutoListener ?? throw new ArgumentNullException(nameof(wifiAutoListener));61}62
63#region Initialization / pipeline reloading64
65public async Task InitializeAsync()66{67using (await _ioLock.LockAsync().ConfigureAwait(false))68{69_nativeWindowsMessageListener.Changed += NativeWindowsMessageListener_Changed;70_powerStateListener.Changed += PowerStateListener_Changed;71_powerModeListener.Changed += PowerModeListener_Changed;72_godModeController.PresetChanged += GodModeController_PresetChanged;73
74_pipelines = _settings.Store.Pipelines.ToList();75
76RaisePipelinesChanged();77
78await UpdateListenersAsync().ConfigureAwait(false);79}80}81
82public async Task SetEnabledAsync(bool enabled)83{84using (await _ioLock.LockAsync().ConfigureAwait(false))85{86_settings.Store.IsEnabled = enabled;87_settings.SynchronizeStore();88
89await UpdateListenersAsync().ConfigureAwait(false);90}91}92
93public async Task ReloadPipelinesAsync(List<AutomationPipeline> pipelines)94{95if (Log.Instance.IsTraceEnabled)96Log.Instance.Trace($"Pipelines reload pending...");97
98using (await _ioLock.LockAsync().ConfigureAwait(false))99{100if (Log.Instance.IsTraceEnabled)101Log.Instance.Trace($"Pipelines reloading...");102
103_pipelines = pipelines.Select(p => p.DeepCopy()).ToList();104
105_settings.Store.Pipelines = pipelines;106_settings.SynchronizeStore();107
108RaisePipelinesChanged();109
110await UpdateListenersAsync().ConfigureAwait(false);111
112if (Log.Instance.IsTraceEnabled)113Log.Instance.Trace($"Pipelines reloaded.");114}115}116
117public async Task<List<AutomationPipeline>> GetPipelinesAsync()118{119using (await _ioLock.LockAsync().ConfigureAwait(false))120return _pipelines.Select(p => p.DeepCopy()).ToList();121}122
123#endregion124
125#region Run126
127public void RunOnStartup()128{129if (!IsEnabled)130{131if (Log.Instance.IsTraceEnabled)132Log.Instance.Trace($"Not enabled. Pipeline run on startup ignored.");133
134return;135}136
137if (Log.Instance.IsTraceEnabled)138Log.Instance.Trace($"Pipeline run on startup pending...");139
140Task.Run(() => ProcessEvent(new StartupAutomationEvent()));141}142
143public async Task RunNowAsync(AutomationPipeline pipeline)144{145if (Log.Instance.IsTraceEnabled)146Log.Instance.Trace($"Pipeline run now pending...");147
148using (await _runLock.LockAsync().ConfigureAwait(false))149{150if (Log.Instance.IsTraceEnabled)151Log.Instance.Trace($"Pipeline run starting...");152
153try154{155await pipeline.DeepCopy().RunAsync().ConfigureAwait(false);156
157if (Log.Instance.IsTraceEnabled)158Log.Instance.Trace($"Pipeline run finished successfully.");159}160catch (Exception ex)161{162if (Log.Instance.IsTraceEnabled)163Log.Instance.Trace($"Pipeline run failed.", ex);164
165throw;166}167}168}169
170public async Task RunNowAsync(Guid pipelineId)171{172using (await _runLock.LockAsync().ConfigureAwait(false))173{174var pipeline = _pipelines.Where(p => p.Trigger is null).FirstOrDefault(p => p.Id == pipelineId);175if (pipeline is null)176return;177
178await RunNowAsync(pipeline).ConfigureAwait(false);179}180}181
182private async Task RunAsync(IAutomationEvent automationEvent)183{184if (Log.Instance.IsTraceEnabled)185Log.Instance.Trace($"Run pending...");186
187using (await _runLock.LockAsync().ConfigureAwait(false))188{189if (Log.Instance.IsTraceEnabled)190Log.Instance.Trace($"Run starting...");191
192_cts?.Cancel();193
194if (!IsEnabled)195return;196
197List<AutomationPipeline> pipelines;198using (await _ioLock.LockAsync().ConfigureAwait(false))199pipelines = _pipelines;200
201_cts = new CancellationTokenSource();202var ct = _cts.Token;203
204foreach (var pipeline in pipelines)205{206if (ct.IsCancellationRequested)207{208if (Log.Instance.IsTraceEnabled)209Log.Instance.Trace($"Run interrupted.");210break;211}212
213try214{215if (pipeline.Trigger is null || !await pipeline.Trigger.IsMatchingEvent(automationEvent).ConfigureAwait(false))216{217if (Log.Instance.IsTraceEnabled)218Log.Instance.Trace($"Pipeline triggers not satisfied. [name={pipeline.Name}, trigger={pipeline.Trigger}, steps.Count={pipeline.Steps.Count}]");219continue;220}221
222if (Log.Instance.IsTraceEnabled)223Log.Instance.Trace($"Running pipeline... [name={pipeline.Name}, trigger={pipeline.Trigger}, steps.Count={pipeline.Steps.Count}]");224
225await pipeline.RunAsync(ct).ConfigureAwait(false);226
227if (Log.Instance.IsTraceEnabled)228Log.Instance.Trace($"Pipeline completed successfully. [name={pipeline.Name}, trigger={pipeline.Trigger}]");229}230catch (Exception ex)231{232if (Log.Instance.IsTraceEnabled)233Log.Instance.Trace($"Pipeline run failed. [name={pipeline.Name}, trigger={pipeline.Trigger}]", ex);234}235
236if (pipeline.IsExclusive)237{238if (Log.Instance.IsTraceEnabled)239Log.Instance.Trace($"Pipeline is exclusive. Breaking. [name={pipeline.Name}, trigger={pipeline.Trigger}, steps.Count={pipeline.Steps.Count}]");240break;241}242}243
244if (Log.Instance.IsTraceEnabled)245Log.Instance.Trace($"Run finished successfully.");246}247}248
249#endregion250
251#region Listeners252
253private async void NativeWindowsMessageListener_Changed(object? sender, NativeWindowsMessage message)254{255var e = new NativeWindowsMessageEvent { Message = message };256await ProcessEvent(e).ConfigureAwait(false);257}258
259private async void PowerStateListener_Changed(object? sender, EventArgs _)260{261var e = new PowerStateAutomationEvent();262await ProcessEvent(e).ConfigureAwait(false);263}264
265private async void PowerModeListener_Changed(object? sender, PowerModeState powerModeState)266{267var e = new PowerModeAutomationEvent { PowerModeState = powerModeState };268await ProcessEvent(e).ConfigureAwait(false);269}270
271private async void GodModeController_PresetChanged(object? sender, Guid presetId)272{273var e = new CustomModePresetAutomationEvent { Id = presetId };274await ProcessEvent(e).ConfigureAwait(false);275}276
277private async void GameAutoListener_Changed(object? sender, bool started)278{279var e = new GameAutomationEvent { Started = started };280await ProcessEvent(e).ConfigureAwait(false);281}282
283private async void ProcessAutoListener_Changed(object? sender, ProcessEventInfo processEventInfo)284{285var e = new ProcessAutomationEvent { ProcessEventInfo = processEventInfo };286await ProcessEvent(e).ConfigureAwait(false);287}288
289private async void TimeAutoListener_Changed(object? sender, (Time time, DayOfWeek day) timeDay)290{291var e = new TimeAutomationEvent { Time = timeDay.time, Day = timeDay.day };292await ProcessEvent(e).ConfigureAwait(false);293}294
295private async void UserInactivityAutoListener_Changed(object? sender, (TimeSpan resolution, uint tickCount) inactivityInfo)296{297var e = new UserInactivityAutomationEvent298{299InactivityTimeSpan = inactivityInfo.resolution * inactivityInfo.tickCount,300ResolutionTimeSpan = inactivityInfo.resolution301};302await ProcessEvent(e).ConfigureAwait(false);303}304
305private async void WiFiAutoListener_Changed(object? sender, (bool connected, string? ssid) wifiInfo)306{307var e = new WiFiAutomationEvent308{309IsConnected = wifiInfo.connected,310Ssid = wifiInfo.ssid311};312await ProcessEvent(e).ConfigureAwait(false);313}314
315#endregion316
317#region Event processing318
319private async Task ProcessEvent(IAutomationEvent e)320{321var potentialMatch = _pipelines.SelectMany(p => p.AllTriggers)322.Select(async t => await t.IsMatchingEvent(e).ConfigureAwait(false))323.Select(t => t.Result)324.Where(t => t)325.Any();326
327if (!potentialMatch)328return;329
330if (Log.Instance.IsTraceEnabled)331Log.Instance.Trace($"Processing event {e}... [type={e.GetType().Name}]");332
333await RunAsync(e).ConfigureAwait(false);334}335
336#endregion337
338#region Helper methods339
340private async Task UpdateListenersAsync()341{342if (Log.Instance.IsTraceEnabled)343Log.Instance.Trace($"Stopping listeners...");344
345await _gameAutoListener.UnsubscribeChangedAsync(GameAutoListener_Changed).ConfigureAwait(false);346await _processAutoListener.UnsubscribeChangedAsync(ProcessAutoListener_Changed).ConfigureAwait(false);347await _timeAutoListener.UnsubscribeChangedAsync(TimeAutoListener_Changed).ConfigureAwait(false);348await _userInactivityAutoListener.UnsubscribeChangedAsync(UserInactivityAutoListener_Changed).ConfigureAwait(false);349await _wifiAutoListener.UnsubscribeChangedAsync(WiFiAutoListener_Changed).ConfigureAwait(false);350
351if (Log.Instance.IsTraceEnabled)352Log.Instance.Trace($"Stopped listeners...");353
354if (!IsEnabled)355{356if (Log.Instance.IsTraceEnabled)357Log.Instance.Trace($"Not enabled. Will not start listeners.");358return;359}360
361if (Log.Instance.IsTraceEnabled)362Log.Instance.Trace($"Starting listeners...");363
364var triggers = _pipelines.SelectMany(p => p.AllTriggers).ToArray();365
366if (triggers.OfType<IGameAutomationPipelineTrigger>().Any())367{368if (Log.Instance.IsTraceEnabled)369Log.Instance.Trace($"Starting game listener...");370
371await _gameAutoListener.SubscribeChangedAsync(GameAutoListener_Changed).ConfigureAwait(false);372}373
374if (triggers.OfType<IProcessesAutomationPipelineTrigger>().Any())375{376if (Log.Instance.IsTraceEnabled)377Log.Instance.Trace($"Starting process listener...");378
379await _processAutoListener.SubscribeChangedAsync(ProcessAutoListener_Changed).ConfigureAwait(false);380}381
382if (triggers.OfType<ITimeAutomationPipelineTrigger>().Any())383{384if (Log.Instance.IsTraceEnabled)385Log.Instance.Trace($"Starting time listener...");386
387await _timeAutoListener.SubscribeChangedAsync(TimeAutoListener_Changed).ConfigureAwait(false);388}389
390if (triggers.OfType<IUserInactivityPipelineTrigger>().Any())391{392if (Log.Instance.IsTraceEnabled)393Log.Instance.Trace($"Starting user inactivity listener...");394
395await _userInactivityAutoListener.SubscribeChangedAsync(UserInactivityAutoListener_Changed).ConfigureAwait(false);396}397
398if (triggers.OfType<IWiFiConnectedPipelineTrigger>().Any() || triggers.OfType<WiFiDisconnectedAutomationPipelineTrigger>().Any())399{400if (Log.Instance.IsTraceEnabled)401Log.Instance.Trace($"Starting WiFi listener...");402
403await _wifiAutoListener.SubscribeChangedAsync(WiFiAutoListener_Changed).ConfigureAwait(false);404}405
406if (Log.Instance.IsTraceEnabled)407Log.Instance.Trace($"Started relevant listeners.");408}409
410private void RaisePipelinesChanged()411{412PipelinesChanged?.Invoke(this, _pipelines.Select(p => p.DeepCopy()).ToList());413}414
415#endregion416
417}
418