解决方案简介
LabMax-Pro SSIM 仪表与 PowerMax-Pro 在 C# 中的高速 OEM 集成指南
引言
客户正越来越多地使用LabMax-Pro SSIM仪表进行深度集成,编写定制代码以传输高速数据。此类仪表接口的实现不仅需要我们的技术指导,更要求客户具备高水平的专业能力。本文档旨在提供成功实施所需的工具。
确定集成所需的工具。
我们为 LabMax-Pro SSIM 仪表提供的高意 连接(CMC)”软件能够以 20kHz / 50us 的采样率读取并实时传输高速数据,并将其导出到文件中。 该过程较为复杂,需要用户/程序员具备丰富的经验才能成功。本文档中提供的数据曾帮助多家客户成功实现该功能。所需资源……
1. 客户方需配备一名经验丰富的程序员或软件工程师来编写接口程序。
2. LabMax-Pro SSIM 用户手册(特别是“主机接口”一章)。
3. 已安装并运行 CMC 应用程序,以便查看 I/O 日志功能。
a. 显示了适用于类似操作设置的 SCPI 命令。
b. 有些命令虽然并非必需,但会被发送多次。
4. 本技术说明。
流程概述——重点内容。
在对 SSIM 仪表进行编程时,整个过程中需注意以下几点。请参考以下操作步骤。
1. 数据采集可通过以下两种方式进行:
a. 计数停止——发送 N 个样本后自动停止
· 强烈建议使用“停止计数”功能
b. 持续运行——直至主机明确下达停止指令
· 由于计费器在停止前会发送额外的记录,因此情况更为复杂
2. 数据采集可通过以下两种方式进行编码:
a. 二进制
· 强烈建议使用二进制文件!
b. ASCII
3. 这两种编码方式都支持传输多种数据
a. 优先考虑速度(不要发送不需要的数据) vs. 为了以防万一而收集更多数据
b. 选项:
- 主要(测量)
- X、Y 偏移量(仅来自 LM 传感器)
- 数据采集标志(始终建议使用)
- 序列ID(仅限能量)
- 脉冲周期(仅适用于能量)
c. 详见SCPI参考资料
d. SSIM 不会发送数据样本的时间戳信息
时间戳通常由接收端通过将变量值增加采样率来重建。
4. 握手
a. 在与 SCPI 交互时非常有用,如果该功能关闭,则无法查看响应
b. 但通常禁用流式数据传输
c. 作为执行“Start”命令前的最后一条命令发出
数据采集概述。
数据采集(DAQ)可以使用ASCII或二进制格式进行——按样本迭代:
- 对于二进制数据:只需读取相应数量的字节,并根据您指定的编码标志将其打包到目标结构体中。无需进行数值转换。
- 二进制流USB数据是以块为单位传输的,通常单次读取无法获取所需的所有字节。您需要反复读取字节,直到收集到所需数量为止。
- 建议使用二进制,因为它速度更快、占用空间更小,且无需进行耗时的文本转数字转换
- 对于 ASCII:读取一行文本,并根据您指定的编码标志进行解析
我们的 CMC 软件在单独的线程中进行数据采集,因此当该线程等待读取数据时,用户界面不会被阻塞。线程机制是一个高级话题,超出了本文的讨论范围。
发送 SCPI 配置命令,以针对您的具体测量需求进行设置
1. 首先,你至少需要(顺序不限)
- 发送CONF:MEAS:MODE W以选择功率测量,或发送J以选择能量测量
- 发送CONF:READ:MODE BINARY以指定二进制模式(推荐)
- 发送CONF:ITEM PRI,FLAG以指定仅需测量数据和标志数据。二进制数据将包含一个
- 4字节浮点数(IEEE浮点数)测量值,后跟
- 2字节无符号整数标志字
- 还有许多其他命令可能适用于您的具体情况
2. 最后,请按以下顺序操作:
i. 发送SYST:COMM:HAND OFF命令以关闭握手协议
ii. 清空输入缓冲区——你需要丢弃在发送上述命令期间积累的任何握手信息、错误消息或其他多余的输入数据,以便首次读取时能从第一个数据记录的开头开始。具体操作方式很大程度上取决于你的编译器、运行时系统和操作系统。
iii. 发送START 100
- 指示仪表开始采集数据,并在采集到100条记录后停止
- 请指定记录数,以便计数器发送固定数量的记录。建议最初仅使用少量记录进行测试和调试。
然后开始读取数据
1. 每次处理一条记录
2. 直到计数用尽
注意事项
每个数据记录通常包含一个标志字。每次采样时,应检查其中若干个测量标志:
1. OverTemp = 0x80 – 表示传感器过热,应终止数据采集,且无论如何都应报告该错误
2. 终止 = 0x8000 – 表示仪表检测到致命错误(例如传感器断开),必须单方面终止采集;此后将不再有数据传出
3. MissingSamples = 0x100 – 主机未能及时从仪表读取数据,导致仪表的内部缓冲区溢出,数据流中部分数据记录因此被省略。数据采集可继续进行,但该标志表示数据存在不连续性
C# 接口代码示例。
客户最常提出的问题是:“如何捕获这路高速数据流,并根据自身需求对其进行处理?”下一节将介绍用于传输高速数据所需的数据采集循环。
ThreadBody– 数据采集的内部循环
ThreadBody 的核心是一个 while 循环,该循环会反复调用 ReadOneRecord 来获取每个数据样本。任何错误(异常)都会导致函数退出。它还实现了可选的StopOnCount功能。
在循环的第一次迭代中,ReadOneRecord 调用是发送 start 命令后读取的第一条数据。循环将持续进行,直到所有数据均已加载并 并添加到 CaptureBuffer。
`data` 是一个全局静态临时数组,其容量足以处理任何记录。
IsStopping是一个全局标志,用于指示所有采集代码提前终止。
TerminatedByMeter 测试 ( record.Flags & MeasurementFlags.Terminated ) != 0 时,
其中 Terminated = 0x8000 // 计量表声明单方面终止
蓝色标出的部分可以忽略。
protected override void ThreadBody()
{
try
{
BoostThreadPriority();
// energy mode has to actually wait a while before IsWaiting comes true
OnDAQ_StateChanged( DAQ_State.Start );
// fill Data array with the required number of bytes
while( !正在停止 && ReadOneRecord( 数据))
// fill Data array with the required number of bytes
while( !IsStopping && ReadOneRecord( Data ) )
{
// if we get data while waiting, then we're no longer waiting
if( IsWaiting )
{
IsWaiting = false;
OnDAQ_StateChanged( DAQ_State.Triggered );
}
// copy the binary 数据 转换为数据记录
#if PREALLOCATE_DATA_RECORDS
Record.Read( 数据 );
#else
Record = new DataRecordSingle( 数据 );
#endif
#if TRACE_DAQ_HS_Read && DEBUG
TraceLogger.TraceRead( $"DAQ.HS.Read: {Record.ToString_AsHex()} // {Record.ToStringEx()}" );
#endif
// if meter sets abort flag, then we have to stop
// (and this record should NOT get added to Capture buffer)
if( TerminatedByMeter( Record ) )
break;
// Add the data Record to the CaptureBuffer, where a timestamp is assigned
CaptureBuffer.TimestampAndAdd( Record );
TraceData( "Add[ {0} ]: {1}", RecordsRead, Record.ToString() );
计数++;
if( StopOnCount && Count >= Capacity )
换行;
}
}
catch( Exception ex )
{
// all uncaught exceptions are reported and terminate the thread
ReportException( ex );
}
// all exits from try statement need to exit successfully
OnThreadExits();
}
ReadOneRecord——数据采集的内循环
ReadOneRecord会反复调用 Read 函数,将所需数量的字节加载到目标数据数组中。
程序的核心是前两个 while 循环。其余大部分代码涉及一些异常情况,这些情况在您的系统上可能不会发生。
如果 ReadOneRecord 因任何原因无法获取所有数据,它将返回 false,从而终止 ThreadBody 循环。还有许多其他异常情况也可能导致循环终止。
复杂性源于COM端口的读取函数并不一定返回所有请求的数据。如果返回的数据少于所需量,内层循环会反复获取数据中越来越小的剩余部分。
Channel.Read实质上是.Net SerialPort的读取函数。
唯一的区别在于,整个目标数组会被作为参数传入,索引指定了新数据应放置的位置,而计数参数则表示请求的字节数。Read 函数返回实际读取的字节数,该数值可能少于请求的字节数。
正在停止 这是一个全局标志,用于指示所有采集代码提前终止。
protected virtual bool ReadOneRecord( byte[] data )
{
int count = data.Length;
int index = 0;
SampleTime.Start();
// outer loop repeatedly attempts reading 1 record,
// restarting an incomplete inner loop after a timeout
while( count > 0 && !正在停止 )
{
try
{
// 内层循环读取一条记录,但
// 可能在读取完成前就超时了
while( count > 0 && !正在停止 )
{
int actual = Channel.Read( data, index, count );
if( actual <= 0 )
{
// eof "cannot happen" thus fatal error
ReportUnexpectedEOF();
return false;
}
count -= actual;
index += actual;
}
// if we get here, we have a complete record
// stats only for complete records
BytesRead += data.Length;
RecordsRead++;
}
// in Energy mode, we sometimes wait a long time for an energy reading,
// so timeout while reading is normal
catch( TimeoutException )
{
// Stop while Waiting terminates the operation
if( IsStopping )
return false; // terminate DAQ
// else if timeout when we're not stopping...
// timeouts are not allowed in power mode
if( !OperatingMode_IsTrueEnergy
&& SampleTime.ElapsedMilliseconds > PowerModeMaxElapsed_ms )
{
ReportUnexpectedTimeout();
return false; // terminate DAQ
}
// signal TriggerWait first time we start waiting again
if( !IsWaiting )
{
IsWaiting = true;
OnDAQ_StateChanged( DAQ_State.TriggerWait );
}
// ignore timeouts if not stopping or PowerMode timeout
continue;
}
finally
{
SampleTime.Stop();
}
} // outer while loop
return ( count == 0 ); // 计数不为零表示失败
}
联系高意
如需帮助或了解更多信息,请访问我们的支持 页面。例如,如果您找不到传感器的校准证书,我们可以为您寄送一份替换件。
如需安排保修服务或年度重新校准,请先联系 所在高意 ,以获取退货授权(RMA)编号。请使用您保留的运输箱和包装材料,将传感器安全地寄回工厂,寄送地址如下:
高意
收件人:RMA #
27650 SW 95th Ave.
俄勒冈州威尔逊维尔市 97070