多个订户并等待不工作

时间:2014-11-10 16:29:25

标签: c# system.reactive

我有一种情况,我通过串口/ 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)连接&等待我们仍处理进入的数据

2 个答案:

答案 0 :(得分:0)

我认为您正在看到拥有源流的多个订阅者的副作用。如果您同时运行用户同时触发OnDataArrivalGetFirmwareVersion代码,那么您最终将针对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课程创造了一个与众不同的世界。