Hook up fswatcher to csv parser, needs proper filename parsing.

This commit is contained in:
Torben Nehmer 2024-09-18 20:34:30 +02:00
parent 802180a26e
commit 275e6de228
No known key found for this signature in database
6 changed files with 82 additions and 65 deletions

View File

@ -3,6 +3,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using StarsAssistant.ViewModels; using StarsAssistant.ViewModels;
using StarsAssistant.Views; using StarsAssistant.Views;
using Splat;
namespace StarsAssistant; namespace StarsAssistant;
@ -23,6 +24,9 @@ public partial class App : Application
}; };
} }
var csvloader = Locator.Current.GetService<Services.CSVDataLoader>()!;
csvloader.StartPlanetCSVWatcher();
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
} }

View File

@ -66,19 +66,25 @@ public class PlanetLoader
r = db.Race.Find(owner.Key); r = db.Race.Find(owner.Key);
if (r == null) if (r == null)
{ {
r = new() r = new() { Name = owner.Key };
{
Name = owner.Key
};
db.Add(r); db.Add(r);
} }
} }
foreach (Planet csvp in owner) foreach (Planet csvp in owner)
{ {
Model.Planet p = new Model.Planet{ Name = csvp.Name }; Model.Planet? p = db.Planet.Find(csvp.Name);
csvp.UpdateDbPlanet(p); if (p == null)
db.Add(p); {
p = new() { Name = csvp.Name };
csvp.UpdateDbPlanet(p);
db.Add(p);
}
else
{
csvp.UpdateDbPlanet(p);
db.Update(p);
}
} }
db.SaveChanges(); db.SaveChanges();
} }

View File

@ -3,28 +3,8 @@ using System.Reactive.Concurrency;
namespace StarsAssistant.Helpers; namespace StarsAssistant.Helpers;
public class FsWatcher public static class FsWatcher
{ {
/// <summary>
/// Throttle a FileSystemObserver and eliminate all duplicates.
/// </summary>
/// <remarks>
/// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet
/// </remarks>
/// <param name="watcher">An observer created with <c>ObserveFileSystem</c></param>
/// <param name="inactivitySeconds">Throttling window, defaults to 1 second.</param>
/// <param name="scheduler">Scheduler to user for throttling, default is set by <c>Quiescent</c></param>
/// <returns>A throttled observer with duplicate elimination.</returns>
public static IObservable<IEnumerable<FileSystemEventArgs>> ThrottleAndDistinctObserver (
IObservable<FileSystemEventArgs> watcher,
int inactivitySeconds = 1,
IScheduler? scheduler = null)
{
return watcher
.Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler)
.Select(changes => changes.DistinctBy(x => (x.ChangeType, x.FullPath)));
}
/// <summary> /// <summary>
/// Helper to convert a FileSystemWatcher into an obervable stream. See FileSystemWatcher /// Helper to convert a FileSystemWatcher into an obervable stream. See FileSystemWatcher
/// documentation for further details on the parameters. /// documentation for further details on the parameters.
@ -52,6 +32,7 @@ public class FsWatcher
if (filters != null) if (filters != null)
foreach (string filter in filters) foreach (string filter in filters)
fsw.Filters.Add(filter); fsw.Filters.Add(filter);
if (notifyFilter != null) if (notifyFilter != null)
fsw.NotifyFilter = (NotifyFilters) notifyFilter; fsw.NotifyFilter = (NotifyFilters) notifyFilter;
fsw.EnableRaisingEvents = true; fsw.EnableRaisingEvents = true;
@ -90,4 +71,29 @@ public class FsWatcher
.RefCount(); .RefCount();
} }
/// <summary>
/// Throttle a FileSystemObserver and eliminate all duplicates.
/// </summary>
/// <remarks>
/// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet
/// </remarks>
/// <param name="watcher">An observer created with <c>ObserveFileSystem</c></param>
/// <param name="inactivitySeconds">Throttling window, defaults to 1 second.</param>
/// <param name="scheduler">Scheduler to user for throttling, default is set by <c>Quiescent</c></param>
/// <returns>A throttled observer with duplicate elimination.</returns>
public static IObservable<FileSystemEventArgs> ThrottleAndDistinct (
this IObservable<FileSystemEventArgs> watcher,
int inactivitySeconds = 1,
IScheduler? scheduler = null)
{
return watcher
.Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler)
.SelectMany(
changes => changes
.DistinctBy(x => (x.ChangeType, x.FullPath))
.ToObservable()
)
;
}
} }

View File

@ -34,24 +34,4 @@ public static class RxExtensions
return src.Buffer(zeroCrossings); return src.Buffer(zeroCrossings);
} }
/// <summary>
/// Throttle a FileSystemObserver and eliminate all duplicates.
/// </summary>
/// <remarks>
/// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet
/// </remarks>
/// <param name="watcher">An observer created with <c>ObserveFileSystem</c></param>
/// <param name="inactivitySeconds">Throttling window, defaults to 1 second.</param>
/// <param name="scheduler">Scheduler to user for throttling, default is set by <c>Quiescent</c></param>
/// <returns>A throttled observer with duplicate elimination.</returns>
public static IObservable<IEnumerable<FileSystemEventArgs>> ThrottleAndDistinct (
this IObservable<FileSystemEventArgs> watcher,
int inactivitySeconds = 1,
IScheduler? scheduler = null)
{
return watcher
.Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler)
.Select(changes => changes.DistinctBy(x => (x.ChangeType, x.FullPath)));
}
} }

View File

@ -22,6 +22,9 @@ sealed class Program
{ {
ModeDetector.OverrideModeDetector(Splat.ModeDetection.Mode.Run); ModeDetector.OverrideModeDetector(Splat.ModeDetection.Mode.Run);
var logger = new ConsoleLogger() { Level = LogLevel.Debug };
Locator.CurrentMutable.RegisterConstant(logger, typeof(ILogger));
Services.Game g = new() Services.Game g = new()
{ {
BaseName = "GOINGTH", BaseName = "GOINGTH",
@ -31,6 +34,7 @@ sealed class Program
Locator.CurrentMutable.RegisterConstant(g, typeof(Services.Game)); Locator.CurrentMutable.RegisterConstant(g, typeof(Services.Game));
Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader)); Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader));
Locator.CurrentMutable.Register(() => new StarsDatabase(g.DatabaseFileName), typeof(StarsDatabase)); Locator.CurrentMutable.Register(() => new StarsDatabase(g.DatabaseFileName), typeof(StarsDatabase));
Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader));
__createTestData(); __createTestData();

View File

@ -1,41 +1,58 @@
using System; using System;
using System.IO; using System.IO;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Text.RegularExpressions;
using ReactiveUI;
using Splat; using Splat;
using StarsAssistant.Helpers; using StarsAssistant.Helpers;
namespace StarsAssistant.Services; namespace StarsAssistant.Services;
public class CSVDataLoader public partial class CSVDataLoader : IEnableLogger
{ {
/// <summary> /// <summary>
/// Reference to the game metadata, retrieved by DI /// Reference to the game metadata, retrieved by DI
/// </summary> /// </summary>
protected Services.Game Game = Locator.Current.GetService<Services.Game>()!; protected Services.Game Game = Locator.Current.GetService<Services.Game>()!;
public void CSVDataLoader() [GeneratedRegex(@".*\.(?<type>[pf])(?<player>\d)+$")]
private static partial Regex MyRegex();
protected Regex FileTypeRegEx = MyRegex();
public CSVDataLoader()
{ {
} }
public void StartPlanetCSVWatcher() public void StartPlanetCSVWatcher()
{ {
// TODO: which scheduler for Throttle? // string[] filters = { "*.p*", "*.f*", "*.map" };
var watcher = FsWatcher var watcher = FsWatcher
.ObserveFileSystem(Game.GamePath, new string[] { Game.PlanetFileSearchPattern }); .ObserveFileSystem(Game.GamePath, [ Game.PlanetFileSearchPattern ])
.ThrottleAndDistinct(2); .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));
watcher.Subscribe(x => }
protected void LoadPlanetFile(string fileName)
{
Match m = FileTypeRegEx.Match(fileName);
if (! m.Success)
{ {
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} got {x.Count()} events:"); this.Log().Error($"Failed to parse {fileName} to identify what we are looking at. Ignoring file.");
return;
}
foreach (var fsEvent in x) string type = m.Groups["type"].Value;
{ string player = m.Groups["player"].Value;
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} {i++} {fsEvent.FullPath} - {fsEvent.ChangeType}");
} this.Log().Debug($"Got file type {type} for player {player}");
}); var loader = new CSV.PlanetLoader();
*/ loader.ImportForRace(Model.Race.Player);
} }
} }