我有一个通过串口与计算机通信的设备。发送“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
个对象。 - 这是按预期工作的;
修改:添加了ToAcknowledge
和ToActivity
扩展方法的实施。
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);
});
}
答案 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秒后写入控制台。