C# 从可观测值中选取。间隔,直到另一个可观测值产生一个值

C# 从可观测值中选取。间隔,直到另一个可观测值产生一个值,c#,system.reactive,C#,System.reactive,我正在使用RX查询来自自动化设备的事件,该设备有连接到它的按钮。我希望在用户刚刚按下并立即释放按钮时,或者在按住按钮一段时间后,能够区分两者之间的差异。我对我用来查看他是否按下按钮的查询有问题 基本思想是,一旦按下按钮,我将每隔半秒产生一个值,直到再次释放按钮。我还在每个值上加时间戳,这样我就知道按钮被按下了多长时间 以下是我的代码,以及我认为应该如何工作: public IObservable<DigitalInputHeldInfo> Adapt(IObservable<E

我正在使用RX查询来自自动化设备的事件,该设备有连接到它的按钮。我希望在用户刚刚按下并立即释放按钮时,或者在按住按钮一段时间后,能够区分两者之间的差异。我对我用来查看他是否按下按钮的查询有问题

基本思想是,一旦按下按钮,我将每隔半秒产生一个值,直到再次释放按钮。我还在每个值上加时间戳,这样我就知道按钮被按下了多长时间

以下是我的代码,以及我认为应该如何工作:

public IObservable<DigitalInputHeldInfo> Adapt(IObservable<EventPattern<AdsNotificationEventArgs>> messageStream) {
  var startObservable = 
    _digitalInputPressedEventAdapter.Adapt(messageStream);
  var endObservable = 
    _digitalInputReleasedEventAdapter.Adapt(messageStream);

  return from notification in startObservable.Timestamp()

    from interval in 
        Observable.
        Interval(
            500.Milliseconds(),
            _schedulerProvider.ThreadPool).
        Timestamp().
        TakeUntil(endObservable)

    select new DigitalInputHeldInfo(
            interval.Timestamp.Subtract(notification.Timestamp),
            notification.Value);
}
public IObservable Adapt(IObservable消息流){
var startObservable=
_digitalInputPressedEventAdapter.Adapt(消息流);
var内可观测=
_适应(消息流);
从startObservable.Timestamp()中的通知返回
从中间隔
可见的。
间歇(
500.毫秒(),
_schedulerProvider.ThreadPool)。
时间戳()。
TakeUntil(内观测)
选择新的DigitalInputEldInfo(
interval.Timestamp.Subtract(通知.Timestamp),
通知(价值);
}
从一个给定的IObservable,我对它应用了一个查询,这样我就有了一个可观察的startObservable,它在每次按下按钮时都会产生一个值(状态从false变为true)。我还有一个可观察的endObservable,从同一个可观察的源查询,当再次释放按钮时,它会产生一个值(状态从true变为false)。当startObservable生成一个值时,我每隔500毫秒就开始一个可观察的间隔。我给这些值加上时间戳,然后从中提取,直到endObservable生成一个值。然后我返回一个对象,该对象包含startObservable的值,以及到现在为止它被保留了多长时间

我有一个单元测试,我按住按钮2秒钟(使用TestScheduler),释放它,然后让scheduler再运行几秒钟。我这样做是为了确保释放按钮后不会产生更多的值

这是我考试失败的地方,也是我问题的主题。在我的测试中,我期望产生4个值(0.5s、1s、1.5s和2s之后)。但是,在按钮被释放后仍然会产生一些事件,尽管我使用的是TakeTill(endObservable)(生成endObservable的DigitalInputEleaseDevenAdapter有自己的一组测试,我相信它的工作方式是正确的)


我不认为我构造的查询有误。我怀疑这可能与冷热观测有关。但是,由于我刚刚开始使用RX,我并不完全了解这与我这里遇到的问题有什么关系。

不确定这是否是错误,但您的时间戳调用没有包含测试计划程序。检查所有使用IsScheduler参数的操作员,确保他们正在传入测试计划程序。

听起来您希望的是只在按钮按下时接收时间事件。我应该在这里帮助你

例如:

using Microsoft.Reactive.Testing;
using System;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Subjects;

namespace ButtonTest
{
    class Program
    {
        enum State
        {
            KeyDown, KeyUp
        }

        static void Main(string[] args)
        {
            var buttonState = new BehaviorSubject<State>(State.KeyUp);
            var testScheduler = new TestScheduler();
            var events = testScheduler.CreateObserver<long>();

            Observable.Interval(TimeSpan.FromTicks(100), testScheduler)
                .CombineLatest(buttonState, (t,s)=> new { TimeStamp = t, ButtonState = s })
                .Where(t => t.ButtonState == State.KeyDown)
                .Select(t => t.TimeStamp)
                .Subscribe(events);

            testScheduler.AdvanceBy(100);//t=0
            testScheduler.AdvanceBy(100);//t=1

            buttonState.OnNext(State.KeyDown);
            testScheduler.AdvanceBy(100);//t=2

            testScheduler.AdvanceBy(100);//t=3
            buttonState.OnNext(State.KeyUp);

            testScheduler.AdvanceBy(100);//t=4
            testScheduler.AdvanceBy(100);//t=5

            buttonState.OnNext(State.KeyDown);
            testScheduler.AdvanceBy(100);//t=6

            buttonState.OnNext(State.KeyUp);

            testScheduler.AdvanceBy(100);//t=7
            testScheduler.AdvanceBy(100);//t=8

            Debug.Assert(events.Messages.Count == 5);
            Debug.Assert(events.Messages[0].Value.Value == 1);
            Debug.Assert(events.Messages[1].Value.Value == 2);
            Debug.Assert(events.Messages[2].Value.Value == 3);
            Debug.Assert(events.Messages[3].Value.Value == 5);
            Debug.Assert(events.Messages[4].Value.Value == 6);
        }
    }
}
使用Microsoft.Reactive.Testing;
使用制度;
使用系统诊断;
使用System.Reactive.Linq;
使用系统、反应、主题;
名称空间按钮测试
{
班级计划
{
枚举状态
{
向下键,向上键
}
静态void Main(字符串[]参数)
{
var buttonState=新行为主体(State.KeyUp);
var testScheduler=newtestscheduler();
var events=testScheduler.CreateObserver();
可观察的时间间隔(TimeSpan.FromTicks(100),testScheduler)
.CombineTest(buttonState,(t,s)=>new{TimeStamp=t,buttonState=s})
.Where(t=>t.ButtonState==State.KeyDown)
.Select(t=>t.TimeStamp)
·订阅(活动);
testScheduler.AdvanceBy(100);//t=0
testScheduler.AdvanceBy(100);//t=1
按钮State.OnNext(State.KeyDown);
testScheduler.AdvanceBy(100);//t=2
testScheduler.AdvanceBy(100);//t=3
按钮State.OnNext(State.KeyUp);
testScheduler.AdvanceBy(100);//t=4
testScheduler.AdvanceBy(100);//t=5
按钮State.OnNext(State.KeyDown);
testScheduler.AdvanceBy(100);//t=6
按钮State.OnNext(State.KeyUp);
testScheduler.AdvanceBy(100);//t=7
testScheduler.AdvanceBy(100);//t=8
Assert(events.Messages.Count==5);
Debug.Assert(events.Messages[0].Value.Value==1);
Assert(events.Messages[1].Value.Value==2);
Assert(events.Messages[2].Value.Value==3);
Assert(events.Messages[3].Value.Value==5);
Assert(events.Messages[4].Value.Value==6);
}
}
}

不确定这是否正是您想要的,但这是“另一种方法”:

void Main()
{
//ButtonPush返回IObservable
var ButtonPush=ButtonPush();
var推送跟踪器=
从buttonPusher.Where(p=>p.Timestamp()中的pushOn开始
让tickWindow=可观察的时间间隔(TimeSpan.From毫秒(500))
从窗口中的勾号
.TakeUntil(按钮引导器位置(p=>!p))
.时间戳()
.Select(t=>t.Timestamp)
选择勾号;
WriteLine(“Start:{0}”,Observable.Return(true).Timestamp().First().Timestamp);
var s=推送跟踪器
.SubscribeOn(NewThreadScheduler.Default)
.Subscribe(x=>Console.WriteLine(“勾选:{0}”,x));
}
IObservable ButtonPush()
{
var push=新行为主体(false);
var rnd=新随机数();
Task.Factory.StartNew(
() => 
{
//运行后“按下按钮”
void Main()
{
    // ButtonPush returns IObservable<bool>
    var buttonPusher = ButtonPush();
    var pushTracker = 
        from pushOn in buttonPusher.Where(p => p).Timestamp()
        let tickWindow = Observable.Interval(TimeSpan.FromMilliseconds(500))
        from tick in tickWindow
            .TakeUntil(buttonPusher.Where(p => !p))
            .Timestamp()
            .Select(t => t.Timestamp)
        select tick;

    Console.WriteLine("Start: {0}", Observable.Return(true).Timestamp().First().Timestamp);
    var s = pushTracker
        .SubscribeOn(NewThreadScheduler.Default)
        .Subscribe(x => Console.WriteLine("tick:{0}", x));
}

IObservable<bool> ButtonPush()
{
    var push = new BehaviorSubject<bool>(false);    
    var rnd = new Random();
    Task.Factory.StartNew(
        () => 
        {
            // "press button" after random # of seconds
            Thread.Sleep(rnd.Next(2, 5) * 1000);
            push.OnNext(true);
            // and stop after another random # of seconds
            Thread.Sleep(rnd.Next(3, 10) * 1000);
            push.OnNext(false);
        });
    return push;
}
public IObservable<DigitalInputHeldInfo> Adapt(
  IObservable<EventPattern<AdsNotificationEventArgs>> messageStream) {

  var startObservable = _digitalInputPressedEventAdapter.
      Adapt(messageStream).
      Publish();
  var endObservable = _digitalInputReleasedEventAdapter.
      Adapt(messageStream).
      Publish();

  startObservable.Connect();
  endObservable.Connect();

  return from notification in startObservable.Timestamp()
         from interval in Observable.Interval(500.Milliseconds(), 
                                              _schedulerProvider.ThreadPool).
                          Timestamp().
                          TakeUntil(endObservable)
         select new DigitalInputHeldInfo(
                interval.Timestamp.Subtract(notification.Timestamp), 
                notification.Value);
}