Jump to content

How to deal with clusters containing clusters?


Recommended Posts

Hi

 

When I call the DLL, there is a structure:

struct Signal
{
uint32 nStartBit; 
uint32 nLen;
double nFactor; 
double nOffset;
double nMin; 
double nMax;
double nValue;
uint64 nRawValue;
bool is_signed; 
char unit[11];
char strName[66];
char strComment[201]; 
};

 

 

There is another  Message structure to the above Signal:

 

struct Message
{
uint32 nSignalCount; 
uint32 nID;
uint8 nExtend; 
uint32 nSize; 
Signal vSignals[513]; 
char strName[66];
char strComment[201]; 
}

 

  The point is  Signal vSignals[513];

 

I've tried to solve it like this,but the program will crash.

 

How to create struct Message ,any good suggestions?  Thanks a lot.

 

qq.png

message.vi

Edited by Fred chen
Link to post
Share on other sites

Hi,

Your issue is related to data structure alignment and padding. See https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member

By default, C/C++ compilers add padding to structs to improve memory alignment. However, LabVIEW does not add padding to clusters. So, in your DLL, the structs' memory layout is probably like this:

struct Signal
{
    uint32 nStartBit;     //   4 bytes
    uint32 nLen;          //   4 bytes
    double nFactor;       //   8 bytes
    double nOffset;       //   8 bytes
    double nMin;          //   8 bytes
    double nMax;          //   8 bytes
    double nValue;        //   8 bytes
    uint64 nRawValue;     //   8 bytes
    bool is_signed;       //   1 byte
    char unit[11];        //  11 bytes
    char strName[66];     //  66 bytes
    char strComment[201]; // 201 bytes
                          //   1 byte (PADDING)
}; // TOTAL: 336 bytes

struct Message
{
    uint32 nSignalCount;  //      4 bytes
    uint32 nID;           //      4 bytes
    uint8 nExtend;        //      1 byte
                          //      3 bytes (PADDING)
    uint32 nSize;         //      4 bytes
    Signal vSignals[513]; // 172368 bytes (=513*336 bytes)
    char strName[66];     //     66 bytes
    char strComment[201]; //    201 bytes
                          //      5 bytes (PADDING)
}; // TOTAL: 172656 bytes

There are two ways you can make your structs and clusters compatible:

  • If you control the DLL source code and you can compile the DLL yourself, then you can update your code to pack the structs.
  • If you cannot compile the DLL yourself or if you don't want to change the DLL, then you can add padding to your LabVIEW clusters.
    • Signal: Add 1 byte (U8) to the end of the cluster
    • Message: Add 3 bytes in between nExtend and nSize. Add 5 bytes to the end of the cluster.


I must say, the Message struct is huge! (>170 KiB)

Edited by JKSH
  • Like 1
Link to post
Share on other sites

Hi,
I think you might want to read this paper. I am afraid that if you do not have access to the dll sources you will not be able to retrieve this data correctly. At the first place I would split up complex data into simpler types, then pass it to dll as separated arguments. That will make things easier in your dll code.

Regards,
Zyga

Link to post
Share on other sites

You're welcome, Fred.

I see on forums.ni.com that your code is a bit different. In particular, your Signal array has 512 elements instead of 513. Which is it? You need to count accurately, or else your program might crash.

Also, nathand posted more important points at forums.ni.com:

  • You must configure the the Array to Cluster node correctly
  • Each Array to Cluster node can only handle up to 256 elements. So, you need to duplicate its output to reach 512/513 elements.
Link to post
Share on other sites
On 1/15/2018 at 9:02 AM, JKSH said:

Hi,

Your issue is related to data structure alignment and padding. See https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member

By default, C/C++ compilers add padding to structs to improve memory alignment. However, LabVIEW does not add padding to clusters. So, in your DLL, the structs' memory layout is probably like this:

That is not quite true. LabVIEW for Windows 32 bit does indeed packed data structs. That is because when LabVIEW for Windows 3.1 was released, there were people wanting to run LabVIEW on computers with 4MB of memory :o, and 8MB of memory was considered a real workstation. Memory padding could make the difference between letting an application run in the limited memory available or crash! When releasing LabVIEW for Windows 95/NT memory was slightly more abundant but for compatibility reasons the packing of data structures was retained.

No such thing happened for LabVIEW for Windows 64 bit and all the other LabVIEW versions such as Mac OSX and Linux 64 bit. LabVIEW on these platforms uses the default padding for these platforms, which is usually 8 byte or the elements own datasize, whatever is smaller.

Quote

struct Signal
{
    uint32 nStartBit;     //   4 bytes
    uint32 nLen;          //   4 bytes
    double nFactor;       //   8 bytes
    double nOffset;       //   8 bytes
    double nMin;          //   8 bytes
    double nMax;          //   8 bytes
    double nValue;        //   8 bytes
    uint64 nRawValue;     //   8 bytes
    bool is_signed;       //   1 byte
    char unit[11];        //  11 bytes
    char strName[66];     //  66 bytes
    char strComment[201]; // 201 bytes
                          //   1 byte (PADDING)
}; // TOTAL: 336 bytes

struct Message
{
    uint32 nSignalCount;  //      4 bytes
    uint32 nID;           //      4 bytes
    uint8 nExtend;        //      1 byte
                          //      3 bytes (PADDING)
    uint32 nSize;         //      4 bytes
    Signal vSignals[513]; // 172368 bytes (=513*336 bytes)
    char strName[66];     //     66 bytes
    char strComment[201]; //    201 bytes
                          //      5 bytes (PADDING)
}; // TOTAL: 172656 bytes

There are two ways you can make your structs and clusters compatible:

The correct thing to use for byte packed data is

Quote

#pragma pack(1)

The following will reset the packing to the default setting, either the compiler default or whatever was given to the compiler as parameter (respectively what the project settings contain).

#pragma pack()
Quote
  • If you cannot compile the DLL yourself or if you don't want to change the DLL, then you can add padding to your LabVIEW clusters.
    • Signal: Add 1 byte (U8) to the end of the cluster
    • Message: Add 3 bytes in between nExtend and nSize. Add 5 bytes to the end of the cluster.


I must say, the Message struct is huge! (>170 KiB)

It sure is and I think trying to create this structure in LabVIEW may seem easier but is  in fact a big pitta. I personally would simply create a byte array with the right size (plus some safety padding at the end and then create a VI to parse the information from the byte array after the DLL call. And if there would be more in terms of complicated data parameters for this DLL even create a wrapper DLL that translates between the C datatypes and more LabVIEW friendly datatypes.

Edited by rolfk
  • Like 1
Link to post
Share on other sites
13 hours ago, rolfk said:

That is not quite true. LabVIEW for Windows 32 bit does indeed packed data structs. That is because when LabVIEW for Windows 3.1 was released, there were people wanting to run LabVIEW on computers with 4MB of memory :o, and 8MB of memory was considered a real workstation. Memory padding could make the difference between letting an application run in the limited memory available or crash! When releasing LabVIEW for Windows 95/NT memory was slightly more abundant but for compatibility reasons the packing of data structures was retained.

No such thing happened for LabVIEW for Windows 64 bit and all the other LabVIEW versions such as Mac OSX and Linux 64 bit. LabVIEW on these platforms uses the default padding for these platforms, which is usually 8 byte or the elements own datasize, whatever is smaller.

TIL!

Thanks for the info, @rolfk

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Similar Content

    • By torekp
      DLL functions or shared variables?  Or something else?
      I have a Labview 2014-64 executable (or I can build a DLL) that runs one piece of equipment, the X-ray.  The other engineer has a large CVI Labwindows 2015 + MS Visual Studio 2012 (C++) executable that runs everything else.  I want the Labview code to be a slave of the CVI code, accepting commands to turn X-ray On or Off, reporting failures, and the like.  Translating the X-ray code into C++ would be possible in principle, but not fun.
      Shared variables look easy, but I'm kinda scared of them.  I would define all the shared variables in my LV code, since I'm more familiar with LV, then use them in both.  There's a thread in here called "Shared Variable Woes" so maybe I should be scared.  In the alternative, I tried building a proof-of-concept DLL in Labview, and calling its functions in CVI/C++, and it works, but it's kinda clunky.  (I'm attaching it below in case you want to play, or advise.)
      Your advice would be appreciated.
      XrayDLL.zip
    • By patufet_99
      To use a controller from LabVIEW I have to use some functions of a DLL.
      For one of the functions, according to the header file .h there is a structure data with parameters of different types that I have to pass to the dll. Some of the parameres are BYTE (1 Byte)  and WORD (2 Bytes).
      When compiling this kind of structure with Visual C++ and looking at it's size with "sizeof()" it seems to me that 4 Bytes variables have to start in a position multiple of 4. For example if there is a BYTE and then a DWORD, the 3 Bytes after the BYTE are ignored and the DWORD starts at Bytes 5 to 8.
      When defining a LabVIEW cluster to match the DLL structure, will LabVIEW do the same? If in my cluster there is a U8 variable and then a U32, will anyway the U8 take 4 bytes?
       
      Thank you.
    • By torekp
      So I created a DLL from a Labview VI that has a 2D array input (and some scalar inputs) and some 2D array outputs.  Labview creates a .h file with these lines

      And then it defines the 2D array arguments to my function as being of this type: "void __cdecl Linear_discrim_4dll(DoubleArray *dataObsFeat, int32_t grpAsz," etc etc.  Trouble is, I have no idea how to fill out this structure so that the DLL can use it.  Say for simplicity I had a 2 by 3 input called dataObsFeat with elements {1,2,3; 11,12,13}; how would I create the object with these values in C or C++ and pass it to the function?  I am a total C++ noob, in case it isn't obvious.
    • By Taylorh140
      After working on the set cluster element by name xnode, it made me realize i could use the same concept to convert a variant array to a cluster. The technique is actually pretty simple, the xnode generates a case structure for each element in a cluster in cluster order, wherein a bundle by name is used to set the value and an unbundle by name is used to get the type, a variant to data is used to convert the data. This has some benefits over some methods, you are not limited to 255 elements, although that is not usually the case some of us are paranoid that clusterosaurus giganticous will be larger than expected. It also has a draw back  that is that when converting from a variant array all the elements must have unique, non-blank names, and this is usually the case. 

      I think this technique (though very brute-force) might be useful for some other things let me know what you guys think.

      VariantArrayToCluster.zip
    • By Taylorh140
      This Xnode allows setting a cluster element by label string without using references. I pulled a great deal of inspiration from Hooovahhs Variant Array to cluster xnode, which i suppose this could be used for, another benefit is its not limited to 255 elements. Its mostly experimental because I haven't used it much. 
       
      SetClusterElement.zip

      SetClusterElementLV2013.zip
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.