【问题标题】:Passing data buffers from C++ to LabVIEW将数据缓冲区从 C++ 传递到 LabVIEW
【发布时间】:2021-05-07 21:47:51
【问题描述】:

我正在尝试创建一个 LabVIEW DLL 并从 C++ 程序中调用它,但我遇到了数据传递问题。

我最近购买的一台科学相机带有 LabVIEW SDK,仅此而已。 SDK提供的示例程序主要是围绕ReadData和DecodeData两个函数的while循环。

  • ReadData从USB采集数据(使用VISA读取),一次调用获取的数据包含几个完整的数据块和一个不完整的传入块。
  • DecodeData 被多次调用以处理所有完整的块(它从缓冲区中删除处理过的数据)。处理完所有完整块后,剩余数据(传入块的开头)将传递给 ReadData,后者将在缓冲区末尾连接其新数据。

完整示例代码:

ReadData 的详细信息:

DecodeData 的详细信息:

在用 LabVIEW 编写的示例程序中,一切正常。问题是当我在 DLL 中导出这些函数时。这两个函数的内存缓冲区、输入和输出都是字符数组。在 ReadData 之后,我的 C++ 程序正确地获得了一个包含数据的缓冲区,包括空字节。

问题是当我在 DecodeData 中注入这个缓冲区时,LabVIEW 似乎只考虑第一个空字节之前的字节......我猜 char[] 输入只是作为一个空终止字符串处理,并且剩下的数据就被丢弃了。

我尝试添加数据转换器(在输出端添加“字符串到字节数组”,在输入端添加“字节数组到字符串”),但转换函数也会丢弃第一个空字符后的数据。

我可以将 .vi 从 sdk 修改为只处理字节数组而不是字符串,但它使用了很多字符处理函数,我宁愿保持原样。

如何在不丢失部分数据的情况下将数据缓冲区从 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);
    }
}

【问题讨论】:

  • 你能告诉我们你的代码吗?
  • @JanSchultke 我加了截图,够了吗?
  • 很难说这么少的信息有什么问题。我会使用可靠的二进制序列化/反序列化库,如 ProtoBuffer 或 FlatBuffer(第一个更容易,第二个更快,旨在实时数据传输)。另外,不要相信 char*。 C/C++ 中的字符串意味着在第一个空字节处终止。使用 uint8* 所以,至少,如果你用它调用一个字符串函数,你会得到一些编译器错误/警告。 C++ 流也有同样的问题。永远不要相信二进制数据的字符串流,它们非常具有误导性。
  • 感谢您的建议。我不以任何方式处理 C++ 中的缓冲区,我只是从 readData 中获取指针并将其传递给 decodeData。我将尝试在 LabVIEW 模块中添加 uint8 转换器。
  • 另一种方法是使用 Kaitai 并尝试重写相同的语法。 Kaitai 是一个为二进制数据创建解析器的工具包,它使用 Yaml 来设计语法,它可以为许多不同的语言生成解析器。

标签: c++ labview


【解决方案1】:

在这种特定情况下回答有点棘手,但假设问题是缓冲区数据中的 NULL 值导致了问题,那么可能值得考虑使用 字符串句柄指针 用于您正在导出的 VI 的字符串类型控件和显示控件。

可以在配置 DLL 构建的“定义 VI 原型”阶段选择此选项

LabVIEW将字符串类型作为字符串长度的整数和无符号字符数组在内部进行管理,因此无论使用什么字符都无关紧要。为了与外部代码交互,LabVIEW 的extcode.h 头文件定义了一个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 数组具有完全相同的内存表示,因此函数NumericArrayResizetypeCode @987654336 @ 可以创建/调整足够大的缓冲区来存储字符串和长度整数。

为长度为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 的 DLL 传递给您的任何句柄,因此一旦完成处理,请在句柄指针引用的句柄上调用 DSDisposeHandle

有关LabVIEW内存管理器功能的更多信息,请阅读this guide

【讨论】:

  • 成功了!感谢您的详细回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-14
  • 1970-01-01
  • 2019-12-13
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多