-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRTracker.cs
220 lines (175 loc) · 7.54 KB
/
RTracker.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace RMVC {
internal class RTracker {
internal string Id { get; private set; }
internal bool Abort { get { return _abort; } set { ApplyAbort(value); } }
internal string ErrorMessage { get; private set; } = string.Empty;
internal bool Error { get { return _error; } }
internal CancellationToken Token { get; private set; }
internal bool ErrorOrAbort { get { return _error || _abort; } }
internal RCommandAsync _command;
internal RFacade facade;
private readonly double _cap;
private readonly bool _allowAutoUpdate;
internal RTracker? _parent;
private RTracker? _child;
private double _localPercent;
private string? _title;
private string? _text;
private bool _abort;
private bool _error;
internal RTracker(RCommandAsync command, RFacade facade, double cap, CancellationToken cancellationToken) {
Id = Guid.NewGuid().ToString();
_cap = cap;
_parent = null;
_child = null;
_localPercent = 0d;
_command = command;
_allowAutoUpdate = command.EnableAutoUpdate;
this.facade = facade;
_text = null;
_title = null;
_abort = false;
_error = false;
Token = cancellationToken;
}
internal RProgress[] GetProgressReport() {
List<RProgress> list = new List<RProgress>();
RTracker[] rTrackerSet = GetAllRTrackersFlat();
foreach (RTracker tracker in rTrackerSet) {
if (tracker != this && tracker._localPercent == 0)
continue;
if (!tracker._allowAutoUpdate)
continue;
list.Add(new RProgress(
Convert.ToInt32(Math.Round(tracker._localPercent)), // Final rounding here for report
tracker._text ?? string.Empty,
tracker._title ?? string.Empty,
tracker.Id
));
}
return list.ToArray();
}
internal void SetProgressTitle(string title) {
_title = title;
SendProgress();
}
internal void SetProgress(string text) {
if (!string.IsNullOrWhiteSpace(text))
_text = text;
SendProgress();
}
internal void SetProgress(double percentComplete, string? text = null) {
percentComplete = RHelper.ClampPercent(percentComplete);
if (percentComplete <= _localPercent) return;
if (!string.IsNullOrWhiteSpace(text))
_text = text;
Log($"Setting progress in {Id}: Local percent: {_localPercent}, Increment: {percentComplete - _localPercent}");
UpdatePercent(percentComplete - _localPercent);
}
internal void SetProgress(int percentComplete, string? text = null) {
percentComplete = SanitizePercent(percentComplete);
if (percentComplete <= _localPercent) return;
if (!string.IsNullOrWhiteSpace(text))
_text = text;
UpdatePercent(percentComplete - _localPercent);
}
internal void SetProgress(int step, int total, string? text = null) {
double adjusted = GetPercent(step, total);
if (adjusted <= _localPercent) return;
if (!string.IsNullOrWhiteSpace(text))
_text = text;
UpdatePercent(adjusted - _localPercent);
}
internal void SetError(string errorMessage) {
if (string.IsNullOrWhiteSpace(errorMessage))
errorMessage = "Unspecified Error.";
_error = true;
ErrorMessage = errorMessage;
RTracker[] arr = GetAllRTrackersFlat();
foreach (RTracker item in arr) {
item._error = true;
item.ErrorMessage = ErrorMessage;
}
}
internal RTracker CreateChild(RCommandAsync command, double percentCap) {
percentCap = RHelper.ClampPercent(percentCap); // Ensure cap is within bounds
Log($"Creating child tracker for {command.GetType().Name} with cap: {percentCap}");
_child = new RTracker(command, facade, percentCap * (_cap / 100d), Token);
_child._parent = this;
return _child;
}
private void ApplyAbort(bool abort) {
if (!abort) return;
RTracker[] arr = GetAllRTrackersFlat();
foreach (RTracker item in arr) {
item._abort = abort;
}
}
private RTracker[] GetAllRTrackersFlat() {
List<RTracker> list = new List<RTracker>();
RTracker rootRTracker = GetRootRTracker();
list.Add(rootRTracker);
RTracker? child = rootRTracker._child;
while (child != null) {
list.Add(child);
child = child._child;
}
return list.ToArray();
}
private void SendProgress() {
if (_parent != null) {
_parent.SendProgress();
return;
}
var progressSet = GetProgressReport();
if (progressSet.Length > 0) {
facade.HandleProgressChange(progressSet);
}
}
private void UpdatePercent(double localPercentIncrement) {
// Accumulate local percent without rounding for internal accuracy
_localPercent += localPercentIncrement;
Log($"Updating percent in {Id}: New local percent: {_localPercent}");
if (_parent != null) {
// Calculate parent increment proportionally based on this tracker's cap and apply it immediately
double parentIncrement = localPercentIncrement * (_cap / 100d);
// Propagate to parent with a fractional increment
_parent.UpdatePercent(parentIncrement);
Log($"Propagating to parent in {Id}: Local increment = {localPercentIncrement}, Parent increment = {parentIncrement}");
}
else {
// At the root level, ensure rounding only when displaying to avoid losing small increments
SendProgress();
}
}
private uint GetPercent(int current, int total) {
int percent = (int)((current / (double)total) * 100);
if (percent > 100) percent = 100;
else if (percent < 0) percent = 0;
return (uint)percent;
}
private int SanitizePercent(int percent) {
if (percent > 100) return 100;
else if (percent < 0) return 0;
else return percent;
}
private RTracker GetBaseRTracker() {
if (_child == null) return this;
else return _child.GetBaseRTracker();
}
private RTracker GetRootRTracker() {
if (_parent == null) return this;
else return _parent.GetRootRTracker();
}
private int ToInt(double val) {
return Convert.ToInt32(val);
}
private void Log(string msg) {
RFacade.Log("[RTracker] " + msg);
}
}
}