Rx.Net:在处理订阅时执行异步副作用

时间:2016-10-04 19:21:12

标签: c# system.reactive reactive-programming

我有一个通过串口与计算机通信的设备。发送“START”命令后,设备会以确认响应,并开始监视某些外部活动。然后,它根据外部活动asynchonousely传输串行端口上的一些消息。当设备收到“STOP”命令时,它会响应确认,然后停止发送更多消息(代表外部活动)。

我已经使用冷观察器实现了启动/停止命令,这些命令执行副作用(在串行端口上发送命令),如果在SerialPort上收到确认,则发出单个Unit.Default。我想构造一个IObservable,它发出与外部活动相对应的消息,并在订阅时执行“START”副作用,并在处理订阅时执行“STOP”副作用。 “START”很简单,我只需要做一个“SelectMany”,但我不知道如何执行“STOP”。

  class MonitoringDevice
{
    private SerialPort _sp;
    private IObservable<byte> _receivedBytes;

    public IObservable<ExternalActivity> ActivityStream { get; }

    public  MonitoringDevice()
    {
        _sp = new SerialPort("COM1");
        _receivedBytes = Observable
                         .FromEventPattern<SerialDataReceivedEventHandler, SerialDataReceivedEventArgs>(
                          h =>
                          {
                              _sp.DiscardInBuffer();
                              _sp.DataReceived += h;
                          },
                          h =>
                          {
                              _sp.DataReceived -= h;
                          })
                         .SelectMany(x =>
                         {

                             byte[] buffer = new byte[1024];
                             var ret = new List<byte>();
                             int bytesRead = 0;
                             do
                             {
                                 bytesRead = _sp.Read(buffer, 0, buffer.Length);
                                 ret.AddRange(buffer.Take(bytesRead));
                             } while ((bytesRead >= buffer.Length));
                             return ret;

                         })
                         .Publish()
                         .RefCount();


        ActivityStream = StartMonitoringAsync()
                         .SelectMany( _receivedBytes.ToActivity());
                         // we need to execute StopMonitoringAsync 
                         // when a subscription to ActivityStream is disposed

        _sp.Open();
    }



    private IObservable<Unit> StartMonitoringAsync()
    {
        return Observable
               .Create<Unit>(
                obs =>
                {
                    _sp.Write("START");
                    return _receivedBytes
                           .ToAcknowledge()
                           .FirstAsync()
                           .Timeout(TimeSpan.FromMilliseconds(1000))
                           .Subscribe(obs);
                });
    }


    private IObservable<Unit> StopMonitoringAsync()
    {
        return Observable
               .Create<Unit>(
                obs =>
                {
                    _sp.Write("STOP");
                    return _receivedBytes
                           .ToAcknowledge()
                           .FirstAsync()
                           .Timeout(TimeSpan.FromMilliseconds(1000))
                           .Subscribe(obs);
                });
    }


}

ExternalActivity只是一个POCO。

ToAcknowledge是一种扩展方法,返回IObservable,当设备发送确认时会发出Unit.Default。 - 这是按预期工作的;

ToActivity是一种返回IObservable的扩展方法,用于解析收到的串行数据并发出ExternalActivity个对象。 - 这是按预期工作的;

修改:添加了ToAcknowledgeToActivity扩展方法的实施。

public static IObservable<Unit> ToAcknowledge(this IObservable<byte> source)
    {
        return source.Buffer(3, 1)
               .Where(bfr => bfr.SequenceEqual(new byte[] { 65, 67, 75 })) // ACK
               .Select(x => Unit.Default);

    }

    public static IObservable<ExternalActivity> ToActivity(this IObservable<byte> source)
    {
        return source
               .Publish(ps => ps.Buffer(ps.Where(x => x == 1),             // SOH
                                           bo => ps.Where(x => x == 4)))   // EOT
               .Select(bfr => bfr.Take(bfr.Count - 1).Skip(1))
               .Where(bfr => bfr.Count() == 12)
               .Select(bfr =>
               {
                   var timestamp = BitConverter.ToInt64(bfr.Take(8).ToArray(), 0);
                   var id = BitConverter.ToInt32(bfr.ToArray(), 8);
                   return new ExternalActivity(timestamp, id);
               });           
    }

1 个答案:

答案 0 :(得分:0)

您可以将StartAsync修改为:

private IObservable<Unit> StartAsync(Action unsubscribe)
{
    return
        Observable
            .Create<Unit>(o =>
            {
                var subscription =
                    Observable
                        .Timer(TimeSpan.FromSeconds(1))
                        .Select(_=> Unit.Default)
                        .Subscribe(o);
                return new CompositeDisposable(
                    subscription,
                    Disposable.Create(unsubscribe));
            });;
}

然后你可以注入你喜欢的任何Action unsubscribe

尝试使用此代码进行测试:

var subscription =
    StartAsync(() => Console.WriteLine("Done"))
    .Subscribe();

Thread.Sleep(3000);

subscription.Dispose();

你会看到&#34;完成&#34; 3秒后写入控制台。