Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何";“重建线条”;使用Rx从串行端口读取的数据_C#_System.reactive - Fatal编程技术网

C# 如何";“重建线条”;使用Rx从串行端口读取的数据

C# 如何";“重建线条”;使用Rx从串行端口读取的数据,c#,system.reactive,C#,System.reactive,我正在学习Rx,并试图通过使用SerialPort的GPS设备实现一个“NMEA句子读取器”。事实上,GPS数据对问题的重要性较低,因此,让我们澄清一下NMEA格式由行组成,“$”符号表示新条目的开始,因此您会得到类似于以下内容的“句子”: $[data for first line goes here] $[data for second line goes here] ... 连接SerialPort的DataReceived事件非常简单: var port = new SerialPo

我正在学习Rx,并试图通过使用SerialPort的GPS设备实现一个“NMEA句子读取器”。事实上,GPS数据对问题的重要性较低,因此,让我们澄清一下NMEA格式由行组成,“$”符号表示新条目的开始,因此您会得到类似于以下内容的“句子”:

$[data for first line goes here]
$[data for second line goes here]
...
连接SerialPort的DataReceived事件非常简单:

 var port = new SerialPort("COM3", 4800);

 var serialPortSource = Observable.FromEventPattern<
    SerialDataReceivedEventHandler,
    SerialDataReceivedEventArgs>
    (
        handler => port.DataReceived += handler,
        handler => port.DataReceived -= handler
    ).Select(e => port.ReadExisting());
如何正确地将其转化为一系列实际的句子?在
IEnumerable
世界中,我可能会从
char
s序列开始,编写一个类似于以下内容的扩展方法:

public static IEnumerable<string> ToNmeaSentence(
    this IEnumerable<char> characters
)
{
    var sb = new StringBuilder();
    foreach (var ch in characters)
    {
        if (ch == '$' && sb.Length > 0) 
        {
            yield return sb.ToString();
            sb.Clear();
        }
        sb.Append(ch);
    }
}
公共静态IEnumerable语句(
这是数不清的字符
)
{
var sb=新的StringBuilder();
foreach(字符中的变量ch)
{
如果(ch='$'&&sb.长度>0)
{
让某人返回字符串();
(某人清楚地);
}
某人附加(ch);
}
}

现在我想知道在Rx中是否有一种惯用的方式来进行这种操作

它与可枚举的代码完全相同。您使用
Subscribe
代替快速枚举,使用
observer.OnNext
代替
yield return
。哦,你必须使用
Observable.Create
,因为C#没有像对枚举表那样对观察者提供语言支持(但这并不是Rx的失败)

可枚举项和可观测项是完全相同的。一个推,另一个拉。创建它们的语法略有不同。就这些

public static IObservable<string> ToNmeaSentence(
    this IObservable<char> characters
)
{
    return Observable.Create<string>(observer => {

        var sb = new StringBuilder();

        return characters.Subscribe(ch => {

            if (ch == '$' && sb.Length > 0)
            {
                observer.OnNext(sb.ToString());
                sb.Clear();
            }
            sb.Append(ch);

        });

    });
}
publicstaticiobservable语句(
这是可观察的字符
)
{
返回可观察的。创建(观察者=>{
var sb=新的StringBuilder();
返回字符。订阅(ch=>{
如果(ch='$'&&sb.长度>0)
{
observer.OnNext(sb.ToString());
(某人清楚地);
}
某人附加(ch);
});
});
}
我通常不会在这么低的级别上编程,但可观察对象不会比可枚举对象更复杂。当人们第一次学习可数时,很难理解。当人们第一次学习可见物时,很难理解。他们两个做同样的事情,但一个推一个拉。除了这一区别之外,两者之间还有1-1的关系


如果您认为Rx比枚举和LINQ对象更复杂,那就错了。当你还在学习的时候,它就这样出现了。

这是一个常见的问题。我目前的想法是,您需要一个操作符,它接受一个源序列,并根据谓词将其分解为(连续的)窗口

这个操作符可能有用

public static IObservable<IObservable<T>> WindowByExclusive<T>(this IObservable<T> input, Func<T, bool> isWindowBoundary)
{
    return Observable.Create<IObservable<T>>(o=>
    {
        var source = input.Publish().RefCount();
        var left = source.Where(isWindowBoundary).Select(_=>Unit.Default).StartWith(Unit.Default);
        return left.GroupJoin(
                        source.Where(c=>!isWindowBoundary(c)),
                        x=>source.Where(isWindowBoundary),
                        x=>Observable.Empty<Unit>(),
                        (_,window)=>window)
                    .Subscribe(o);
    });
}
输出:

WindowByExclusive

[data for first line goes here]
[data for second line goes here] 

没有什么比Rx更能混淆简单的东西了。某个地方的某个人必须维护一个缓冲区,该缓冲区跟踪以前接收到的内容,然后将新接收到的数据附加到该缓冲区中,并实现一个规则,该规则表示已收集完整的响应,以便对其进行处理。您的代码中缺少了这些。@HansPassant哈哈,+1:-)。我想这就是这个问题的要点:在Rx中有没有一种简单的方法可以做到这一点(比如使用IEnumerable的简单扩展方法)?也就是说,在Rx中可以很容易做到这一点。可数项和可观测项只是彼此的倒数。我很快会发布答案。你只是没有发布
&&
的正确技能。别担心,你会随着时间进步的。非常好的洞察力。Rx开发人员有一种倾向,即总是尝试使用现有的运营商,而有时候简单的解决方案也同样好。
onNext
应该是
onNext
,而且没有OnCompleted@LeeCampbell它对我有用。在这种情况下,OnCompleted并不是真正需要的,因为它是一个无限序列。对于
onNext
打字错误,很抱歉。来自这里的土地。:)好啊你应该坚持足够简单。
void Main()
{
    var data = new List<string>(){@"$[data for first line g", "oes here]\r\n$[data for ", "second line goes here]"};

    data.ToObservable()
        .SelectMany(s=>s)
        .WindowByExclusive(c => c=='$')
        .SelectMany(window=>window.ToList().Select(l=>string.Join(string.Empty, l)))
        .Where(s=>!string.IsNullOrEmpty(s))
        .Dump("WindowByExclusive");
}

// Define other methods and classes here
public static class ObEx
{
    public static IObservable<IObservable<T>> WindowByExclusive<T>(this IObservable<T> input, Func<T, bool> isWindowBoundary)
    {
        return Observable.Create<IObservable<T>>(o=>
        {
            var source = input.Publish().RefCount();
            var left = source.Where(isWindowBoundary).Select(_=>Unit.Default).StartWith(Unit.Default);
            return left.GroupJoin(
                            source.Where(c=>!isWindowBoundary(c)),
                            x=>source.Where(isWindowBoundary),
                            x=>Observable.Empty<Unit>(),
                            (_,window)=>window)
                        .Subscribe(o);
        });
    }
}
[data for first line goes here]
[data for second line goes here]