From 31631b879a955e76658537dc91f95183f0aedd48 Mon Sep 17 00:00:00 2001 From: JusterZhu Date: Sat, 23 May 2026 19:13:17 +0800 Subject: [PATCH] feat(Drivelution): add IProgress support for update progress reporting - Add UpdateProgress model with status, step name, percentage, message - Add IProgress? parameter to IGeneralDrivelution.UpdateAsync - Wire progress reporting in BaseDriverUpdater pipeline at step start and completion - Update GeneralDrivelution.QuickUpdateAsync overloads to pass IProgress through - Update MacOS placeholder to match new interface signature - Progress reports step-level info: percentage, step index, total steps, current status Closes #292 --- .../Abstractions/IGeneralDrivelution.cs | 7 ++- .../Abstractions/Models/UpdateProgress.cs | 44 +++++++++++++++++++ .../Core/Pipeline/BaseDriverUpdater.cs | 28 ++++++++++-- .../GeneralDrivelution.cs | 6 ++- .../Implementation/MacOsGeneralDrivelution.cs | 1 + 5 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 src/c#/GeneralUpdate.Drivelution/Abstractions/Models/UpdateProgress.cs diff --git a/src/c#/GeneralUpdate.Drivelution/Abstractions/IGeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/Abstractions/IGeneralDrivelution.cs index f3d95162..ddf41291 100644 --- a/src/c#/GeneralUpdate.Drivelution/Abstractions/IGeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/Abstractions/IGeneralDrivelution.cs @@ -12,9 +12,14 @@ public interface IGeneralDrivelution /// /// Driver information /// Update strategy + /// Optional progress reporter for real-time status updates. /// Cancellation token /// Update result - Task UpdateAsync(DriverInfo driverInfo, UpdateStrategy strategy, CancellationToken cancellationToken = default); + Task UpdateAsync( + DriverInfo driverInfo, + UpdateStrategy strategy, + IProgress? progress = null, + CancellationToken cancellationToken = default); /// /// Validates driver asynchronously diff --git a/src/c#/GeneralUpdate.Drivelution/Abstractions/Models/UpdateProgress.cs b/src/c#/GeneralUpdate.Drivelution/Abstractions/Models/UpdateProgress.cs new file mode 100644 index 00000000..150c33ae --- /dev/null +++ b/src/c#/GeneralUpdate.Drivelution/Abstractions/Models/UpdateProgress.cs @@ -0,0 +1,44 @@ +using GeneralUpdate.Drivelution.Abstractions.Models; + +namespace GeneralUpdate.Drivelution.Abstractions.Models; + +/// +/// Progress information reported during a driver update operation. +/// Suitable for binding to UI progress bars and status displays. +/// +public class UpdateProgress +{ + /// + /// Current update status in the pipeline. + /// + public UpdateStatus CurrentStatus { get; init; } + + /// + /// Name of the currently executing pipeline step. + /// + public string StepName { get; init; } = string.Empty; + + /// + /// Overall completion percentage (0-100). + /// + public int Percentage { get; init; } + + /// + /// Human-readable progress message. + /// + public string Message { get; init; } = string.Empty; + + /// + /// Index of the current step (0-based). + /// + public int StepIndex { get; init; } + + /// + /// Total number of steps in the pipeline. + /// + public int TotalSteps { get; init; } + + /// + public override string ToString() + => $"[{Percentage}%] {StepName} ({StepIndex + 1}/{TotalSteps}): {Message}"; +} diff --git a/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs b/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs index 029ab0fd..8fc6d335 100644 --- a/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs +++ b/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs @@ -49,6 +49,7 @@ protected BaseDriverUpdater( public async Task UpdateAsync( DriverInfo driverInfo, UpdateStrategy strategy, + IProgress? progress = null, CancellationToken cancellationToken = default) { var result = new UpdateResult @@ -70,6 +71,7 @@ public async Task UpdateAsync( using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( cancellationToken, timeoutCts.Token); + int stepCount = 0; try { GeneralTracer.Info($"Starting driver update: {driverInfo.Name} v{driverInfo.Version} " + @@ -78,8 +80,9 @@ public async Task UpdateAsync( var steps = GetPipelineSteps(strategy) .Where(s => s.ShouldExecute(context)) .ToList(); + stepCount = steps.Count; - var totalSteps = steps.Count; + var totalSteps = stepCount; int stepIndex = 0; PipelineResult? lastStepResult = null; @@ -87,7 +90,16 @@ public async Task UpdateAsync( { stepIndex++; OnStepStarted?.Invoke(step.StepName); - ReportProgress((int)((float)(stepIndex - 1) / totalSteps * 100), $"Running: {step.StepName}"); + + progress?.Report(new UpdateProgress + { + CurrentStatus = UpdateStatus.Updating, + StepName = step.StepName, + Percentage = (int)((float)(stepIndex - 1) / totalSteps * 100), + Message = $"Running: {step.StepName}", + StepIndex = stepIndex - 1, + TotalSteps = totalSteps + }); // Execute step with retry on transient failures try @@ -179,7 +191,17 @@ public async Task UpdateAsync( { result.EndTime = DateTime.UtcNow; GeneralTracer.Info($"Driver update finished. Duration={result.DurationMs}ms, Success={result.Success}"); - ReportProgress(100, result.Success ? "Completed" : "Failed"); + + progress?.Report(new UpdateProgress + { + CurrentStatus = result.Status, + StepName = result.Success ? "Completed" : "Failed", + Percentage = 100, + Message = result.Success ? "Driver update completed successfully" : result.Error?.Message ?? "Failed", + StepIndex = stepCount, + TotalSteps = stepCount + }); + OnUpdateCompleted?.Invoke(result); } diff --git a/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs index b7238055..c5d39410 100644 --- a/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs @@ -60,6 +60,7 @@ public static IGeneralDrivelution Create(IServiceProvider serviceProvider) /// Update result public static async Task QuickUpdateAsync( DriverInfo driverInfo, + IProgress? progress = null, CancellationToken cancellationToken = default) { GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync: starting quick driver update. Driver={driverInfo.Name}, Version={driverInfo.Version}"); @@ -71,7 +72,7 @@ public static async Task QuickUpdateAsync( RetryIntervalSeconds = 5 }; - var result = await updater.UpdateAsync(driverInfo, strategy, cancellationToken); + var result = await updater.UpdateAsync(driverInfo, strategy, progress, cancellationToken); GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync: quick driver update completed. Success={result.Success}, Status={result.Status}, DurationMs={result.DurationMs}"); return result; } @@ -86,11 +87,12 @@ public static async Task QuickUpdateAsync( public static async Task QuickUpdateAsync( DriverInfo driverInfo, UpdateStrategy strategy, + IProgress? progress = null, CancellationToken cancellationToken = default) { GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync(strategy): starting driver update with custom strategy. Driver={driverInfo.Name}, Version={driverInfo.Version}, RequireBackup={strategy.RequireBackup}, RetryCount={strategy.RetryCount}"); var updater = Create(); - var result = await updater.UpdateAsync(driverInfo, strategy, cancellationToken); + var result = await updater.UpdateAsync(driverInfo, strategy, progress, cancellationToken); GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync(strategy): driver update completed. Success={result.Success}, Status={result.Status}, DurationMs={result.DurationMs}"); return result; } diff --git a/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs index d23a345d..7b2a22f2 100644 --- a/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs @@ -26,6 +26,7 @@ public class MacOsGeneralDrivelution : IGeneralDrivelution public Task UpdateAsync( DriverInfo driverInfo, UpdateStrategy strategy, + IProgress? progress = null, CancellationToken cancellationToken = default) { throw new PlatformNotSupportedException(