aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs373
1 files changed, 161 insertions, 212 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
index 4616203..0f71222 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
@@ -43,25 +43,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
43 { 43 {
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 45
46 public string Identifier { get; private set; } 46 private static Int32 m_counter = 0;
47
48 public int DebugLevel { get; set; }
49 47
50 /// <summary> 48// private Int32 m_identifier;
51 /// Number of ticks (ms) per quantum, drip rate and max burst 49
52 /// are defined over this interval. 50 protected const float m_timeScale = 1e-3f;
53 /// </summary>
54 protected const Int32 m_ticksPerQuantum = 1000;
55 51
56 /// <summary> 52 /// <summary>
57 /// This is the number of quantums worth of packets that can 53 /// This is the number of m_minimumDripRate bytes
58 /// be accommodated during a burst 54 /// allowed in a burst
55 /// roughtly, with this settings, the maximum time system will take
56 /// to recheck a bucket in ms
57 ///
59 /// </summary> 58 /// </summary>
60 protected const Double m_quantumsPerBurst = 1.5; 59 protected const float m_quantumsPerBurst = 5;
61 60
62 /// <summary> 61 /// <summary>
63 /// </summary> 62 /// </summary>
64 protected const Int32 m_minimumDripRate = LLUDPServer.MTU; 63 protected const float m_minimumDripRate = 1500;
65 64
66 /// <summary>Time of the last drip, in system ticks</summary> 65 /// <summary>Time of the last drip, in system ticks</summary>
67 protected Int32 m_lastDrip; 66 protected Int32 m_lastDrip;
@@ -70,40 +69,57 @@ namespace OpenSim.Region.ClientStack.LindenUDP
70 /// The number of bytes that can be sent at this moment. This is the 69 /// The number of bytes that can be sent at this moment. This is the
71 /// current number of tokens in the bucket 70 /// current number of tokens in the bucket
72 /// </summary> 71 /// </summary>
73 protected Int64 m_tokenCount; 72 protected float m_tokenCount;
74 73
75 /// <summary> 74 /// <summary>
76 /// Map of children buckets and their requested maximum burst rate 75 /// Map of children buckets and their requested maximum burst rate
77 /// </summary> 76 /// </summary>
78 protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); 77
78 protected Dictionary<TokenBucket, float> m_children = new Dictionary<TokenBucket, float>();
79
80#region Properties
79 81
80 /// <summary> 82 /// <summary>
81 /// The parent bucket of this bucket, or null if this bucket has no 83 /// The parent bucket of this bucket, or null if this bucket has no
82 /// parent. The parent bucket will limit the aggregate bandwidth of all 84 /// parent. The parent bucket will limit the aggregate bandwidth of all
83 /// of its children buckets 85 /// of its children buckets
84 /// </summary> 86 /// </summary>
85 public TokenBucket Parent { get; protected set; } 87 protected TokenBucket m_parent;
86 88 public TokenBucket Parent
89 {
90 get { return m_parent; }
91 set { m_parent = value; }
92 }
87 /// <summary> 93 /// <summary>
88 /// Maximum burst rate in bytes per second. This is the maximum number 94 /// This is the maximum number
89 /// of tokens that can accumulate in the bucket at any one time. This 95 /// of tokens that can accumulate in the bucket at any one time. This
90 /// also sets the total request for leaf nodes 96 /// also sets the total request for leaf nodes
91 /// </summary> 97 /// </summary>
92 protected Int64 m_burstRate; 98 protected float m_burst;
93 public Int64 RequestedBurstRate 99//not in use
100 public float MaxDripRate { get; set; }
101
102 public float RequestedBurst
94 { 103 {
95 get { return m_burstRate; } 104 get { return m_burst; }
96 set { m_burstRate = (value < 0 ? 0 : value); } 105 set {
106 float rate = (value < 0 ? 0 : value);
107 if (rate < 1.5f * m_minimumDripRate)
108 rate = 1.5f * m_minimumDripRate;
109 else if (rate > m_minimumDripRate * m_quantumsPerBurst)
110 rate = m_minimumDripRate * m_quantumsPerBurst;
111
112 m_burst = rate;
113 }
97 } 114 }
98 115
99 public Int64 BurstRate 116 public float Burst
100 { 117 {
101 get { 118 get {
102 double rate = RequestedBurstRate * BurstRateModifier(); 119 float rate = RequestedBurst * BurstModifier();
103 if (rate < m_minimumDripRate * m_quantumsPerBurst) 120 if (rate < m_minimumDripRate)
104 rate = m_minimumDripRate * m_quantumsPerBurst; 121 rate = m_minimumDripRate;
105 122 return (float)rate;
106 return (Int64) rate;
107 } 123 }
108 } 124 }
109 125
@@ -115,78 +131,50 @@ namespace OpenSim.Region.ClientStack.LindenUDP
115 /// Can never be above MaxDripRate. 131 /// Can never be above MaxDripRate.
116 /// Tokens are added to the bucket at any time 132 /// Tokens are added to the bucket at any time
117 /// <seealso cref="RemoveTokens"/> is called, at the granularity of 133 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
118 /// the system tick interval (typically around 15-22ms) 134 /// the system tick interval (typically around 15-22ms)</remarks>
119 /// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive 135 protected float m_dripRate;
120 /// number on get if TotalDripRequest is set. This also stops us being able to retrieve the fact that
121 /// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get
122 /// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties.
123 /// </remarks>
124 public virtual Int64 RequestedDripRate
125 {
126 get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
127 set
128 {
129 if (value <= 0)
130 m_dripRate = 0;
131 else if (MaxDripRate > 0 && value > MaxDripRate)
132 m_dripRate = MaxDripRate;
133 else
134 m_dripRate = value;
135 136
136 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); 137 public virtual float RequestedDripRate
138 {
139 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
140 set {
141 m_dripRate = (value < 0 ? 0 : value);
142 m_totalDripRequest = m_dripRate;
137 143
138 if (Parent != null) 144 if (m_parent != null)
139 Parent.RegisterRequest(this, m_dripRate); 145 m_parent.RegisterRequest(this,m_dripRate);
140 } 146 }
141 } 147 }
142 148
143 /// <summary> 149 public virtual float DripRate
144 /// Gets the drip rate.
145 /// </summary>
146 /// <value>
147 /// DripRate can never be above max drip rate or below min drip rate.
148 /// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the
149 /// parent bucket.
150 /// </value>
151 public virtual Int64 DripRate
152 { 150 {
153 get 151 get {
154 { 152 float rate = Math.Min(RequestedDripRate,TotalDripRequest);
155 double rate; 153 if (m_parent == null)
156 154 return rate;
157 // FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set
158 // on ourselves which is not equal to the child drip rates.
159 if (Parent == null)
160 {
161 if (TotalDripRequest > 0)
162 rate = Math.Min(RequestedDripRate, TotalDripRequest);
163 else
164 rate = RequestedDripRate;
165 }
166 else
167 {
168 rate = (double)RequestedDripRate * Parent.DripRateModifier();
169 }
170 155
156 rate *= m_parent.DripRateModifier();
171 if (rate < m_minimumDripRate) 157 if (rate < m_minimumDripRate)
172 rate = m_minimumDripRate; 158 rate = m_minimumDripRate;
173 else if (MaxDripRate > 0 && rate > MaxDripRate)
174 rate = MaxDripRate;
175 159
176 return (Int64)rate; 160 return (float)rate;
177 } 161 }
178 } 162 }
179 protected Int64 m_dripRate;
180
181 // <summary>
182 // The maximum rate for flow control. Drip rate can never be greater than this.
183 // </summary>
184 public Int64 MaxDripRate { get; set; }
185 163
186 /// <summary> 164 /// <summary>
187 /// The current total of the requested maximum burst rates of children buckets. 165 /// The current total of the requested maximum burst rates of children buckets.
188 /// </summary> 166 /// </summary>
189 public Int64 TotalDripRequest { get; protected set; } 167 protected float m_totalDripRequest;
168 public float TotalDripRequest
169 {
170 get { return m_totalDripRequest; }
171 set { m_totalDripRequest = value; }
172 }
173
174#endregion Properties
175
176#region Constructor
177
190 178
191 /// <summary> 179 /// <summary>
192 /// Default constructor 180 /// Default constructor
@@ -194,20 +182,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
194 /// <param name="identifier">Identifier for this token bucket</param> 182 /// <param name="identifier">Identifier for this token bucket</param>
195 /// <param name="parent">Parent bucket if this is a child bucket, or 183 /// <param name="parent">Parent bucket if this is a child bucket, or
196 /// null if this is a root bucket</param> 184 /// null if this is a root bucket</param>
197 /// <param name="requestedDripRate"> 185 /// <param name="maxBurst">Maximum size of the bucket in bytes, or
198 /// Requested rate that the bucket fills, in bytes per 186 /// zero if this bucket has no maximum capacity</param>
199 /// second. If zero, the bucket always remains full. 187 /// <param name="dripRate">Rate that the bucket fills, in bytes per
200 /// </param> 188 /// second. If zero, the bucket always remains full</param>
201 public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate) 189 public TokenBucket(TokenBucket parent, float dripRate, float MaxBurst)
202 { 190 {
203 Identifier = identifier; 191 m_counter++;
204 192
205 Parent = parent; 193 Parent = parent;
206 RequestedDripRate = requestedDripRate; 194 RequestedDripRate = dripRate;
207 MaxDripRate = maxDripRate; 195 RequestedBurst = MaxBurst;
208 m_lastDrip = Util.EnvironmentTickCount(); 196 // TotalDripRequest = dripRate; // this will be overwritten when a child node registers
197 // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
198 m_lastDrip = Util.EnvironmentTickCount() + 100000;
209 } 199 }
210 200
201#endregion Constructor
202
211 /// <summary> 203 /// <summary>
212 /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning 204 /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
213 /// no modification if the requested bandwidth is less than the 205 /// no modification if the requested bandwidth is less than the
@@ -215,22 +207,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
215 /// hierarchy. However, if any of the parents is over-booked, then 207 /// hierarchy. However, if any of the parents is over-booked, then
216 /// the modifier will be less than 1. 208 /// the modifier will be less than 1.
217 /// </summary> 209 /// </summary>
218 protected double DripRateModifier() 210 protected float DripRateModifier()
219 { 211 {
220 Int64 driprate = DripRate; 212 float driprate = DripRate;
221 double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; 213 return driprate >= TotalDripRequest ? 1.0f : driprate / TotalDripRequest;
222
223// if (DebugLevel > 0)
224// m_log.DebugFormat(
225// "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}",
226// driprate, TotalDripRequest, modifier, Identifier);
227
228 return modifier;
229 } 214 }
230 215
231 /// <summary> 216 /// <summary>
232 /// </summary> 217 /// </summary>
233 protected double BurstRateModifier() 218 protected float BurstModifier()
234 { 219 {
235 // for now... burst rate is always m_quantumsPerBurst (constant) 220 // for now... burst rate is always m_quantumsPerBurst (constant)
236 // larger than drip rate so the ratio of burst requests is the 221 // larger than drip rate so the ratio of burst requests is the
@@ -242,29 +227,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
242 /// Register drip rate requested by a child of this throttle. Pass the 227 /// Register drip rate requested by a child of this throttle. Pass the
243 /// changes up the hierarchy. 228 /// changes up the hierarchy.
244 /// </summary> 229 /// </summary>
245 public void RegisterRequest(TokenBucket child, Int64 request) 230 public void RegisterRequest(TokenBucket child, float request)
246 { 231 {
247 lock (m_children) 232 lock (m_children)
248 { 233 {
249 m_children[child] = request; 234 m_children[child] = request;
250 235
251 TotalDripRequest = 0; 236 m_totalDripRequest = 0;
252 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) 237 foreach (KeyValuePair<TokenBucket, float> cref in m_children)
253 TotalDripRequest += cref.Value; 238 m_totalDripRequest += cref.Value;
254 } 239 }
255 240
256 // Pass the new values up to the parent 241 // Pass the new values up to the parent
257 if (Parent != null) 242 if (m_parent != null)
258 { 243 m_parent.RegisterRequest(this, Math.Min(RequestedDripRate, TotalDripRequest));
259 Int64 effectiveDripRate;
260
261 if (RequestedDripRate > 0)
262 effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest);
263 else
264 effectiveDripRate = TotalDripRequest;
265
266 Parent.RegisterRequest(this, effectiveDripRate);
267 }
268 } 244 }
269 245
270 /// <summary> 246 /// <summary>
@@ -277,9 +253,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
277 { 253 {
278 m_children.Remove(child); 254 m_children.Remove(child);
279 255
280 TotalDripRequest = 0; 256 m_totalDripRequest = 0;
281 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) 257 foreach (KeyValuePair<TokenBucket, float> cref in m_children)
282 TotalDripRequest += cref.Value; 258 m_totalDripRequest += cref.Value;
283 } 259 }
284 260
285 // Pass the new values up to the parent 261 // Pass the new values up to the parent
@@ -293,7 +269,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
293 /// <param name="amount">Number of tokens to remove from the bucket</param> 269 /// <param name="amount">Number of tokens to remove from the bucket</param>
294 /// <returns>True if the requested number of tokens were removed from 270 /// <returns>True if the requested number of tokens were removed from
295 /// the bucket, otherwise false</returns> 271 /// the bucket, otherwise false</returns>
296 public bool RemoveTokens(Int64 amount) 272 public bool RemoveTokens(int amount)
297 { 273 {
298 // Deposit tokens for this interval 274 // Deposit tokens for this interval
299 Drip(); 275 Drip();
@@ -310,19 +286,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
310 return false; 286 return false;
311 } 287 }
312 288
313 /// <summary> 289 public bool CheckTokens(int amount)
314 /// Deposit tokens into the bucket from a child bucket that did
315 /// not use all of its available tokens
316 /// </summary>
317 protected void Deposit(Int64 count)
318 { 290 {
319 m_tokenCount += count; 291 return (m_tokenCount - amount >= 0);
292 }
320 293
321 // Deposit the overflow in the parent bucket, this is how we share 294 public int GetCatBytesCanSend(int timeMS)
322 // unused bandwidth 295 {
323 Int64 burstrate = BurstRate; 296// return (int)(m_tokenCount + timeMS * m_dripRate * 1e-3);
324 if (m_tokenCount > burstrate) 297 return (int)(timeMS * DripRate * 1e-3);
325 m_tokenCount = burstrate;
326 } 298 }
327 299
328 /// <summary> 300 /// <summary>
@@ -337,21 +309,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
337 // with no drip rate... 309 // with no drip rate...
338 if (DripRate == 0) 310 if (DripRate == 0)
339 { 311 {
340 m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", Identifier); 312 m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", m_counter);
341 return; 313 return;
342 } 314 }
343 315
344 // Determine the interval over which we are adding tokens, never add 316 Int32 now = Util.EnvironmentTickCount();
345 // more than a single quantum of tokens 317 Int32 deltaMS = now - m_lastDrip;
346 Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum); 318 m_lastDrip = now;
347 m_lastDrip = Util.EnvironmentTickCount();
348 319
349 // This can be 0 in the very unusual case that the timer wrapped
350 // It can be 0 if we try add tokens at a sub-tick rate
351 if (deltaMS <= 0) 320 if (deltaMS <= 0)
352 return; 321 return;
353 322
354 Deposit(deltaMS * DripRate / m_ticksPerQuantum); 323 m_tokenCount += deltaMS * DripRate * m_timeScale;
324
325 float burst = Burst;
326 if (m_tokenCount > burst)
327 m_tokenCount = burst;
355 } 328 }
356 } 329 }
357 330
@@ -362,103 +335,79 @@ namespace OpenSim.Region.ClientStack.LindenUDP
362 public bool AdaptiveEnabled { get; set; } 335 public bool AdaptiveEnabled { get; set; }
363 336
364 /// <summary> 337 /// <summary>
365 /// Target drip rate for this bucket. 338 /// The minimum rate for flow control. Minimum drip rate is one
339 /// packet per second.
366 /// </summary> 340 /// </summary>
367 /// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks> 341
368 public Int64 TargetDripRate 342 protected const float m_minimumFlow = 50000;
369 { 343
370 get { return m_targetDripRate; } 344 // <summary>
371 set 345 // The maximum rate for flow control. Drip rate can never be
346 // greater than this.
347 // </summary>
348
349 protected float m_maxDripRate = 0;
350 public float MaxDripRate
351 {
352 get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
353 set
372 { 354 {
373 m_targetDripRate = Math.Max(value, m_minimumFlow); 355 m_maxDripRate = (value == 0 ? m_totalDripRequest : Math.Max(value, m_minimumFlow));
374 } 356 }
375 } 357 }
376 protected Int64 m_targetDripRate; 358
359 private bool m_enabled = false;
377 360
378 // <summary> 361 // <summary>
379 // Adjust drip rate in response to network conditions. 362 // Adjust drip rate in response to network conditions.
380 // </summary> 363 // </summary>
381 public virtual Int64 AdjustedDripRate 364 public virtual float AdjustedDripRate
382 { 365 {
383 get { return m_dripRate; } 366 get { return m_dripRate; }
384 set 367 set
385 { 368 {
386 m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate); 369 m_dripRate = OpenSim.Framework.Util.Clamp<float>(value, m_minimumFlow, MaxDripRate);
387 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
388 370
389 if (Parent != null) 371 if (m_parent != null)
390 Parent.RegisterRequest(this, m_dripRate); 372 m_parent.RegisterRequest(this, m_dripRate);
391 } 373 }
392 } 374 }
393 375
394 /// <summary> 376
395 /// The minimum rate for adaptive flow control. 377 // <summary>
396 /// </summary> 378 //
397 protected Int64 m_minimumFlow = 32000; 379 // </summary>
398 380 public AdaptiveTokenBucket(TokenBucket parent, float maxDripRate, float maxBurst, bool enabled)
399 /// <summary> 381 : base(parent, maxDripRate, maxBurst)
400 /// Constructor for the AdaptiveTokenBucket class
401 /// <param name="identifier">Unique identifier for the client</param>
402 /// <param name="parent">Parent bucket in the hierarchy</param>
403 /// <param name="requestedDripRate"></param>
404 /// <param name="maxDripRate">The ceiling rate for adaptation</param>
405 /// <param name="minDripRate">The floor rate for adaptation</param>
406 /// </summary>
407 public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, Int64 minDripRate, bool enabled)
408 : base(identifier, parent, requestedDripRate, maxDripRate)
409 { 382 {
410 AdaptiveEnabled = enabled; 383 m_enabled = enabled;
411 384
412 if (AdaptiveEnabled) 385 MaxDripRate = maxDripRate;
413 { 386
414// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); 387 if (enabled)
415 m_minimumFlow = minDripRate; 388 AdjustedDripRate = m_maxDripRate * .5f;
416 TargetDripRate = m_minimumFlow; 389 else
417 AdjustedDripRate = m_minimumFlow; 390 AdjustedDripRate = m_maxDripRate;
418 }
419 } 391 }
420 392
421 /// <summary> 393 /// <summary>
422 /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down. 394 /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
423 /// <param name="packets">Number of packets that expired without successful delivery</param> 395 /// <param name="packets">Number of packets that expired without successful delivery</param>
424 /// </summary> 396 /// </summary>
425 public void ExpirePackets(Int32 packets) 397 public void ExpirePackets(Int32 count)
426 { 398 {
427 if (AdaptiveEnabled) 399 // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
428 { 400 if (m_enabled)
429 if (DebugLevel > 0) 401 AdjustedDripRate = (Int64)(AdjustedDripRate / Math.Pow(2, count));
430 m_log.WarnFormat(
431 "[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}",
432 AdjustedDripRate, packets, Identifier);
433
434 // AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,packets));
435
436 // Compute the fallback solely on the rate allocated beyond the minimum, this
437 // should smooth out the fallback to the minimum rate
438 AdjustedDripRate = m_minimumFlow + (Int64) ((AdjustedDripRate - m_minimumFlow) / Math.Pow(2, packets));
439 }
440 } 402 }
441 403
442 /// <summary> 404 // <summary>
443 /// Reliable packets acked by the client adjust the drip rate up. 405 //
444 /// <param name="packets">Number of packets successfully acknowledged</param> 406 // </summary>
445 /// </summary> 407 public void AcknowledgePackets(Int32 count)
446 public void AcknowledgePackets(Int32 packets)
447 {
448 if (AdaptiveEnabled)
449 AdjustedDripRate = AdjustedDripRate + packets * LLUDPServer.MTU;
450 }
451
452 /// <summary>
453 /// Adjust the minimum flow level for the adaptive throttle, this will drop adjusted
454 /// throttles back to the minimum levels
455 /// <param>minDripRate--the new minimum flow</param>
456 /// </summary>
457 public void ResetMinimumAdaptiveFlow(Int64 minDripRate)
458 { 408 {
459 m_minimumFlow = minDripRate; 409 if (m_enabled)
460 TargetDripRate = m_minimumFlow; 410 AdjustedDripRate = AdjustedDripRate + count;
461 AdjustedDripRate = m_minimumFlow;
462 } 411 }
463 } 412 }
464} \ No newline at end of file 413}