private async Task _BackgroundRunner(CancellationToken ct) { _Accumulator.InfosChanged += (sender, infos) => { var ordered = infos.OrderByDescending(x => x.TotalDamage); var items = ordered.Select(i => new DamageDisplayList.DamageDisplayData { Name = i.Name, Damage = i.TotalDamage, DamageRatio = (100.0 / ordered.First().TotalDamage) * i.TotalDamage, DamageRatioNormal = (100.0 / ordered.First().TotalDamage) * i.TotalDamage, MaxHitDamage = i.MaxHitDamage, MaxHitName = $"Id: {i.MaxHitAction}" }); Dispatcher.Invoke(() => { _DamageDisplayList.Items.Clear(); foreach (var i in items) { _DamageDisplayList.Items.Add(i); } _DamageDispalyListState = DamageDispalyListState.Visible; }); }; // TODO: this should probably be done in a better way. // at the moment if anything goes wrong the system will // reset the accumulator and start again, ~better than a crash?~ while (true) { ct.ThrowIfCancellationRequested(); // wait for file to show up var logFile = _GetLatestLogFile(_DamageDumpFolder); if (logFile == null) { await Task.Delay(1000, ct); continue; } try { Dispatcher.Invoke(() => _DamageDispalyListState = DamageDispalyListState.Loading); var watcher = new LogFileWatcher(); watcher.OnNewEntries += async(sender, entries) => { await _BackgroundSemaphore.WaitAsync(); try { _Accumulator.ProcessEntries(entries); } finally { _BackgroundSemaphore.Release(); } }; using (var fsWatcher = new FileSystemWatcher()) { fsWatcher.Path = _DamageDumpFolder; var fsWatcherSource = new CancellationTokenSource(); void OnFsChanged(object source, object _) { if (_GetLatestLogFile(_DamageDumpFolder) != logFile) { fsWatcherSource.Cancel(); } } fsWatcher.Created += OnFsChanged; fsWatcher.Deleted += OnFsChanged; fsWatcher.Changed += OnFsChanged; fsWatcher.Renamed += OnFsChanged; fsWatcher.EnableRaisingEvents = true; await watcher.RunAsync(logFile, CancellationTokenSource.CreateLinkedTokenSource(ct, fsWatcherSource.Token).Token); } } catch (OperationCanceledException) { // this exception does not mean anything as there are multiple // cancellation tokens that could have caused it, the function however // only cancels to one specific token and that is handled separately } catch (Exception ex) { Debug.WriteLine($"Exception in log file watcher loop -> {ex.Message}"); } finally { await _ResetAccumulator(); Dispatcher.Invoke(() => _DamageDispalyListState = DamageDispalyListState.Waiting); } } }
static void Main(string[] args) { Outputer("Starting watcher process.."); // Read configuration var configPath = args[0]; Outputer($"Loading config path from {configPath}"); var configText = File.ReadAllText(configPath); var config = JsonConvert.DeserializeObject <Configuration>(configText); // TODO: Set up logs if (config.Etw.Any()) { Outputer($"Configuring ETW providers..."); var etwWatcher = new EtwWatcher(Outputer); foreach (var etwConfiguration in config.Etw) { Outputer($"Adding Provider: {etwConfiguration.ProviderName}"); etwWatcher.Watch(etwConfiguration.ProviderName); } Task.Run(() => etwWatcher.StartListening()); } if (config.EventLogs.Any()) { Outputer($"Configuring EventLogs..."); foreach (var eventLog in config.EventLogs) { Outputer($"Adding EventLog: {eventLog.LogName}"); Task.Run(() => { var eventLogWatcher = new EventLogWatcher(Outputer, eventLog.Source); eventLogWatcher.Watch(eventLog.LogName); }); } } if (config.LogFiles.Any()) { Outputer("Configuring Logs..."); foreach (var logFile in config.LogFiles) { Outputer($"Adding Logs: {logFile.Location}"); Task.Run(() => { var logFileWatcher = new LogFileWatcher(Outputer); logFileWatcher.Watch(logFile.Location); }); } } // Watch services Outputer($"Starting services"); IServiceWatcher serviceWatcher = new PollingServiceWatcher(); serviceWatcher.StartServices(config.Services, Crash); Thread.Sleep(Timeout.Infinite); }