Rx中包含历史和实时部分的流的度量标准

时间:2017-03-12 07:27:16

标签: c# system.reactive reactive-programming reactive

我的问题是Advanceable historical stream and live stream in Rx

的扩展

我希望能够使用各种滚动时间范围(每小时,每天等)监控流中的更改。如果我的历史数据存在差距,例如在每小时的时间范围内,我的数据可以追溯到30'以前和过去120'以前,即我缺少[-120'至-30']间隔,我想根据120'观察。到目前为止我所拥有的代码的问题是,上面示例中的更改将基于值0,因为缺少-60观察值。

我不知道如何完成这项工作,或者是否有一个更清洁,更好的解决方案。

void Main()
{   
    const double bufferDuration = 8;    
    var now = DateTimeOffset.Now;

    // create a historical log for testing
    var log = new List<ValueTime>
            {
                new ValueTime { Ts = now.AddMilliseconds(-5000).ToString(), Balance = 1L },
                new ValueTime { Ts = now.AddMilliseconds(-4000).ToString(), Balance = 2L },
                new ValueTime { Ts = now.AddMilliseconds(-3000).ToString(), Balance = 4L }
            };

    var scheduler = new HistoricalScheduler();

    scheduler.AdvanceTo(DateTime.Parse(log[0].Ts).AddSeconds(-0));

    // historical part of the stream
    var replay = Observable.Generate(
            log.ToObservable().GetEnumerator(),
            events => events.MoveNext(),
            events => events,
            events => events.Current.Balance,
            events => DateTime.Parse(events.Current.Ts),
            scheduler);

    // create the real time part of the stream using a timer
    var realTime = Observable.Create<long>(observer =>
        {
            var timer = Observable.Interval(TimeSpan.FromSeconds(2)).Select(x => 10 + x);
            var disposable = timer.Subscribe(x => observer.OnNext(x));
            return disposable;
        });

    // combine the two streams
    var combined = replay
            .Concat(realTime)
            .Publish()
            .RefCount();

    combined.Timestamp(scheduler).Dump("Combined stream");

    // use the real time stream to set the time of the historical scheduler's
    realTime.Subscribe(_ =>
        {
            scheduler.AdvanceTo(DateTime.Now);
        },
        ex => Console.WriteLine(ex.Message),
        () => Console.WriteLine("Done"));

    // use a rolling buffer of "bufferDuration" length 
    var combinedBuffer = combined
            .RollingBuffer(TimeSpan.FromSeconds(bufferDuration), scheduler)
            .Select(x => x.Sum());

    combinedBuffer.Timestamp(scheduler).Dump($"{bufferDuration}' Rolling buffer aggregation");

    // display the values that are included in each window of the rolling buffer
    combined
    .RollingBuffer(TimeSpan.FromSeconds(bufferDuration), scheduler)
    .Select(x => string.Join(",", x))
    .Timestamp(scheduler).Dump($"{bufferDuration}' Rolling buffer lists");

    // observe the difference between two consecutive observations
    combinedBuffer.CombineWithPrevious((x, y) => y - x).Timestamp(scheduler).Dump($"{bufferDuration}' Rolling differences");

    scheduler.Start();
}

class ValueTime
{
    public long Balance;
    public string Ts;
}

static class Extensions
{
    public static IObservable<T[]> RollingBuffer<T>(
        this IObservable<T> @this,
        TimeSpan buffering, IScheduler scheduler)
    {
        return Observable.Create<
        T[]>(o =>
        {
            var list = new LinkedList<Timestamped<T>>();
            return @this.Timestamp(scheduler).Subscribe(tx =>
            {
                list.AddLast(tx);
                while (list.First.Value.Timestamp < DateTime.Now.Subtract(buffering))
                {
                    list.RemoveFirst();
                }
                o.OnNext(list.Select(tx2 => tx2.Value).ToArray());

            }, o.OnError, o.OnCompleted);
        });
    }

    public static IObservable<TResult> CombineWithPrevious<TSource, TResult>(this IObservable<TSource> source,
                                                                                Func<TSource, TSource, TResult> resultSelector)
    {
        return source.Scan(
            Tuple.Create(default(TSource), default(TSource)),
            (previous, current) => Tuple.Create(previous.Item2, current))
            .Select(t => resultSelector(t.Item1, t.Item2));
    }
}

在LINQPad中运行它会产生以下结果:

enter image description here

在&#34; 8&#39;滚动差异&#34;表,上午1:13:30的条目应该是9而不是13,因为我希望在1:13:19 AM产生的4的值被包括在计算差异中。

似乎滚动缓冲区的当前版本或者我正在采取的方法是不够的,它可能会分散我正在努力实现的目标。我不能使用基于计数的缓冲区,因为如果我在观察中有漏洞,这可以让我很好地回到过去。如果我在下面的例子中使用2的计数,则10:00:00的滚动差异将使用x2 - x0,这不是我正在寻找的结果。

我正在寻找的行为是获得当前值和&gt; = 8''之前(8''之前或8'之前)的滚动差异。例如。

Ts:    9:32:00 9:36:00 10:00:00 10:00:04 10:00:08 10:00:12 10:00:16
Level: --x0------x1-------x2-------x3-------x4-------x5-------x6--- 
Roll D.--x0----(x1-x0)-(x2-x1)--(x3-x1)--(x4-x2)--(x5-x3)--(x6-x4)-

1 个答案:

答案 0 :(得分:0)

我原本以为问题已经足够普遍,已经找到了解决方案,或者至少有一些人对此感兴趣。无论如何,在经历了相当多的时间之后,这主要是由于我对Rx的理解不足,我发现所需行为的步骤是使用上面的代码作为基础并且:

一个。修改滚动缓冲区扩展方法:

list.AddLast(tx);
while (list.First.Value.Timestamp < DateTime.Now.Subtract(buffering))
{
    list.RemoveFirst();
}
o.OnNext(list.Select(tx2 => tx2.Value).ToArray());

为:

    list.AddLast(tx);

    var nowTime = scheduler.Now.DateTime;
    System.Reactive.Timestamped<T> el = default(System.Reactive.Timestamped<T>);
    while (list.Count > 1 && list.First.Value.Timestamp < nowTime.Subtract(buffering))
    {
        el = list.First.Value;
        list.RemoveFirst();
    }

    if (el != default(System.Reactive.Timestamped<T>) && (list.Count <= 1 || list.First.Value.Timestamp > nowTime.Subtract(buffering)))
    {
        list.AddFirst(el);
    }
    o.OnNext(list.Select(tx2 => tx2.Value).ToArray());

B中。而不是值的总和列表,使用列表中最后一个元素和第一个元素之间的差异:

var combinedBuffer = combined
            .RollingBuffer(TimeSpan.FromSeconds(bufferDuration), scheduler)
            .Select(x => x.Last() - x.First());

℃。完全删除“CombineWithPrevious”调用。

combinedBuffer.CombineWithPrevious((x, y) => y - x).Timestamp(scheduler).Dump($"{bufferDuration}' Rolling differences");

带有一些调试打印输出的完整调用如下:

 void Main()
{
    const double bufferDuration = 8;
    var now = DateTimeOffset.Now;

    Console.WriteLine(now);

    // create a historical log for testing
    var log = new List<ValueTime>
            {
                new ValueTime { Ts = now.AddMilliseconds(-5000).ToString(), Balance = 1L },
                new ValueTime { Ts = now.AddMilliseconds(-4000).ToString(), Balance = 2L },
                new ValueTime { Ts = now.AddMilliseconds(-3000).ToString(), Balance = 4L }
            };

    var scheduler = new HistoricalScheduler();

    scheduler.AdvanceTo(DateTime.Parse(log[0].Ts));

    // historical part of the stream
    var replay = Observable.Generate(
            log.ToObservable().GetEnumerator(),
            events => events.MoveNext(),
            events => events,
            events => events.Current.Balance,
            events => DateTime.Parse(events.Current.Ts),
            scheduler);

    // create the real time part of the stream using a timer
    var realTime = Observable.Create<long>(observer =>
        {
            var timer = Observable.Interval(TimeSpan.FromSeconds(2)).Select(x => 10 + x);
            var disposable = timer.Subscribe(x => observer.OnNext(x));
            return disposable;
        });

    // combine the two streams
    var combined = replay
            .Concat(realTime)
            .Publish()
            .RefCount();

    combined.Timestamp(scheduler).Dump("Combined stream");

    // use the real time stream to set the time of the historical scheduler's
    realTime.Subscribe(_ =>
        {
            scheduler.AdvanceTo(DateTime.Now);
        },
        ex => Console.WriteLine(ex.Message),
        () => Console.WriteLine("Done"));

    var combinedRollingBuffer = combined
            .RollingBufferDeltaChange(TimeSpan.FromSeconds(bufferDuration), scheduler)
            .Publish()
            .RefCount();

    // use a rolling buffer of "bufferDuration" length 
    var combinedBuffer = combinedRollingBuffer
            //.Select(x => x.Sum());
            .Select(x => x.Last() - x.First());

    combinedBuffer.Timestamp(scheduler).Dump($"{bufferDuration}'' Rolling buffer aggregation");

    // display the values that are included in each window of the rolling buffer
    combinedRollingBuffer
    .Select(x => string.Join(",", x))
    .Timestamp(scheduler).Dump($"{bufferDuration}'' Rolling buffer lists");

    scheduler.Start();
}

class ValueTime
{
    public long Balance;
    public string Ts;
}

static class Extensions
{
    public static IObservable<T[]> RollingBufferDeltaChange<T>(
        this IObservable<T> @this,
        TimeSpan buffering, IScheduler scheduler)
    {
        return Observable.Create<
        T[]>(o =>
        {
            var list = new LinkedList<Timestamped<T>>();
            return @this.Timestamp(scheduler).Subscribe(tx =>
            {
                list.AddLast(tx);
                Console.WriteLine($"{scheduler.Now} Adding Tx: {tx.Timestamp}  {tx.Value} list.First: {list.First.Value.Timestamp} {list.First.Value.Value} list.Last: {list.Last.Value.Timestamp} {list.Last.Value.Value}");

                DateTime nowTime = scheduler.Now.DateTime; // DateTime.Now;

                System.Reactive.Timestamped<T> el = default(System.Reactive.Timestamped<T>);
                while (list.Count > 1 && list.First.Value.Timestamp < nowTime.Subtract(buffering))
                {
                    el = list.First.Value;
                    list.RemoveFirst();

                    Console.WriteLine($"{scheduler.Now} Removing el: {el.Timestamp}  {el.Value} {list.Count}");
                }

                if (el != default(System.Reactive.Timestamped<T>) && (list.Count <= 1 || list.First.Value.Timestamp > nowTime.Subtract(buffering)))
                {
                    list.AddFirst(el);
                    Console.WriteLine($"{scheduler.Now} Adding el: {el.Timestamp}  {el.Value} {list.Count}");
                    if (list.Count > 0)
                    {
                        Console.WriteLine($"{scheduler.Now} el: {el.Timestamp}  {el.Value} list.First: {list.First.Value.Timestamp} {list.First.Value.Value} list.Last: {list.Last.Value.Timestamp} {list.Last.Value.Value}");
                    }
                }

                o.OnNext(list.Select(tx2 => tx2.Value).ToArray());

            }, o.OnError, o.OnCompleted);
        });
    }
}

它产生了我正在寻找的行为:

enter image description here

如果除了Advanceable historical stream and live stream in Rx中我最初提到詹姆斯世界的答案之外,我还不会在reactive extensions sliding time window中承认他的回应和他的答案,这将是一个很大的遗漏。没有他们的帮助,不仅在这些特定的帖子中,而且在其他许多帖子中,我都没有机会在我的代码中使用Rx,所以非常感谢他们两个。