我有一种情况,我通过串口/ rs232连接到设备; 建立与设备的连接后,它将开始将特定于传感器的数据泵回客户端。
我有两个目标: a)接收连续的传感器状态更新数据流并切换到UI b)发送命令并等待回复
var messageObserver = Observable.Defer(() => serialPort.TakeWhile(
s => s != string.Empty))
.Repeat();
从这里开始,我想开始处理传感器特定的数据: 目前我正在尝试这个
this.messageObserver.Where(s => Regex.Match(s, @"...").Success)
.Subscribe(o => OnDataArrival(o));
哪个很好..直到我想执行像这样的代码
public string GetFirmwareVersion()
{
var firmwareObserver =
this.messageObserver.Where(s => Regex.Match(s, @"...").Success)
.Take(1)
.PublishLast();
var connectable = firmwareObserver.Connect();
// send command for firmware version
port.Send(new byte[] { 0x9 });
// wait for a reply up to 10 seconds
var data = firmwareObserver.Timeout(TimeSpan.FromSeconds(10)).Wait();
connectable.Dispose();
return data;
}
我从未收到任何回复,并抛出异常(显然来自TimeOut)。
如果我注释掉了初始订阅者(触发OnDataArrival
),GetFirmwareVersion
代码就可以了!
所以我认为我有兴趣学习的东西是建议实现两个目标的途径: a)处理在接收时通过线路传入的数据 b)连接&等待我们仍处理进入的数据
答案 0 :(得分:0)
我认为您正在看到拥有源流的多个订阅者的副作用。如果您同时运行用户同时触发OnDataArrival
和GetFirmwareVersion
代码,那么您最终将针对serialPort
运行两个并发查询。
虽然您要在GetFirmwareVersion
代码中发布,但您必须从serialPort
发布一个来源并在所有订阅者之间共享。所以你需要做的是发布messageObserver,并在两个查询中使用它,并在所有订阅者订阅时连接它。您可以在订阅所有订阅者之前进行连接(如果您希望在从端口开始读取后的某个任意点获取固件版本,您的方案可能会要求此连接) - 请注意,您可能会错过事件。 / p>
存在替代发布运营商以避免这种情况,例如Replay()
。以下是基本Publish
- Connect
用法:
var messageObserver = Observable.Defer(
() => serialPort.TakeWhile(s => s != string.Empty)).Repeat().Publish();
然后订阅电话:
messageObserver.Connect();
根据您的方案,有许多方法可以管理发布和连接,但这是基本方法。现在,订阅者将共享一个订阅端口订阅。
答案 1 :(得分:0)
对我有用的是什么:
private readonly ObservableSerialPort port; // serial port wrapper
private readonly IObservable<string> messageObserver;
private readonly Subject<string> subject;
在我的类构造函数中:
this.port = new ObservableSerialPort_string("COM4");
this.messageObserver = Observable.Defer(() => port.TakeWhile(s => s != string.Empty)).Select(s => new Regex(@"...", RegexOptions.CultureInvariant).Replace(s, string.Empty)).Repeat();
subject = new Subject<string>();
var subSource = messageObserver.Subscribe(subject);
subject.Subscribe(x => Console.WriteLine("raw: " + x));
subject.Where(s => Regex.Match(s, @"...").Success)
.Subscribe(x => Console.WriteLine("gauge: " + x));
现在我可以执行这样的代码:
public string GetFirmwareVersion()
{
var f = subject.Where(s => Regex.Match(s, @"...").Success)
.Take(1)
.PublishLast();
var c = f.Connect();
// send command for firmware version
port.Send(new byte[] { 0x9 });
var data = f.Timeout(TimeSpan.FromSeconds(10)).Wait();
c.Dispose();
return data;
}
public Config GetConfiguration()
{
using (var subject2 = new Subject<Config>())
{
var f = subject.Where(s => Regex.Match(s, @"...").Success)
.Select(s =>
{
// get data via xmodem
Thread.Sleep(500);
var modem = new Modem(this.port._serialPort);
var bytes = modem.XModemReceive(true);
subject2.OnNext(new DeviceConfig(bytes));
subject2.OnCompleted();
return s;
})
.Take(1)
.PublishLast();
var c = f.Connect();
// send command for firmware version
port.Send(new byte[] { 0x2 });
var ret = subject2.Timeout(TimeSpan.FromSeconds(60)).Wait();
c.Dispose();
return ret;
}
}
@James,你可能会砰的一声。作为Rx的新手,我能够描述我之前的结果/问题的最佳方式与线程阻塞类似。使用Subject
课程创造了一个与众不同的世界。