简体   繁体   English

如何从可观察序列计算出预计到达时间?

[英]How to calculate an ETA from an Observable sequence?

I have a method that sends push me updates with the % complete at a given time. 我有一个方法,可以在给定的时间向我发送%完成的更新。

public Task MyMethod(IObserver<double> progress)
{
   ...
}

My consumer does this: 我的消费者这样做:

ISubject<double> progressObserver = new Subject<double>();
await MyMethod(progressObserver);

It subscribes to the subject to watch for updates: 它订阅该subject以观看更新:

progressObserver.Subscribe(percent => Console.WriteLine(percent));

That works nicely, but I would like to calculate an ETA (the estimated time to complete). 效果很好,但我想计算一个ETA(预计完成时间)。 I know it can be calculated considering the time and percentage, but how? 我知道可以考虑时间和百分比来计算,但是如何计算呢?

For sure there's an elegant way to do it using Observables (System.Reactive), maybe taking the last n percentage notifications and the elapsed time between them to estimate when the 100% will be accomplished. 当然,有一种使用Observables (System.Reactive)的优雅方法,也许可以获取最后n个百分比的通知以及它们之间经过的时间,以估计何时可以完成100%。

But, excuse me, I don't have a clue on how to do it nicely and elegantly. 但是,对不起,我不知道如何做得好而优雅。

It could look like this: 它可能看起来像这样:

var secondsRemaining = progressObservable
    .Timestamp()
    .Buffer(5, 1)
    .Select(l => ((l[4].Timestamp - l[0].Timestamp).TotalMilliseconds / (l[4].Value - l[0].Value)) * (100 - l[4].Value))
    .Select(msRemaining => msRemaining / 1000);

Explanation: 说明:

For each progress update, 对于每个进度更新,

  • .Buffer(5, 1) releases a list of the last 5 progress updates. .Buffer(5, 1)释放最近5个进度更新的列表。
  • .Timestamp() staples the timestamp onto each one. .Timestamp()将时间戳记钉在每个时间戳记上。
  • The first Select operator calculates the amount of milliseconds between the most distant timestamp and the most recent, divides it by progress achieved, and then multiplies it by progress remaining, outputting milliseconds remaining. 第一个Select运算符计算距离最远的时间戳和最近的时间戳之间的毫秒数,将其除以已实现的进度,然后将其乘以剩余的进度,输出剩余的毫秒数。
  • The last Select divides by 1000 to achieve seconds left. 最后一个Select除以1000可获得秒数。

Here's how I would look at this: 这是我的看法:

IObservable<Timestamped<double>> estimatedCompletion =
    progressObservable
        .Timestamp()
        .Buffer(2, 1)
        .Where(x => x.Count() == 2)
        .Scan((a, b) => a.Take(1).Concat(b).Take(1).Concat(b.Skip(1)).ToList())
        .Select(x => new
        {
            current = x[1].Value,
            delta = x[1].Timestamp.Subtract(x[0].Timestamp),
        })
        .Select(x => new
        {
            x.current,
            rate = x.current / x.delta.TotalSeconds,
        })
        .Select(x => new
        {
            x.current,
            estimated = DateTimeOffset.Now.AddSeconds((1.0 - x.current) / x.rate),
        })
        .Select(x => new Timestamped<double>(x.current, x.estimated));

This produces an IObservable<Timestamped<double>> where the timestamp is the estimated DateTimeOffset that the observable will reach 1.0 (or 100% ). 这将产生IObservable<Timestamped<double>> ,其中时间戳是可观察值将达到1.0 (或100% )的估计DateTimeOffset

The key thing is that it uses .Buffer(2, 1).Where(x => x.Count() == 2) to make pairs of values as the observable produces values and then it uses the seemingly complicated .Scan((a, b) => a.Take(1).Concat(b).Take(1).Concat(b.Skip(1)).ToList()) to always produce a pair of the first value with the latest one. 关键是它使用.Buffer(2, 1).Where(x => x.Count() == 2)来创建值对,因为可观察到的值会生成,然后使用看似复杂的.Scan((a, b) => a.Take(1).Concat(b).Take(1).Concat(b.Skip(1)).ToList())总是产生一对第一个值与最新的一个。

Then it just simply does the estimate calculation in a series of steps. 然后,只需简单地按一系列步骤进行估算即可。

Because the original sequence goes from 0.0 to 1.0 this will most accurately hone in on the final time. 由于原始序列从0.0变为1.0,因此可以最准确地在最终时间进行磨合。 It's only an estimate, but if the steps to get there are fairly consistent then this will be fairly accurate. 这只是一个估计,但是如果达到目标的步骤相当一致,那么这将是相当准确的。

You can test it with this code: 您可以使用以下代码进行测试:

var rnd = new Random();
var progressObservable = Observable.Generate(0, x => x <= 100, x => x + 1, x => x / 100.0, x => TimeSpan.FromSeconds(rnd.NextDouble()));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM