2

I have a hot observable stream of sensor data. I need an observable signal that fires only when the sensor value has been below 15 for a set period of time. If at anytime the value goes above 15 it should reset the sliding window. I've made it work partially with the code below - however it does not trigger if the value stays below 15 the entire time.

var notification = _sensor.Where(v => v >= 15)
                          .Throttle(new TimeSpan(0, 1, 0))
                          .SelectMany(_ => Observable.Return(Unit.Default));

Any suggestions?

batkuip
  • 1,480
  • 3
  • 15
  • 25
  • Can I clarify that what you want this to do is only produces values when the value is below `15` but only after the set period of time has elapsed since the last value that is `15` or greater? – Enigmativity Sep 09 '15 at 08:07
  • Correct. And the moment it's above 15 again it should stop producing values. – batkuip Sep 09 '15 at 11:15
  • Why do you do `.SelectMany(_ => Observable.Return(Unit.Default))` and not just `.Select(_ => Unit.Default)`? – Enigmativity Sep 09 '15 at 13:38
  • Can you please explain what you're trying to do again? Based on my question above it seems to me that the answer you selected doesn't do that at all. I'm confused. – Enigmativity Sep 09 '15 at 13:39
  • I believe he requires a single notification every time the sensor has provided values only below 15 for specified TimeSpan (after the most recent value that is equal or over 15, hence the Throttle) – supertopi Sep 09 '15 at 14:17
  • @supertopi - I thought he wanted all values to raises a notification unless they were equal to or greater than 15 or were within a set period after the last value equal to or greater than 15. – Enigmativity Sep 09 '15 at 22:14

2 Answers2

1

If _sensor never emits a value equal or over 15, the Throttle is never called.

Easy fix is to add a wakeup notification either to _sensor or notification

var wakeup = Observable.Return(15);

var notification = _sensor.Merge(wakeup)
                          .Where(v => v >= 15)
                          .Throttle(new TimeSpan(0, 1, 0))
                          .SelectMany(_ => Observable.Return(Unit.Default));
supertopi
  • 3,469
  • 26
  • 38
  • 1
    Deceptively simple and works like charm. Only change I made was to swap the merge around to wakeup.Merge(_sensor) so wakeup always comes first. – batkuip Sep 09 '15 at 12:55
0

If I understand your need correctly then this works:

var notification = _sensor.Publish(ps => ps
    .Select(x => x >= 15.0)
    .DistinctUntilChanged()
    .Select(p => p
        ? Observable.Empty<double>()
        : Observable
            .Timer(TimeSpan.FromMinutes(1.0))
            .Select(x => -1.0)
            .IgnoreElements()
            .Concat(ps))
    .Switch());

I have assumed that _sensor is an IObservable<double>.

So this observable will publish all values from the _sensor stream so long as the values have remained below 15.0 for at least one minute. I have tested this functionality.

My test code is:

var random = new Random();
var _sensor = Observable.Generate(
    0,
    x => true,
    x => x,
    x => random.NextDouble() * 16.0,
    x => TimeSpan.FromSeconds(random.NextDouble()));

var published_sensor = _sensor.Publish();

var notification = published_sensor.Publish(ps => ps
    .Select(x => x >= 15.0)
    .DistinctUntilChanged()
    .Select(p => p
        ? Observable.Empty<double>()
        : Observable
            .Timer(TimeSpan.FromSeconds(5.0))
            .Select(x => -1.0)
            .IgnoreElements()
            .Concat(ps))
    .Switch());

published_sensor.Merge(notification).Timestamp().Dump();

published_sensor.Connect();

The results I got are:

results

Note that 5 seconds pass before duplicate values are published and the duplicates stop when the source produces a value over 15.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • I'm not entirely sure what is happening here. Is it creating a Timer for every sensor value? Also what is the purpose of the duplicates? – batkuip Sep 09 '15 at 12:58
  • The timer is simply for producing a series of random values at random intervals - it is for mocking the sensor values. The duplicates are because I merged the original series with the notifications - `published_sensor.Merge(notification)` - it shows when the `notification` stream is producing values with the original `_sensor` stream. When there's a duplicate you know that the original value is now being output in the notifications. – Enigmativity Sep 09 '15 at 13:11