我有一个简单的IObservable,它从串行设备提供NMEA字符串:
var source = Observable.Create<string>(
observer =>
{
var port = new SerialPort("COM4", 4800, Parity.None, 8, StopBits.One);
string output = string.Empty;
port.DataReceived += (sender, eventArgs) =>
{
var input = (SerialPort)sender;
var buffer = new byte[input.BytesToRead];
try
{
input.Read(buffer, 0, buffer.Length);
}
catch (Exception exception)
{
observer.OnError(exception);
}
var encoding = Encoding.ASCII;
var data = encoding.GetString(buffer);
if (data.StartsWith("$") && output != string.Empty)
{
if (output.StartsWith("$") || data.EndsWith(Environment.NewLine))
{
output = output.TrimEnd(Environment.NewLine.ToCharArray());
observer.OnNext(output);
}
output = data;
}
else if ((output == string.Empty || data.StartsWith("$")) || output.StartsWith("$"))
{
output += data;
}
};
try
{
port.Open();
}
catch (Exception error)
{
observer.OnError(error);
}
return port;
}
);
现在我想将这些字符串解析为稍后可以过滤的具体消息类型。 因此,IObservable需要更专业化为IObservable。
这可以通过.Select()语句解决,其中结果被重新投影到NMEAMessage中...... (以下只是简单重投影的一个例子)
var selected = source.Select(s => { return new NmeaMessage(s); });
...但是如果无法进行重投影会发生什么(例如,传递的字符串的未知消息类型或解析错误)。怎么处理?我不能在这里调用OnError(因为它是一个Observable而不是Observer。只是抑制解析错误并且在这种情况下不返回任何内容?如何声明源可能不是有效的NMEA源?我应该创建一个虚拟的“Device” “class(使用里面的字符串源代码)而不是级联或过滤Observables?”Device“类可以使用事件,然后可以再次创建一个Observable(Observable.FromEventPattern&lt;&gt;)。
我还希望“观察”Parser能够订阅不同的IObservable源。将解析器和重新投影集成到此场景中的最佳方法是什么?
答案 0 :(得分:3)
将NmeaMessage
构造函数包装在这样的静态函数中:
public static NmeaMessage TryParseNmeaMessage(TInputData d)
{
if (IsValidInput(d))
return new NmeaMessage(d);
else
return null;
}
然后你可以这样做:
var inputData = Observable.Create(...);
var parsed = inputData.Select(d => TryParseNmeaMessage(d))
.Where(d => d != null);
显然你也需要定义IsValidInput()
。
而不是null,你也可以从ParseMessage返回一些BadNmeaMessage(包含错误信息的NmeaMessage的子类)。
然后,您可以使用.OfType<(Bad)NmeaMessage>()
分别对好/坏消息做出反应。
或者您可以发出OnError信号(在ParseMessage中抛出),然后重新启动序列。
有关高级错误处理的信息,请参阅here。
在F#中有一篇关于此类错误处理的优秀文章:Railway oriented programming 相同的原则可以在带有RX的C#中使用。