我 能否 使用 反应 式 扩展 来 控制 仪器 测试 周期 的 时间
本文关键字:测试 仪器 周期 时间 控制 使用 能否 反应 扩展 | 更新日期: 2023-09-27 18:30:00
这是我试图解决的问题...
我正在编写一个控制一台测试设备的程序。程序每秒通过串行接口轮询仪器,并返回一串测试数据。
连接速度为 9600 波特,仪器一次仅发送约 100 个字符,因此性能不是问题。
其中一个要求是"循环"模式,其工作原理如下:
-
用户设置三个计时器间隔 - 预测试时间、测试时间和测试间时间 - 然后单击 UI 上的"开始"按钮。
-
程序命令仪器开始测试周期,并显示仪器接收的数据,直到预测试时间到期。
-
然后,程序显示 AND 记录数据,直到测试时间到期。
-
然后,程序将显示数据,直到测试间时间到期。
-
重复步骤 2 - 4,直到用户单击 UI 上的"停止"按钮。
过去,我为这类问题编写了简单的状态机。但是对于这个项目(并在SO Rx专家的帮助下(,我已经能够使用Rx来解析串行数据,并且对生成的代码非常满意。
我的直觉告诉我,Rx也可以很好地处理这个问题,但根本不清楚如何进行。
那么,我的直觉是正确的,还是你会建议一种不同的方法?
如果 Rx 很合适,有人可以提供一些快速而肮脏的示例代码来演示解决方案吗?
编辑。。。根据谜团:
下面是读取串行数据的代码。简而言之,它将串行数据流转换为单独的数据包字符串,例如"A0"、"C00004"、"H0501100"等。这些被传递给 ProcessPacket 方法,该方法将数据包解释为仪器状态、压力等。
/// <summary>
/// Create a Reactive Extensions subscription to the selected serial port
/// </summary>
private IDisposable SetupSerialDataSubscription()
{
// Create Observable stream of chars from serial port's DataRecieved event
var serialData = Observable
.FromEventPattern<SerialDataReceivedEventArgs>(_serialPort, "DataReceived")
// Collapse groups of chars into stream of individual chars
.SelectMany(_ =>
{
int bytesToRead = _serialPort.BytesToRead;
byte[] bytes = new byte[bytesToRead];
int nbrBytesRead = _serialPort.Read(bytes, 0, bytesToRead);
// Empty array if no bytes were actually read
if (nbrBytesRead == 0)
return new char[0];
var chars = Encoding.ASCII.GetChars(bytes);
return chars;
})
// Strip out null chars, which can cause trouble elsewhere (e.g. Console.WriteLine)
.SkipWhile(c => c == ''0');
// Emits packets, one string per packet, e.g. "A0", "C00004", "H0501100", etc
var packets = serialData
.Scan(string.Empty, (prev, cur) => char.IsLetter(cur) ? cur.ToString() : prev + cur)
.Where(IsCompletePacket);
// Process each packet, on the UI thread
var packetsSubscription = packets
.ObserveOn(this)
.Subscribe(packet => ProcessPacket(packet) );
return packetsSubscription;
}
你可以这样做。使用帮助程序方法发出无限IEnumerable
,该提供记录信号(真/假(和时间跨度:
public IEnumerable<Tuple<TimeSpan, bool>> GetTestPeriods()
{
TimeSpan preTestTime = TimeSpan.FromSeconds(5);
TimeSpan testTime = TimeSpan.FromSeconds(3);
TimeSpan interTestTime = TimeSpan.FromSeconds(2);
yield return Tuple.Create(preTestTime, false);
while(true)
{
yield return Tuple.Create(testTime, true);
yield return Tuple.Create(interTestTime, false);
}
}
这可用于创建在适当时间输出记录信号的循环流:
// cycle outputs true at start of recording period
// false at start of a display period
var cycle = GetTestPeriods()
.Select(s => Observable.Return(s.Item2)
.Concat(Observable.Empty<bool>().Delay(s.Item1)))
.Concat();
现在我们可以制作一些随机数据:
// make some random data
var random = new Random();
var dummyData = Observable.Interval(TimeSpan.FromSeconds(1))
.Select(_ => random.Next(10));
设置一个主题以发出停止信号(您可以使用FromEventPattern
来挂钩按钮单击此处(:
var stop = new Subject<Unit>();
然后将数据与记录信号合并,直到发出停止信号。这会将每个数据点与指示是否显示或记录的布尔值配对:
var output = cycle.CombineLatest(dummyData, (c,d) => Tuple.Create(c,d))
.TakeUntil(stop);
您可以使用Where
筛选器发布和订阅几个流,以获取要记录的数据与要显示的数据分开。下面是一个测试,它为流添加时间戳并将数据转储出去:
// timestamp data and "record" or "display" it.
output.Timestamp()
.Subscribe(x => {
if(x.Value.Item1)
Console.WriteLine(x.Timestamp + ": Recording " + x.Value.Item2);
else
Console.WriteLine(x.Timestamp + ": Displaying " + x.Value.Item2);
},
() => Console.WriteLine("Stopped"));
最后是一些发出停止信号的代码:
Console.ReadLine();
stop.OnNext(Unit.Default);