使用变量Period将IEnumerable转换为IObservable

时间:2015-06-08 20:33:57

标签: c# unit-testing system.reactive

我正在使用Rx消耗3轴加速度计数据。我需要设置一些单元测试。数据帧快速进入,帧之间的中间时间跨度为80ms,但有时它会在120ms时出现。此外,它永远不会是80毫秒,但会在该范围内盘旋。

所以我创建了一个订阅IObservable的类,并在.csv文件中按顺序记录数据帧。 .csv文件中的一个字段是帧的开始时间,第一帧具有开始时间= 0.0。

现在我想读取该文件并再次流式传输以进行测试和开发。我想使用StartTime字段作为我在测试时触发任何给定的Accelerometer帧的时间表。

我查看了这个问题的答案,Scheduling a IEnumerable periodically with .NET reactive extensions 但它似乎只是为了解决不断的时间跨度。

问题: 是否已经有一种规范和首选的方式来在Rx框架中以不规则(但已知)的间隔安排帧推送,或者我应该以某种方式滚动自己的?

编辑2: 我对一些可能很简单的事情感兴趣:

IObservable<T> AsObservable(
   IEnumerable<T> source, Func<T, TimeSpan> getTimeDelta)
{
   var retVal = ColdObservableVaryingTime();
   foreach(var frame in source)
   {
       retVal.AddScheduled(getTimeDelta, frame);
   }
   return retVal;
}

编辑1:在这个问题中我称之为“框架”,Rx文档调用TState。

3 个答案:

答案 0 :(得分:3)

这是您的另一个选择。 Observable.Generate方法非常强大,可用于生成非常复杂的值序列。

这是你如何做到的。

所以,从这样的CSV文件开始:

var csvLines = new []
{
    "A,0",
    "B,3",
    "C,4",
    "D,6",
};

(可以像var csvLines = File.ReadAllLines(@"...");一样阅读。)

然后你可以解析这些行:

var parsed =
(
    from line in csvLines
    let parts = line.Split(',')
    select new
    {
        item = parts[0],
        seconds = int.Parse(parts[1])
    }
).ToArray();

然后确定CSV中每行之间的秒offset

var data =
    parsed
        .Zip(parsed.Skip(1), (p0, p1) => new
        {
            p1.item,
            offset = p1.seconds - p0.seconds,
        })
        .ToArray();

现在你可以创建observable:

var observable =
    Observable
        .Generate(
            0,
            n => n < data.Length,
            n => n + 1,
            n => data[n].item,
            n => TimeSpan.FromSeconds(data[n].offset))
        .StartWith(parsed[0].item);

当我浏览这段代码时,我从源文件中得到了正确的时间。

我已根据以下评论中的类定义更新了我的代码:

IEnumerable<AccelerometerFrame_raw> frames = ...;

var data =
    frames
        .Zip(frames.Skip(1), (f0, f1) => new
        {
            f1,
            offset = f1.TimeStampSeconds - f0.TimeStampSeconds,
        })
        .ToArray();

IObservable<AccelerometerFrame_raw> observable =
    Observable
        .Generate(
            0,
            n => n < data.Length,
            n => n + 1,
            n => data[n].f1,
            n => TimeSpan.FromSeconds(data[n].offset))
        .StartWith(frames.First());

答案 1 :(得分:1)

为此,您可以使用TestScheduler(获取Rx-Testing Nuget包)。在测试中使用此类作为IScheduler实现将允许您安排序列,因为测试调度程序允许您通过声明何时应按下每个项目来创建可观察序列。然后,您可以“播放”调度程序或前进到某个时间等,看看发生了什么。请注意,在此设置中,时间是虚拟化的,为简单起见,TestScheduler将Ticks作为其时间进度单位处理。

RX网站简介在这里有一个很好的RX测试部分:http://www.introtorx.com/content/v1.0.10621.0/16_TestingRx.html

答案 2 :(得分:0)

James Lucas提供的答案是一个指向正确方向的好指针,但成功的答案更多。

使用csv文件中的值填充IEnumerable后,我必须填充一个

的数组
lista_esferas = malloc(sizeof( *esfera)*cantidad_de_esferas);

                               ^

然后我将数组作为参数传递给调度程序CreateColdAbservable。完成这项工作后,寒冷的观察者坐在那里等待开始。在我的特殊情况下,代码如下所示:

Recorded<Notification<AccelerometerFrame_raw>>

////当我准备开始冷观察时,我打电话给

private TestScheduler sched {get; set;}
public IObservable<AccelerometerFrame_raw> DataStream { get; protected set; }
ctor()
{
   DataStream = SetupDeviceStream();
}
  private IObservable<AccelerometerFrame_raw> SetupDeviceStream()
  {
     var framesArray = 
       new Recorded<Notification<AccelerometerFrame_raw>>[allFrames.Count+1];
     int i = 0;
     long timeStamp = 0;
     foreach(var item in allFrames)
     {
        timeStamp += (long) (item.TimeStampSeconds * 1000.0);
        framesArray[i] = new Recorded<Notification<AccelerometerFrame_raw>>(
           timeStamp, 
           Notification.CreateOnNext(item));
        i++;
     }
     framesArray[i] = new Recorded<Notification<AccelerometerFrame_raw>>(
           timeStamp + 10, 
           Notification.CreateOnCompleted<AccelerometerFrame_raw>());

     sched = new TestScheduler();
     var stream =sched.CreateColdObservable(framesArray);
     return stream;
  }

将这些全部放在一起的帮助来自

(Phil Haak)http://haacked.com/archive/2014/03/10/master-time-with-reactive-extensions/

https://msdn.microsoft.com/en-us/library/hh229343(v=vs.103).aspx

还需要安装另一个Nuget包:

http://www.nuget.org/packages/reactiveui-testing/

正如Phil Haak在链接的博客文章中所说,&#34;不幸的是,[TestScheduler]使用原样有点痛苦,这就是Paul Betts自己编写一些有用的TestScheduler扩展方法的原因&# 34;