C# 如何监控IObservable<;双倍>;对于超出范围的长时间

C# 如何监控IObservable<;双倍>;对于超出范围的长时间,c#,system.reactive,C#,System.reactive,我有一个IObservable,它以一定的间隔给出从传感器读取的值。我想在传感器的值超出范围很长一段时间后发出信号 作为一个具体的例子,假设可观察到的是一个温度传感器。我想监测温度何时超过100°C 5秒钟。也就是说,从IObservable生成一个可观察或事件,当温度超过100°C并持续5秒时触发一次。如果任何数量的样品的温度读数高于100°C且持续时间少于5秒,则此操作无效。第一组样品温度均高于100°C后5秒,应发出信号。如果该值继续高于100°C,则在温度降至100°C以下并再次超过该值

我有一个
IObservable
,它以一定的间隔给出从传感器读取的值。我想在传感器的值超出范围很长一段时间后发出信号

作为一个具体的例子,假设可观察到的是一个温度传感器。我想监测温度何时超过100°C 5秒钟。也就是说,从
IObservable
生成一个可观察或事件,当温度超过100°C并持续5秒时触发一次。如果任何数量的样品的温度读数高于100°C且持续时间少于5秒,则此操作无效。第一组样品温度均高于100°C后5秒,应发出信号。如果该值继续高于100°C,则在温度降至100°C以下并再次超过该值5秒之前,不应重新发出信号


对于反应式扩展来说,这似乎应该很简单,但是作为一个新手,我找不到任何东西。我看了一遍,但什么也没找到。我可能只是不知道要寻找的正确术语。

应该可以通过使用内置运算符(函数式)来实现,但通过实现带有命令式逻辑的自定义运算符来实现则更加困难

public static IObservable<Unit> Alarm<T>(this IObservable<T> source,
    T threshold, TimeSpan delay, IComparer<T> comparer = null)
{
    comparer = comparer ?? Comparer<T>.Default;
    return Observable.Create<Unit>(o =>
    {
        Stopwatch stopwatch = new Stopwatch();
        int alarmState = 0; // 0: OK, 1: above threshold, 2: signal transmitted

        return source.Subscribe(x =>
        {
            if (comparer.Compare(x, threshold) >= 0)
            {
                if (alarmState == 0)
                {
                    alarmState = 1;
                    stopwatch.Restart();
                }
                else if (alarmState == 1 && stopwatch.Elapsed >= delay)
                {
                    alarmState = 2;
                    o.OnNext(Unit.Default);
                }
            }
            else
            {
                alarmState = 0;
            }
        }, o.OnError, o.OnCompleted);
    });
}
公共静态IObservable报警(此IObservable源,
T阈值,时间间隔延迟,IComparer比较器=null)
{
比较器=比较器??比较器。默认值;
返回可观察的。创建(o=>
{
秒表秒表=新秒表();
int alarmState=0;//0:正常,1:高于阈值,2:信号已传输
返回source.Subscribe(x=>
{
如果(比较器比较(x,阈值)>=0)
{
如果(alarmState==0)
{
alarmState=1;
stopwatch.Restart();
}
else if(alarmState==1&&stopwatch.appeased>=延迟)
{
报警状态=2;
o、 OnNext(单位默认值);
}
}
其他的
{
alarmState=0;
}
},o.OnError,o.OnCompleted);
});
}

我考虑了一下,意识到我们可能想得太多了

让我们从一个可观察的阈值开始:

var threshold = 
source
.Select(temp => temp > 100)
.DistinctUntilChanged();
当更改为大于或小于100时,将生成一个值。如果它继续高于100或保持低于100,则不会产生任何新值

现在让我们来定义:

var alarmUp =
    threshold
    .Throttle(TimeSpan.FromSeconds(5))
    .Where(cond => cond == true);
在这里,如果条件在5秒钟内没有改变,
油门
操作员会吐出一个值。现在它可以是
true
(>100)持续5秒,或者
false
(<100)。 我们只对它的真实性感兴趣


在这两种情况之间,如果有任何变化,油门操纵器将被重置,因此该状态必须保持至少5秒钟。

当出现新值时,您应该使用
.Switch()
,以便忽略以前出现的任何值的结果

以下是您需要的查询:

IObservable<Unit> query =
    source
        .Select(x => x > 100.0)
        .DistinctUntilChanged()
        .Select(x => x
            ? Observable.Timer(TimeSpan.FromSeconds(5.0)).Select(x => Unit.Default)
            : Observable.Never<Unit>())
        .Switch();

这不符合OP的要求。如果源可观测值仅产生一个高于
100
的值,则该操作符在
5.0
秒后不会触发。事实上,它永远不会开火。答案需要一个
5.0
第二个计时器才能按照OP的要求工作。@Enigmativity这确实是我的解决方案中的一个缺陷,谢谢你指出。我没有提到这一点,因为OP规定传感器应该以某种固定的间隔产生值。无论如何都比我的好,应该优先考虑。谢谢。在这种情况下,我实际上更喜欢
节流阀
解决方案<代码>开关
功能更强大,可用于
节流
的更多场景,因此学习它很好。向上投票。这是我最初尝试编写的解决方案,但失败了。:-)回答得很好。关于我刚刚学到的操作符的一些信息:忽略一个可观察序列中的元素,该序列在指定的相对时间内后跟另一个元素。在ReactiveX词汇表中称为。
IObservable<Unit> query =
    source
        .Select(x => x > 100.0)
        .DistinctUntilChanged()
        .Select(x => x
            ? Observable.Timer(TimeSpan.FromSeconds(5.0))
            : Observable.Never<long>())
        .Switch()
        .Select(x => Unit.Default);