[Blazor] Move importing DAT to single thread.

Does not take so much longer, but UX is much improved.
This commit is contained in:
2025-07-27 12:16:39 +01:00
parent e1ced26bc5
commit a290996037
2 changed files with 130 additions and 48 deletions

View File

@@ -3,13 +3,22 @@
<FluentDialog Width="800px" Height="400px" Title="Import DATs" Modal="true" TrapFocus="true"> <FluentDialog Width="800px" Height="400px" Title="Import DATs" Modal="true" TrapFocus="true">
<FluentDialogBody> <FluentDialogBody>
<div>
<p hidden="@IsBusy">DAT files will be imported from @path.</p> <p hidden="@IsBusy">DAT files will be imported from @path.</p>
<FluentLabel Color="@StatusColor">@StatusMessage</FluentLabel> <FluentLabel Color="@StatusColor">@StatusMessage</FluentLabel>
<FluentProgress Max="@ProgressMax" Min="@ProgressMin" Value="@ProgressValue" Visible="@ProgressVisible"/> <FluentProgress Max="@ProgressMax" Min="@ProgressMin" Value="@ProgressValue" Visible="@ProgressVisible"/>
</div>
@if(Progress2Visible)
{
<div>
<p>@StatusMessage2</p>
<FluentProgress Max="@Progress2Max" Min="@Progress2Min" Value="@Progress2Value"/>
</div>
}
</FluentDialogBody> </FluentDialogBody>
<FluentDialogFooter> <FluentDialogFooter>
<FluentStack Orientation="Orientation.Horizontal"> <FluentStack Orientation="Orientation.Horizontal">
<FluentButton OnClick="@StartAsync" Disabled="@IsBusy">Start</FluentButton> <FluentButton OnClick="@Start" Disabled="@IsBusy">Start</FluentButton>
<FluentButton OnClick="@CloseAsync" Disabled="@CannotClose">Close</FluentButton> <FluentButton OnClick="@CloseAsync" Disabled="@CannotClose">Close</FluentButton>
</FluentStack> </FluentStack>
</FluentDialogFooter> </FluentDialogFooter>

View File

@@ -1,9 +1,11 @@
using System.Diagnostics; using System.Diagnostics;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components; using Microsoft.FluentUI.AspNetCore.Components;
using RomRepoMgr.Core.EventArgs;
using RomRepoMgr.Core.Workers; using RomRepoMgr.Core.Workers;
using Serilog; using Serilog;
using Serilog.Extensions.Logging; using Serilog.Extensions.Logging;
using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs;
namespace RomRepoMgr.Blazor.Components.Dialogs; namespace RomRepoMgr.Blazor.Components.Dialogs;
@@ -23,6 +25,11 @@ public partial class ImportDats : ComponentBase
public int? ProgressValue { get; set; } public int? ProgressValue { get; set; }
public bool CannotClose { get; set; } public bool CannotClose { get; set; }
public bool ProgressVisible { get; set; } public bool ProgressVisible { get; set; }
public string StatusMessage2 { get; set; }
public bool Progress2Visible { get; set; }
public int? Progress2Max { get; set; }
public int? Progress2Min { get; set; }
public int? Progress2Value { get; set; }
public Color? StatusColor { get; set; } public Color? StatusColor { get; set; }
/// <inheritdoc /> /// <inheritdoc />
@@ -31,13 +38,15 @@ public partial class ImportDats : ComponentBase
base.OnInitialized(); base.OnInitialized();
path = Path.Combine(Environment.CurrentDirectory, Consts.IncomingDatFolder); path = Path.Combine(Environment.CurrentDirectory, Consts.IncomingDatFolder);
StatusMessage = string.Empty; StatusMessage = "";
StatusMessage2 = "";
IsBusy = false; IsBusy = false;
CannotClose = false; CannotClose = false;
ProgressVisible = false; ProgressVisible = false;
Progress2Visible = false;
} }
Task StartAsync() void Start()
{ {
IsBusy = true; IsBusy = true;
CannotClose = true; CannotClose = true;
@@ -62,46 +71,110 @@ public partial class ImportDats : ComponentBase
ProgressMin = 0; ProgressMin = 0;
ProgressMax = _datFiles.Length; ProgressMax = _datFiles.Length;
ProgressValue = 0; ProgressValue = 0;
Progress2Visible = true;
_listPosition = 0; _listPosition = 0;
_workers = 0; _workers = 0;
StateHasChanged(); StateHasChanged();
return ImportAsync();
}
async Task ImportAsync()
{
_stopwatch.Restart(); _stopwatch.Restart();
Logger.LogDebug("Starting to import DAT files..."); Logger.LogDebug("Starting to import DAT files...");
Import();
}
Parallel.ForEach(_datFiles, void Import()
datFile => {
if(_listPosition >= _datFiles.Length)
{ {
_ = InvokeAsync(() => _ = InvokeAsync(() =>
{ {
StatusMessage = string.Format("Importing {0}...", Path.GetFileName(datFile)); ProgressVisible = false;
Progress2Visible = false;
StatusMessage = "Finished";
CannotClose = false;
ProgressValue = _listPosition;
StateHasChanged(); StateHasChanged();
}); });
var worker = new DatImporter(datFile, null, new SerilogLoggerFactory(Log.Logger));
worker.Import();
Interlocked.Increment(ref _listPosition);
});
ProgressVisible = false;
StatusMessage = "Finished";
CannotClose = false;
_stopwatch.Stop(); _stopwatch.Stop();
Logger.LogDebug("Took {TotalSeconds} seconds to import {Length} DAT files", Logger.LogDebug("Took {TotalSeconds} seconds to import {Length} DAT files",
_stopwatch.Elapsed.TotalSeconds, _stopwatch.Elapsed.TotalSeconds,
_datFiles.Length); _datFiles.Length);
return;
}
_ = InvokeAsync(() =>
{
StatusMessage = string.Format("Importing {0}...", Path.GetFileName(_datFiles[_listPosition]));
ProgressValue = _listPosition;
StateHasChanged(); StateHasChanged();
});
var worker = new DatImporter(_datFiles[_listPosition], null, new SerilogLoggerFactory(Log.Logger));
worker.ErrorOccurred += OnWorkerOnErrorOccurred;
worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress;
worker.SetMessage += OnWorkerOnSetMessage;
worker.SetProgress += OnWorkerOnSetProgress;
worker.SetProgressBounds += OnWorkerOnSetProgressBounds;
worker.WorkFinished += OnWorkerOnWorkFinished;
_ = Task.Run(worker.Import);
}
void OnWorkerOnWorkFinished(object? sender, MessageEventArgs args)
{
_listPosition++;
Import();
}
void OnWorkerOnSetProgressBounds(object? sender, ProgressBoundsEventArgs args)
{
_ = InvokeAsync(() =>
{
Progress2Value = 0;
Progress2Max = (int?)args.Maximum;
Progress2Min = (int?)args.Minimum;
StateHasChanged();
});
}
void OnWorkerOnSetProgress(object? sender, ProgressEventArgs args)
{
_ = InvokeAsync(() =>
{
Progress2Value = (int?)args.Value;
StateHasChanged();
});
}
void OnWorkerOnSetMessage(object? sender, MessageEventArgs args)
{
_ = InvokeAsync(() =>
{
StatusMessage2 = args.Message;
StateHasChanged();
});
}
void OnWorkerOnSetIndeterminateProgress(object? sender, EventArgs args)
{
_ = InvokeAsync(() =>
{
Progress2Value = null;
StateHasChanged();
});
}
void OnWorkerOnErrorOccurred(object? sender, ErrorEventArgs args)
{
_ = InvokeAsync(() =>
{
_listPosition++;
Import();
});
} }
Task CloseAsync() => Dialog.CloseAsync(); Task CloseAsync() => Dialog.CloseAsync();