TimeNormalizer interface
Vladimir Bauer
7 years ago
| 7 | 7 | "github.com/VividCortex/ewma" |
| 8 | 8 | ) |
| 9 | 9 | |
| 10 | type TimeNormalizer func(time.Duration) time.Duration | |
| 10 | // TimeNormalizer interface | |
| 11 | // Implementors meant to normalize ETA. | |
| 12 | type TimeNormalizer interface { | |
| 13 | Normalize(time.Duration) time.Duration | |
| 14 | } | |
| 15 | ||
| 16 | // TimeNormalizerFunc is function type adapter to convert function | |
| 17 | // into TimeNormalizer. | |
| 18 | type TimeNormalizerFunc func(time.Duration) time.Duration | |
| 19 | ||
| 20 | func (f TimeNormalizerFunc) Normalize(src time.Duration) time.Duration { | |
| 21 | return f(src) | |
| 22 | } | |
| 11 | 23 | |
| 12 | 24 | // EwmaETA exponential-weighted-moving-average based ETA decorator. |
| 13 | 25 | // |
| 17 | 29 | // |
| 18 | 30 | // `wcc` optional WC config |
| 19 | 31 | func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator { |
| 20 | return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...) | |
| 32 | return MovingAverageETA(style, ewma.NewMovingAverage(age), nil, wcc...) | |
| 21 | 33 | } |
| 22 | 34 | |
| 23 | 35 | // MovingAverageETA decorator relies on MovingAverage implementation to calculate its average. |
| 26 | 38 | // |
| 27 | 39 | // `average` available implementations of MovingAverage [ewma.MovingAverage|NewMedian|NewMedianEwma] |
| 28 | 40 | // |
| 29 | // `normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] | |
| 41 | // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] | |
| 30 | 42 | // |
| 31 | 43 | // `wcc` optional WC config |
| 32 | 44 | func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { |
| 58 | 70 | } |
| 59 | 71 | |
| 60 | 72 | v := math.Round(d.average.Value()) |
| 61 | remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v))) | |
| 73 | remaining := time.Duration((st.Total - st.Current) * int64(v)) | |
| 74 | if d.normalizer != nil { | |
| 75 | remaining = d.normalizer.Normalize(remaining) | |
| 76 | } | |
| 62 | 77 | hours := int64((remaining / time.Hour) % 60) |
| 63 | 78 | minutes := int64((remaining / time.Minute) % 60) |
| 64 | 79 | seconds := int64((remaining / time.Second) % 60) |
| 165 | 180 | func MaxTolerateTimeNormalizer(maxTolerate time.Duration) TimeNormalizer { |
| 166 | 181 | var normalized time.Duration |
| 167 | 182 | var lastCall time.Time |
| 168 | return func(remaining time.Duration) time.Duration { | |
| 183 | return TimeNormalizerFunc(func(remaining time.Duration) time.Duration { | |
| 169 | 184 | if diff := normalized - remaining; diff <= 0 || diff > maxTolerate || remaining < maxTolerate/2 { |
| 170 | 185 | normalized = remaining |
| 171 | 186 | lastCall = time.Now() |
| 174 | 189 | normalized -= time.Since(lastCall) |
| 175 | 190 | lastCall = time.Now() |
| 176 | 191 | return normalized |
| 177 | } | |
| 192 | }) | |
| 178 | 193 | } |
| 179 | 194 | |
| 180 | 195 | func FixedIntervalTimeNormalizer(updInterval int) TimeNormalizer { |
| 181 | 196 | var normalized time.Duration |
| 182 | 197 | var lastCall time.Time |
| 183 | 198 | var count int |
| 184 | return func(remaining time.Duration) time.Duration { | |
| 199 | return TimeNormalizerFunc(func(remaining time.Duration) time.Duration { | |
| 185 | 200 | if count == 0 || remaining <= time.Duration(15*time.Second) { |
| 186 | 201 | count = updInterval |
| 187 | 202 | normalized = remaining |
| 195 | 210 | return normalized |
| 196 | 211 | } |
| 197 | 212 | return remaining |
| 198 | } | |
| 199 | } | |
| 200 | ||
| 201 | func NopNormalizer() TimeNormalizer { | |
| 202 | return func(remaining time.Duration) time.Duration { | |
| 203 | return remaining | |
| 204 | } | |
| 205 | } | |
| 213 | }) | |
| 214 | } | |