将数据缓冲区从 C++ 传递到 LabVIEW
Passing data buffers from C++ to LabVIEW
我正在尝试创建一个 LabVIEW DLL 并从 C++ 程序中调用它,但我遇到了数据传递问题。
我最近买的一台科学相机自带LabVIEW SDK,除此之外别无其他。 SDK附带的示例程序主要是一个while循环,围绕两个函数,ReadData和DecodeData。
- ReadData从USB采集数据(使用VISA读取),一次调用得到的数据包含几个完整的数据块和一个不完整的传入块。
- DecodeData 被多次调用以处理所有完整的块(它从缓冲区中删除已处理的数据)。处理完所有完整块后,剩余数据(传入块的开头)将传递给 ReadData,后者将在缓冲区末尾连接其新数据。
完整示例代码:
读取数据的详细信息:
解码数据详情:
在用LabVIEW编写的示例程序中,一切正常。问题是当我在 DLL 中导出这些函数时。两个函数的内存缓冲区、输入和输出都是字符数组。在 ReadData 之后,我的 C++ 程序正确地获取了一个包含数据的缓冲区,包括空字节。
问题是当我在 DecodeData 中注入这个缓冲区时,LabVIEW 似乎只考虑第一个空字节之前的字节...我猜 char[] 输入只是作为 null-terminated 字符串,其余数据被丢弃。
我尝试添加数据转换器(输出端为“字符串到字节数组”,输入端为“字节数组到字符串”),但转换函数也会丢弃第一个空字符后的数据。
我可以将 sdk 中的 .vi 修改为只处理字节数组而不处理字符串,但它使用了很多字符处理函数,我更愿意保留它原样。
如何在不丢失部分数据的情况下将数据缓冲区从 C++ 传递到 LabVIEW DLL?
编辑:这是 C++ 代码。
用 LabVIEW DLL 导出的头文件:
int32_t __cdecl CORE_S_Read_data_from_USB(char VISARefIn[],
Enum1 blockToProcessPrevCycle, uint32_t bytesToProcessPrevCycle,
uint8_t inBytesRead[], uint32_t *BytesReceived, LVBoolean *DataReception,
uint8_t outBytesRead[], Enum1 *blockToProcess, uint32_t *bytesToProcess,
int32_t longueur, int32_t longueur2);
void __cdecl CORE_S_Decode_data(uint8_t inBytesRead[],
LVBoolean LUXELL256TypeB, uint32_t bytesToProcess, Enum1 blockToProcess,
Cluster2 *PrevHeader, LVBoolean *FrameCompleto,
uint32_t *bytesToProcessNextCycle, Enum1 *blockToProcessNextCycle,
Cluster2 *HeaderOut, uint8_t outBytesRead[], Int16Array *InfraredImage,
Cluster2 *Header, int32_t longueur, int32_t longueur2, int32_t longueur3);
我的 C++ 源代码中的用法:
while (...)
{
// Append new data in uiBytesRead
ret = CORE_S_Read_data_from_USB(VISARef, blockToProcess, bytesToProcess, uiBytesRead, &BytesReceived,
&DataReception, uiBytesRead, &blockToProcess, &bytesToProcess, BUFFER_SIZE, BUFFER_SIZE);
if (DataReception == 0)
continue;
bool FrameCompleto = true;
while (FrameCompleto)
{
// Removes one frame of uiBytesRead per call
CORE_S_Decode_data(uiBytesRead, LUXELL256TypeB, 0, blockToProcess, &Header, &FrameCompleto, &bytesToProcess, &blockToProcess, &Header,
uiBytesRead, &InfraredImage, &Header, BUFFER_SIZE, BUFFER_SIZE, BUFFER_SIZE);
}
}
在这种特定情况下回答起来有点棘手,但假设问题是缓冲区数据中的 NULL 值导致问题,那么可能值得考虑使用 String Handle 的选项您正在导出的 VI 的 String-Type 控件和显示控件的指针。
可以在配置 DLL 构建的“定义 VI 原型”阶段选择此选项
LabVIEW 在内部将字符串类型管理为字符串长度的整数和无符号字符数组,因此使用什么字符无关紧要。对于与外部代码的接口,LabVIEW 的 extcode.h
header 定义了一个 LStrHandle
如下:
typedef struct {
int32 cnt; /* number of bytes that follow */
uChar str[1]; /* cnt bytes */
} LStr, *LStrPtr, **LStrHandle;
因此字符串句柄指针的类型为 *LStrHandle
。
extcode.h
提供了宏 LHStrBuf(LStrHandle)
和 LHStrLen(LStrHandle)
,当您想要读取或更新字符串内容和长度时,它们可以简化对字符串句柄指针的取消引用。另外,请注意 NULL 句柄可用于表示空字符串,因此不要假设该句柄未经检查就有效。
在创建或调整字符串句柄指针的大小以传递给函数时,值得注意的是 LStr
与 LabVIEW-array 具有完全相同的 in-memory 表示形式,因此函数 NumericArrayResize
和 typeCode
uB
可以 create/resize 一个足够大的缓冲区来存储字符串和 length-integer.
为长度为 required_string_length
的字符串创建新的字符串句柄指针的示例是通过传递 NumericArrayResize
一个句柄为 NULL 的句柄指针来实现的。
LStrHandle* new_string_handle_pointer;
// assign NULL value to handle
*new_string_handle_pointer=0;
err = NumericArrayResize(uB, 1, (UHandle *)new_string_handle_pointer, required_string_length);
// new_string_handle_pointer will now reference the new LStrHandle
更新字符串句柄中的字符串值时,请记住将字符串的字符写入 uChar 数组 和 以更新大小整数。从性能的角度来看,将字符串句柄更新为较短的字符串时可能不值得缩小字符串句柄,但如果您知道要写入的字符串将超过它可以容纳的长度,则需要调整它的大小。
您应该清理从 LabVIEW 或 LabVIEW-based DLL 传递给您的任何句柄,因此一旦您完成处理,就在 handle-pointer 的句柄上调用 DSDisposeHandle
参考资料。
有关LabVIEW内存管理器功能的更多信息,请阅读this guide。
我正在尝试创建一个 LabVIEW DLL 并从 C++ 程序中调用它,但我遇到了数据传递问题。
我最近买的一台科学相机自带LabVIEW SDK,除此之外别无其他。 SDK附带的示例程序主要是一个while循环,围绕两个函数,ReadData和DecodeData。
- ReadData从USB采集数据(使用VISA读取),一次调用得到的数据包含几个完整的数据块和一个不完整的传入块。
- DecodeData 被多次调用以处理所有完整的块(它从缓冲区中删除已处理的数据)。处理完所有完整块后,剩余数据(传入块的开头)将传递给 ReadData,后者将在缓冲区末尾连接其新数据。
完整示例代码:
读取数据的详细信息:
解码数据详情:
在用LabVIEW编写的示例程序中,一切正常。问题是当我在 DLL 中导出这些函数时。两个函数的内存缓冲区、输入和输出都是字符数组。在 ReadData 之后,我的 C++ 程序正确地获取了一个包含数据的缓冲区,包括空字节。
问题是当我在 DecodeData 中注入这个缓冲区时,LabVIEW 似乎只考虑第一个空字节之前的字节...我猜 char[] 输入只是作为 null-terminated 字符串,其余数据被丢弃。
我尝试添加数据转换器(输出端为“字符串到字节数组”,输入端为“字节数组到字符串”),但转换函数也会丢弃第一个空字符后的数据。
我可以将 sdk 中的 .vi 修改为只处理字节数组而不处理字符串,但它使用了很多字符处理函数,我更愿意保留它原样。
如何在不丢失部分数据的情况下将数据缓冲区从 C++ 传递到 LabVIEW DLL?
编辑:这是 C++ 代码。
用 LabVIEW DLL 导出的头文件:
int32_t __cdecl CORE_S_Read_data_from_USB(char VISARefIn[],
Enum1 blockToProcessPrevCycle, uint32_t bytesToProcessPrevCycle,
uint8_t inBytesRead[], uint32_t *BytesReceived, LVBoolean *DataReception,
uint8_t outBytesRead[], Enum1 *blockToProcess, uint32_t *bytesToProcess,
int32_t longueur, int32_t longueur2);
void __cdecl CORE_S_Decode_data(uint8_t inBytesRead[],
LVBoolean LUXELL256TypeB, uint32_t bytesToProcess, Enum1 blockToProcess,
Cluster2 *PrevHeader, LVBoolean *FrameCompleto,
uint32_t *bytesToProcessNextCycle, Enum1 *blockToProcessNextCycle,
Cluster2 *HeaderOut, uint8_t outBytesRead[], Int16Array *InfraredImage,
Cluster2 *Header, int32_t longueur, int32_t longueur2, int32_t longueur3);
我的 C++ 源代码中的用法:
while (...)
{
// Append new data in uiBytesRead
ret = CORE_S_Read_data_from_USB(VISARef, blockToProcess, bytesToProcess, uiBytesRead, &BytesReceived,
&DataReception, uiBytesRead, &blockToProcess, &bytesToProcess, BUFFER_SIZE, BUFFER_SIZE);
if (DataReception == 0)
continue;
bool FrameCompleto = true;
while (FrameCompleto)
{
// Removes one frame of uiBytesRead per call
CORE_S_Decode_data(uiBytesRead, LUXELL256TypeB, 0, blockToProcess, &Header, &FrameCompleto, &bytesToProcess, &blockToProcess, &Header,
uiBytesRead, &InfraredImage, &Header, BUFFER_SIZE, BUFFER_SIZE, BUFFER_SIZE);
}
}
在这种特定情况下回答起来有点棘手,但假设问题是缓冲区数据中的 NULL 值导致问题,那么可能值得考虑使用 String Handle 的选项您正在导出的 VI 的 String-Type 控件和显示控件的指针。
可以在配置 DLL 构建的“定义 VI 原型”阶段选择此选项
LabVIEW 在内部将字符串类型管理为字符串长度的整数和无符号字符数组,因此使用什么字符无关紧要。对于与外部代码的接口,LabVIEW 的 extcode.h
header 定义了一个 LStrHandle
如下:
typedef struct {
int32 cnt; /* number of bytes that follow */
uChar str[1]; /* cnt bytes */
} LStr, *LStrPtr, **LStrHandle;
因此字符串句柄指针的类型为 *LStrHandle
。
extcode.h
提供了宏 LHStrBuf(LStrHandle)
和 LHStrLen(LStrHandle)
,当您想要读取或更新字符串内容和长度时,它们可以简化对字符串句柄指针的取消引用。另外,请注意 NULL 句柄可用于表示空字符串,因此不要假设该句柄未经检查就有效。
在创建或调整字符串句柄指针的大小以传递给函数时,值得注意的是 LStr
与 LabVIEW-array 具有完全相同的 in-memory 表示形式,因此函数 NumericArrayResize
和 typeCode
uB
可以 create/resize 一个足够大的缓冲区来存储字符串和 length-integer.
为长度为 required_string_length
的字符串创建新的字符串句柄指针的示例是通过传递 NumericArrayResize
一个句柄为 NULL 的句柄指针来实现的。
LStrHandle* new_string_handle_pointer;
// assign NULL value to handle
*new_string_handle_pointer=0;
err = NumericArrayResize(uB, 1, (UHandle *)new_string_handle_pointer, required_string_length);
// new_string_handle_pointer will now reference the new LStrHandle
更新字符串句柄中的字符串值时,请记住将字符串的字符写入 uChar 数组 和 以更新大小整数。从性能的角度来看,将字符串句柄更新为较短的字符串时可能不值得缩小字符串句柄,但如果您知道要写入的字符串将超过它可以容纳的长度,则需要调整它的大小。
您应该清理从 LabVIEW 或 LabVIEW-based DLL 传递给您的任何句柄,因此一旦您完成处理,就在 handle-pointer 的句柄上调用 DSDisposeHandle
参考资料。
有关LabVIEW内存管理器功能的更多信息,请阅读this guide。