From 275e6de22832c5d8e66130bb928350285723c8be Mon Sep 17 00:00:00 2001 From: Torben Nehmer Date: Wed, 18 Sep 2024 20:34:30 +0200 Subject: [PATCH] Hook up fswatcher to csv parser, needs proper filename parsing. --- Stars Assistant/App.axaml.cs | 4 ++ Stars Assistant/CSV/PlanetLoader.cs | 20 +++++---- Stars Assistant/Helpers/FsWatcher.cs | 48 +++++++++++---------- Stars Assistant/Helpers/RxExtensions.cs | 20 --------- Stars Assistant/Program.cs | 4 ++ Stars Assistant/Services/CSVDataLoader.cs | 51 +++++++++++++++-------- 6 files changed, 82 insertions(+), 65 deletions(-) diff --git a/Stars Assistant/App.axaml.cs b/Stars Assistant/App.axaml.cs index c7543d8..f023837 100644 --- a/Stars Assistant/App.axaml.cs +++ b/Stars Assistant/App.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using StarsAssistant.ViewModels; using StarsAssistant.Views; +using Splat; namespace StarsAssistant; @@ -23,6 +24,9 @@ public partial class App : Application }; } + var csvloader = Locator.Current.GetService()!; + csvloader.StartPlanetCSVWatcher(); + base.OnFrameworkInitializationCompleted(); } } \ No newline at end of file diff --git a/Stars Assistant/CSV/PlanetLoader.cs b/Stars Assistant/CSV/PlanetLoader.cs index 496d27a..0665347 100644 --- a/Stars Assistant/CSV/PlanetLoader.cs +++ b/Stars Assistant/CSV/PlanetLoader.cs @@ -66,19 +66,25 @@ public class PlanetLoader r = db.Race.Find(owner.Key); if (r == null) { - r = new() - { - Name = owner.Key - }; + r = new() { Name = owner.Key }; db.Add(r); } } foreach (Planet csvp in owner) { - Model.Planet p = new Model.Planet{ Name = csvp.Name }; - csvp.UpdateDbPlanet(p); - db.Add(p); + Model.Planet? p = db.Planet.Find(csvp.Name); + if (p == null) + { + p = new() { Name = csvp.Name }; + csvp.UpdateDbPlanet(p); + db.Add(p); + } + else + { + csvp.UpdateDbPlanet(p); + db.Update(p); + } } db.SaveChanges(); } diff --git a/Stars Assistant/Helpers/FsWatcher.cs b/Stars Assistant/Helpers/FsWatcher.cs index d2587da..552cd48 100644 --- a/Stars Assistant/Helpers/FsWatcher.cs +++ b/Stars Assistant/Helpers/FsWatcher.cs @@ -3,28 +3,8 @@ using System.Reactive.Concurrency; namespace StarsAssistant.Helpers; -public class FsWatcher +public static class FsWatcher { - /// - /// Throttle a FileSystemObserver and eliminate all duplicates. - /// - /// - /// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet - /// - /// An observer created with ObserveFileSystem - /// Throttling window, defaults to 1 second. - /// Scheduler to user for throttling, default is set by Quiescent - /// A throttled observer with duplicate elimination. - public static IObservable> ThrottleAndDistinctObserver ( - IObservable watcher, - int inactivitySeconds = 1, - IScheduler? scheduler = null) - { - return watcher - .Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler) - .Select(changes => changes.DistinctBy(x => (x.ChangeType, x.FullPath))); - } - /// /// Helper to convert a FileSystemWatcher into an obervable stream. See FileSystemWatcher /// documentation for further details on the parameters. @@ -52,6 +32,7 @@ public class FsWatcher if (filters != null) foreach (string filter in filters) fsw.Filters.Add(filter); + if (notifyFilter != null) fsw.NotifyFilter = (NotifyFilters) notifyFilter; fsw.EnableRaisingEvents = true; @@ -90,4 +71,29 @@ public class FsWatcher .RefCount(); } + /// + /// Throttle a FileSystemObserver and eliminate all duplicates. + /// + /// + /// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet + /// + /// An observer created with ObserveFileSystem + /// Throttling window, defaults to 1 second. + /// Scheduler to user for throttling, default is set by Quiescent + /// A throttled observer with duplicate elimination. + public static IObservable ThrottleAndDistinct ( + this IObservable watcher, + int inactivitySeconds = 1, + IScheduler? scheduler = null) + { + return watcher + .Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler) + .SelectMany( + changes => changes + .DistinctBy(x => (x.ChangeType, x.FullPath)) + .ToObservable() + ) + ; + } + } diff --git a/Stars Assistant/Helpers/RxExtensions.cs b/Stars Assistant/Helpers/RxExtensions.cs index e8d4a17..ee001ed 100644 --- a/Stars Assistant/Helpers/RxExtensions.cs +++ b/Stars Assistant/Helpers/RxExtensions.cs @@ -34,24 +34,4 @@ public static class RxExtensions return src.Buffer(zeroCrossings); } - - /// - /// Throttle a FileSystemObserver and eliminate all duplicates. - /// - /// - /// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet - /// - /// An observer created with ObserveFileSystem - /// Throttling window, defaults to 1 second. - /// Scheduler to user for throttling, default is set by Quiescent - /// A throttled observer with duplicate elimination. - public static IObservable> ThrottleAndDistinct ( - this IObservable watcher, - int inactivitySeconds = 1, - IScheduler? scheduler = null) - { - return watcher - .Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler) - .Select(changes => changes.DistinctBy(x => (x.ChangeType, x.FullPath))); - } } diff --git a/Stars Assistant/Program.cs b/Stars Assistant/Program.cs index db574d2..fc460e4 100644 --- a/Stars Assistant/Program.cs +++ b/Stars Assistant/Program.cs @@ -22,6 +22,9 @@ sealed class Program { ModeDetector.OverrideModeDetector(Splat.ModeDetection.Mode.Run); + var logger = new ConsoleLogger() { Level = LogLevel.Debug }; + Locator.CurrentMutable.RegisterConstant(logger, typeof(ILogger)); + Services.Game g = new() { BaseName = "GOINGTH", @@ -31,6 +34,7 @@ sealed class Program Locator.CurrentMutable.RegisterConstant(g, typeof(Services.Game)); Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader)); Locator.CurrentMutable.Register(() => new StarsDatabase(g.DatabaseFileName), typeof(StarsDatabase)); + Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader)); __createTestData(); diff --git a/Stars Assistant/Services/CSVDataLoader.cs b/Stars Assistant/Services/CSVDataLoader.cs index 946d9ea..c2a70b3 100644 --- a/Stars Assistant/Services/CSVDataLoader.cs +++ b/Stars Assistant/Services/CSVDataLoader.cs @@ -1,41 +1,58 @@ using System; using System.IO; using System.Reactive.Linq; +using System.Text.RegularExpressions; +using ReactiveUI; using Splat; using StarsAssistant.Helpers; namespace StarsAssistant.Services; -public class CSVDataLoader +public partial class CSVDataLoader : IEnableLogger { /// /// Reference to the game metadata, retrieved by DI /// protected Services.Game Game = Locator.Current.GetService()!; - public void CSVDataLoader() + [GeneratedRegex(@".*\.(?[pf])(?\d)+$")] + private static partial Regex MyRegex(); + + protected Regex FileTypeRegEx = MyRegex(); + + public CSVDataLoader() { } public void StartPlanetCSVWatcher() { - // TODO: which scheduler for Throttle? - var watcher = FsWatcher - .ObserveFileSystem(Game.GamePath, new string[] { Game.PlanetFileSearchPattern }); - .ThrottleAndDistinct(2); - - /* - watcher.Subscribe(x => - { - Console.WriteLine($"{DateTime.Now.ToLongTimeString()} got {x.Count()} events:"); + // string[] filters = { "*.p*", "*.f*", "*.map" }; - foreach (var fsEvent in x) - { - Console.WriteLine($"{DateTime.Now.ToLongTimeString()} {i++} {fsEvent.FullPath} - {fsEvent.ChangeType}"); - } - }); - */ + var watcher = FsWatcher + .ObserveFileSystem(Game.GamePath, [ Game.PlanetFileSearchPattern ]) + .ThrottleAndDistinct(2, RxApp.TaskpoolScheduler) + .Log(this, $"{DateTime.Now.ToLongTimeString()} FsEvent", fsEvent => $"{fsEvent.FullPath} {fsEvent.ChangeType}") + .ObserveOn(RxApp.TaskpoolScheduler); + + watcher.Subscribe(fsEvent => this.LoadPlanetFile(fsEvent.FullPath)); + } + + protected void LoadPlanetFile(string fileName) + { + Match m = FileTypeRegEx.Match(fileName); + if (! m.Success) + { + this.Log().Error($"Failed to parse {fileName} to identify what we are looking at. Ignoring file."); + return; + } + + string type = m.Groups["type"].Value; + string player = m.Groups["player"].Value; + + this.Log().Debug($"Got file type {type} for player {player}"); + var loader = new CSV.PlanetLoader(); + loader.ImportForRace(Model.Race.Player); } }