C# 如何";“重建线条”;使用Rx从串行端口读取的数据
我正在学习Rx,并试图通过使用SerialPort的GPS设备实现一个“NMEA句子读取器”。事实上,GPS数据对问题的重要性较低,因此,让我们澄清一下NMEA格式由行组成,“$”符号表示新条目的开始,因此您会得到类似于以下内容的“句子”: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
$[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]