/Source/Deployer.NetFx/ImageFlasher.cs

https://github.com/WOA-Project/WOA-Deployer · C# · 106 lines · 91 code · 15 blank · 0 comment · 10 complexity · 2203bfd1686c1fcc01766de3fc0dc8c2 MD5 · raw file

  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Reactive.Linq;
  5. using System.Reactive.Subjects;
  6. using System.Text.RegularExpressions;
  7. using System.Threading.Tasks;
  8. using Deployer.Core;
  9. using Deployer.Core.FileSystem;
  10. using Deployer.Core.Services;
  11. using Deployer.Core.Utils;
  12. using Serilog;
  13. using Zafiro.Core;
  14. namespace Deployer.NetFx
  15. {
  16. public class ImageFlasher : IImageFlasher
  17. {
  18. private readonly Regex percentRegex = new Regex(@"(\d*.\d*)%");
  19. public string EtcherPath
  20. {
  21. get
  22. {
  23. var platformSuffix = Environment.Is64BitProcess ? "x64" : "x86";
  24. var etcherPath = Path.Combine("Core", "Tools", platformSuffix, "Etcher-Cli", "Etcher");
  25. return etcherPath;
  26. }
  27. }
  28. public async Task Flash(IDisk disk, string imagePath, IOperationProgress progressObserver = null)
  29. {
  30. Log.Information("Flashing GPT image...");
  31. ISubject<string> outputSubject = new Subject<string>();
  32. IDisposable stdOutputSubscription = null;
  33. bool isValidating = false;
  34. if (progressObserver != null)
  35. {
  36. stdOutputSubscription = outputSubject
  37. .Do(s =>
  38. {
  39. if (!isValidating && CultureInfo.CurrentCulture.CompareInfo.IndexOf(s, "validating", 0, CompareOptions.IgnoreCase) != -1)
  40. {
  41. progressObserver?.Percentage.OnNext(double.NaN);
  42. Log.Information("Validating flashed image...");
  43. isValidating = true;
  44. }
  45. })
  46. .Select(GetPercentage)
  47. .Where(d => !double.IsNaN(d))
  48. .Subscribe(progressObserver.Percentage);
  49. }
  50. var args = $@"-d \\.\PHYSICALDRIVE{disk.Number} ""{imagePath}"" --yes --no-unmount";
  51. Log.Verbose("We are about to run Etcher: {ExecName} {Parameters}", EtcherPath, args);
  52. var processResults = await ProcessMixin.RunProcess(EtcherPath, args, outputObserver: outputSubject);
  53. if (processResults.ExitCode != 0)
  54. {
  55. Log.Error("Cannot flash the image with Etcher. Execution results: {Results}", processResults);
  56. throw new FlashException($"Cannot flash the image: {imagePath} to {disk}");
  57. }
  58. progressObserver?.Percentage.OnNext(double.NaN);
  59. stdOutputSubscription?.Dispose();
  60. await disk.Refresh();
  61. await EnsureDiskHasNewGuid(disk);
  62. Log.Information("GPT image flashed");
  63. }
  64. private static async Task EnsureDiskHasNewGuid(IDisk disk)
  65. {
  66. await disk.SetGuid(Guid.NewGuid());
  67. }
  68. private double GetPercentage(string output)
  69. {
  70. if (output == null)
  71. {
  72. return double.NaN;
  73. }
  74. var matches = percentRegex.Match(output);
  75. if (matches.Success)
  76. {
  77. var value = matches.Groups[1].Value;
  78. try
  79. {
  80. var percentage = double.Parse(value, CultureInfo.InvariantCulture) / 100D;
  81. return percentage;
  82. }
  83. catch (FormatException)
  84. {
  85. Log.Warning($"Cannot convert {value} to double");
  86. }
  87. }
  88. return double.NaN;
  89. }
  90. }
  91. }