3

RXを使用して、ボタンが接続されている自動化デバイスから発生するイベントを照会しています。ユーザーがボタンを押してすぐに離したときと、ボタンをしばらく押し続けたときの違いを区別できるようにしたいと思います。彼がボタンを押しているかどうかを確認するために使用するクエリに問題があります。

基本的な考え方は、ボタンが押されると、ボタンが再び離されるまで、0.5秒ごとに値を生成するというものです。また、ボタンが押された時間を知るために、各値にタイムスタンプを付けています。

これが私のコードであり、これがどのように機能すると思うかです。

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);
}

与えられたIObservableから、ボタンが押されるたびに値を生成する監視可能なstartObservableを持つようにクエリを適用しています(状態はfalseからtrueになります)。また、同じソースobservableからクエリされたobservable endObservableがあり、ボタンをもう一度離すと値が生成されます(状態がtrueからfalseになります)。startObservableが値を生成するとき、500ミリ秒ごとに監視可能な間隔を開始します。これらの値にタイムスタンプを付け、 endObservableが値を生成するまでそれを取得します。次に、 startObservableの値を保持するオブジェクトを返しますそして今までどれくらいの期間が保持されてきました。

このための単体テストがあり、ボタンを2秒間押し続け(TestSchedulerを使用)、ボタンを放してから、スケジューラーをさらに数秒間続行します。これは、ボタンを離した後に値が生成されないようにするためです。

これは私のテストが失敗するポイントであり、私の質問のトピックです。私のテストでは、4つの値が生成されることを期待しています(0.5秒、1秒、1.5秒、2秒後)。ただし、 TakeUntil(endObservable)を使用している場合でも、ボタンを離した後もイベントが生成されます(endObservableを生成するdigitalInputReleasedEventAdapterには独自のテストセットがあり、正常に機能すると確信しています) 。

クエリを間違って作成したとは思いません。高温と低温の観測量と関係があるのではないかと思います。しかし、私はRXを始めたばかりなので、それが私がここで抱えている問題にどのように関係しているのかを完全には理解していません。

4

4 に答える 4

1

これがあなたが求めているものを正確に実行するかどうかはわかりませんが、それは「さらに別のアプローチ」です。

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;
}
于 2013-01-12T00:15:16.403 に答える
0

ボタンが押されている間だけタイムイベントを受信することが必要なようです。CombineLatestここであなたを助ける必要があります。

例えば:

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);
        }
    }
}
于 2013-01-11T23:25:57.433 に答える
-1

これがバグかどうかはわかりませんが、TimeStamp呼び出しにテストスケジューラが組み込まれていません。ISchedulerパラメーターを受け取るすべてのオペレーターをチェックし、それらがテスト・スケジューラーに合格していることを確認してください。

于 2013-01-11T18:46:13.267 に答える
-1

私は解決策を見つけました。

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);
}

このコードをコンソールアプリケーションに分離し、IEnumerable <>からソースオブザーバブル(ここではmessageStreamと呼びます)を構築しました。これにより、真と偽の値が生成されました。このIEnumerable<>が数回生成されたので、いくつかのスレッドが開始されたに違いありません。生成された値は、Observable.Intervalの個別のインスタンスによって消費されており、ボタンのリリースを示すメッセージを受信するために互いに競合していたと思います。したがって、startObservableとendObservableでPublish()とConnect()を呼び出しているので、Observable.Intervalインスタンスは同じサブスクリプションを共有します。

于 2013-01-12T15:56:29.573 に答える