PackageCatalogProgress.cpp (6308B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "PackageCatalogProgress.h" 5 #include "AppInstallerStrings.h" 6 #include "Microsoft/PredefinedInstalledSourceFactory.h" 7 8 using namespace AppInstaller; 9 using namespace AppInstaller::Repository; 10 11 namespace winrt::Microsoft::Management::Deployment 12 { 13 namespace ProgressSinkFactory 14 { 15 std::shared_ptr<AppInstaller::IProgressSink> CreatePackageCatalogProgressSink(std::string sourceType, std::function<void(double)> progressReporter, bool removeOperation) 16 { 17 if (sourceType.empty() 18 || Utility::CaseInsensitiveEquals( Repository::Microsoft::PredefinedInstalledSourceFactory::Type(), sourceType)) 19 { 20 std::vector<std::pair<AppInstaller::ProgressType, double>> progressWeights; 21 22 // There is no download operation for remove operation, so use only percentage based progress to account for uninstall. 23 if (removeOperation) 24 { 25 // it is percentage based progress. 26 progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 1.0)); 27 } 28 else 29 { 30 // Add/Update operation has two progress types: 31 // 1. Bytes for downloading index and 32 // 2. Percent for index installation. 33 progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Bytes, 0.7)); 34 progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 0.3)); 35 } 36 37 return std::make_shared<PreIndexedPackageCatalogProgressSink>(progressWeights, progressReporter); 38 } 39 else 40 { 41 return std::make_shared<CompletionOnlyProgressSink>(progressReporter); 42 } 43 } 44 } 45 46 CompletionOnlyProgressSink::CompletionOnlyProgressSink(std::function<void(double)> progressReporter) : 47 m_progressReporter(progressReporter) 48 { 49 if (!m_progressReporter) 50 { 51 THROW_HR(E_INVALIDARG); 52 } 53 } 54 55 void CompletionOnlyProgressSink::OnProgress(uint64_t /*current*/, uint64_t /*maximum*/, AppInstaller::ProgressType /*type*/) 56 { 57 } 58 59 void CompletionOnlyProgressSink::SetProgressMessage(std::string_view /*message*/) 60 { 61 } 62 63 void CompletionOnlyProgressSink::BeginProgress() 64 { 65 m_progressReporter(0); 66 } 67 68 void CompletionOnlyProgressSink::EndProgress(bool /*hideProgressWhenDone*/) 69 { 70 m_progressReporter(100); 71 } 72 73 PreIndexedPackageCatalogProgressSink::PreIndexedPackageCatalogProgressSink(std::vector<std::pair<AppInstaller::ProgressType, double>> progressWeights, std::function<void(double)> progressReporter) : 74 m_progressWeights(progressWeights), m_progressReporter(progressReporter) 75 { 76 if (!m_progressReporter) 77 { 78 THROW_HR(E_INVALIDARG); 79 } 80 81 // If no weights are provided, default to percent. 82 if (m_progressWeights.empty()) 83 { 84 m_progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 1.0)); 85 } 86 87 // Calculate the total weight. 88 double totalWeight = 0; 89 for (const auto& weight : m_progressWeights) 90 { 91 if (weight.first != AppInstaller::ProgressType::None) 92 { 93 totalWeight += weight.second; 94 } 95 } 96 97 // If the total weight is greater than 1, throw an exception. 98 if (totalWeight != 1.0) 99 { 100 THROW_HR(E_INVALIDARG); 101 } 102 } 103 104 void PreIndexedPackageCatalogProgressSink::OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type) 105 { 106 if (maximum == 0 || type == AppInstaller::ProgressType::None) 107 { 108 return; 109 } 110 111 double progress = static_cast<double>(current) / maximum; 112 m_progressValues[type] = progress; 113 114 double totalProgress = 0.0; 115 double totalWeight = 0.0; 116 117 // Calculate the total progress. 118 for (const auto& [progressType, weight] : m_progressWeights) 119 { 120 double progressValue = m_progressValues[progressType]; 121 122 // [NOTE:] Sequential execution assumption & Handling incomplete progress reports : 123 // This progress calculation assumes that each operation is executed sequentially, meaning the download must be complete before 124 // the installation begins.If the download fails, the installation will not proceed.However, there may be cases where the previous 125 // operation completes successfully, but its onprogress callback does not report 100% completion(e.g., the last progress report for 126 // the download was at 90%, but the download is complete, and the installation has started).This can result in the total progress not 127 // reaching 100% after the last operation completes due to the gap in the previous operation's progress report.To handle this, consider 128 // the progress for the last operation as complete by assigning its full weight while computing progress for the following operation. 129 // For example, while computing progress for the installation, consider the download operation complete even if it did not report progress 130 // exactly at 100%. 131 if (progressValue != 0) 132 { 133 totalProgress = totalWeight; 134 } 135 136 // Adjust the total progress value based on the weight. 137 totalWeight += weight; 138 totalProgress += progressValue * weight; 139 } 140 141 m_progressReporter(totalProgress * 100); 142 } 143 144 void PreIndexedPackageCatalogProgressSink::SetProgressMessage(std::string_view /*message*/) 145 { 146 } 147 148 void PreIndexedPackageCatalogProgressSink::BeginProgress() 149 { 150 m_progressReporter(0); 151 } 152 153 void PreIndexedPackageCatalogProgressSink::EndProgress(bool /*hideProgressWhenDone*/) 154 { 155 m_progressReporter(100); 156 } 157 }