Jump to content

How to get a Data from a array of structs in ANSI C into an array of clusters in Labview


Recommended Posts

Hello,

 

i have a problem.

 

I am writing a C code and i have to edit an existing Labview code.

My big problem is with the CFN that i dont know how to get the data from the array of structs with integer (array of 16 structs with 7 integer elements) into an array of cluster of 7 integers.

 

I tried it with struct {..7 integer (each uint16_t)..}table;  struct table[16];

"int32 func (struct *pointer)" and memcpy( pointer, table, sizeof(struct)*16); , and then my labview crashes without any error warning. And i found out via debugger from visual studio, that i think memcpy is saving his data into the memory of labview where it isnt allowed to save it.

 

so my first question is:

1) if i make a numeric constant and bundle 7 types of it to one cluster and use "build array" and make all of this before the CFN, is the memory for this allocated and the value is "0"?

Second:

2) how much bytes are allocated for this? How can i look how much bytes does an array of 16 cluster with each 7 uint16-integers need?

3) What is the difference between Call Library Function node and "call interface node" exactly?

4) If i have another three function in C which copies some data into the C array "table" how can i get the data from this "table" into Labview when i only have the possibility to return void, numeric or string??

 

Because as far as i know i need in Ansi C for the array of 16 structs with 7 uint16_t-Integers  224 Bytes (16*7*2Bytes) . Am i correct by this?

 

I think the most problem is about the memory handling here.

 

About help i am very grateful.

 

Sincerly

Labviewbeginner

Link to comment

Hello,

 

i have a problem.

 

I am writing a C code and i have to edit an existing Labview code.

My big problem is with the CFN that i dont know how to get the data from the array of structs with integer (array of 16 structs with 7 integer elements) into an array of cluster of 7 integers.

 

I tried it with struct {..7 integer (each uint16_t)..}table;  struct table[16];

"int32 func (struct *pointer)" and memcpy( pointer, table, sizeof(struct)*16); , and then my labview crashes without any error warning. And i found out via debugger from visual studio, that i think memcpy is saving his data into the memory of labview where it isnt allowed to save it.

 

so my first question is:

1) if i make a numeric constant and bundle 7 types of it to one cluster and use "build array" and make all of this before the CFN, is the memory for this allocated and the value is "0"?

Second:

2) how much bytes are allocated for this? How can i look how much bytes does an array of 16 cluster with each 7 uint16-integers need?

3) What is the difference between Call Library Function node and "call interface node" exactly?

4) If i have another three function in C which copies some data into the C array "table" how can i get the data from this "table" into Labview when i only have the possibility to return void, numeric or string??

 

Because as far as i know i need in Ansi C for the array of 16 structs with 7 uint16_t-Integers  224 Bytes (16*7*2Bytes) . Am i correct by this?

 

I think the most problem is about the memory handling here.

 

About help i am very grateful.

 

Sincerly

Labviewbeginner

 

The biggest problem is about trying to tackle this without having a fundamental understanding of C and memory handling in general.

 

1) yes

2) 16 times 7 times 2 bytes, plus some LabVIEW dataspace for management of the array but you do not want to pass the LabVIEW array to the C function but the C pointer to the array. Only specially written C code can directly handle native LabVIEW arrays and strings.

3)  Code Interface node is an old legacy technology to incorporate external code in LabVIEW. It is only present in some of the LabVIEW platforms nowadays and completely missing on any 64 bit versions of LabVIEW and also all realtime targets. LabVIEW ceased to ship with any tools that were necessary to create CINs many versions ago.

4) You can return data from a function through its parameters and are not limited to the function return value.

 

Also it is good courtesy to mention that you have crossposted this in the NI forums already. The LabVIEW community is not that big that nobody would notice, but it helps others to see if they should spend any time to provide an answer that was already given elsewhere to the same person.

Link to comment

Thanks for the answer,

 

1) yes you are right. But at least i have to try it :).

2) But the memory usage of the C file should be fitting to the labview vi. Am i right? So i need to know how i can solve this difference between the C 224-Bytes and the labview 224+x Bytes for the dataspace management. How can i do this?

3) ok thanks. So am i right that i need to use the CFN not the CIN?

4) ok if i have the struct and the function like above:

struct{ 7xuint16_t Integer}table;

 

table datatable[16];  (here is C allocating the memory for the array with struct of integers if i am right)

 

int32_t func (struct *pointer)

{

     copy_in_table();  (this copies my data into the table)

 

    pointer = datatable;  (did you mean it like this? that i can give the pointer the data of the table, because C is using arrays as pointers as i read somewhere)

 

 return 1;

}

 

Is this correct? Because if you say yes i dont know why my Labview all the time crashes without any error warning. it just closes itself.

 

Yes sorry for this i will stick to this forum.

 

Thanks.

LabviewBeginner

Link to comment

Thanks for the answer,

 

1) yes you are right. But at least i have to try it :).

2) But the memory usage of the C file should be fitting to the labview vi. Am i right? So i need to know how i can solve this difference between the C 224-Bytes and the labview 224+x Bytes for the dataspace management. How can i do this?

3) ok thanks. So am i right that i need to use the CFN not the CIN?

4) ok if i have the struct and the function like above:

struct{ 7xuint16_t Integer}table;

 

table datatable[16];  (here is C allocating the memory for the array with struct of integers if i am right)

 

int32_t func (struct *pointer)

{

     copy_in_table();  (this copies my data into the table)

 

    pointer = datatable;  (did you mean it like this? that i can give the pointer the data of the table, because C is using arrays as pointers as i read somewhere)

 

 return 1;

}

 

Is this correct? Because if you say yes i dont know why my Labview all the time crashes without any error warning. it just closes itself.

 

Yes sorry for this i will stick to this forum.

 

Thanks.

LabviewBeginner

 

2) You configure the Call Library Node parameter as Adapt to type: Pass C Array Pointer.

4) Just because you assign the pointer value of memory location to a pointer parameter does nothing about making sure the contents of the memory area is copied into that parameter.

table datatable[16];  (here is C allocating the memory for the array with struct of integers if i am right)

int32_t func (struct *pointer)
{
   pointer = datatable;
} 

This only assigns the memory address of datatable to the pointer. However the calling function never sees that pointer nor any data in the pointer. You simply destroy the data area that LabVIEW has allocated and on return of the function LabVIEW can't help but trip over the trap you laid out for it.

 

You would have to do something like

int32_t func (struct **pointer)
{
   *pointer = datatable;
} 
 
but that only works for C as caller (and not like this since the datatypes are not the same). LabVIEW can't deal directly with pointers. 
 
More appropriate would be:
 
int32_t func (struct *pointer)
{
   *pointer = *datatable;
} 
 
But that works in standard C not for all compilers and even in modern C++ it would cause a compilation error since the datatypes are incompatible.
 
The best thing to do would be:
 
int32_t func (struct *pointer)
{
   memcpy(pointer, datatable, sizeof(datatable);
} 
And making sure that you allocated a big enough variable in LabVIEW to be passed to the pointer parameter. If the dataarea from LabVIEW is even one single byte smaller than the length of datatable, very bad things will happen for sure.
  • Like 1
Link to comment

thanks for the big explaining.

 

Yes i tried it also with memcpy(pointer, datatable, sizeof(datatable));

But all the time labview crashes (just close without warning) when i start labview.

 

I made me a test.vi: in this i just want to get the data from c to an array in labview.

I did this:    constant uint16 - bundle 7 times to get a cluster with 7 uint16 - then i made "initialize array" and wired the cluster in the above input and by the dimension size i made  "constant int32" (this data type came automatically) then i wired it to the CFN. In the CFN i made "return type = numeric, signed 32-bit Integer" "arg1 = Adapt to type -> Handles by value".

My C code is this:

 

struct{ 7xuint16_t Integer}table;

 

table datatable[16];

 

int32_t LV_DLL_EXPORT func(struct *pointer)  

{

     copy_to_datatable();

 

    memcpy(pointer, datatable, sizeof(datatable));

 

    return 1;

}

 

in the CFN the prototype is this : "int32_t func(void *arg1);

 

And then i wired the CFN with an indicator array. This array i build like this in the FP : i took array. Then i took a cluster and put this into the array. In this cluster i put 7 numeric constant of the data type uin16.

So now i have an 1D array with one cluster with 7 numeric constants. now i just raise the size of the array that i have 16 clusters in line.

 

But still if i want to start the vi. it crashes without saying anything. So i think that i write or copy something into a position in the memory where something is written so that it should not be copied there.

 

Or can you explain me what i make wrong?

 

thanks anyway for the big help to understand it better.

 

regards.

labviewbeginner

 

 

ps: i put labview screenshots attached

post-52525-0-24500500-1407226349_thumb.j

post-52525-0-97473800-1407226350_thumb.j

post-52525-0-64866800-1407226352_thumb.j

post-52525-0-38227200-1407226355_thumb.j

Edited by labviewbeginner
Link to comment

thanks for the big explaining.

 

Yes i tried it also with memcpy(pointer, datatable, sizeof(datatable));

But all the time labview crashes (just close without warning) when i start labview.

 

I made me a test.vi: in this i just want to get the data from c to an array in labview.

I did this:    constant uint16 - bundle 7 times to get a cluster with 7 uint16 - then i made "initialize array" and wired the cluster in the above input and by the dimension size i made  "constant int32" (this data type came automatically) then i wired it to the CFN. In the CFN i made "return type = numeric, signed 32-bit Integer" "arg1 = Adapt to type -> Handles by value".

My C code is this:

 

struct{ 7xuint16_t Integer}table;

 

table datatable[16];

 

int32_t LV_DLL_EXPORT func(struct *pointer)  

{

     copy_to_datatable();

 

    memcpy(pointer, datatable, sizeof(datatable));

 

    return 1;

}

 

in the CFN the prototype is this : "int32_t func(void *arg1);

 

And then i wired the CFN with an indicator array. This array i build like this in the FP : i took array. Then i took a cluster and put this into the array. In this cluster i put 7 numeric constant of the data type uin16.

So now i have an 1D array with one cluster with 7 numeric constants. now i just raise the size of the array that i have 16 clusters in line.

 

But still if i want to start the vi. it crashes without saying anything. So i think that i write or copy something into a position in the memory where something is written so that it should not be copied there.

 

Or can you explain me what i make wrong?

 

thanks anyway for the big help to understand it better.

 

regards.

labviewbeginner

 

 

ps: i put labview screenshots attached

 

Didn't I say to configure the parameter as: Adapt to Type, Array Data Pointer????

 

Also it would be more correct to write your function prototoype like this:

int32_t LV_DLL_EXPORT func(table *pointer);  

That is not causing your crashes (the previous point about configuring the parameter however sure is), but it simply makes things more clear that way.

 

Also what top secret nuclear project are you working at, that you still refuse to attach even your actual test VIs, which surely won't reveal your super duper algorithme that you will only eventually implement in the code after your principal interfacing to your test VI is working?

Link to comment

Sorry for the late answer. But i forgot my password and had to make a new account.

 

i will send attached my Test.vi and my C-Code.

 

it worked now when i wanted to transfer an array of structs with 7 integer.

 

But this was just a test. In fact i need an array of structs and each structs has 7 integer AND an char array (i want to get the serialnr of a device).

 

But if i make it, something is kind of wrong. There is also picture attached how it should look.

 

I dont know where my problem is maybe someone can help me.

 

greets sincerly

Project.zip

Link to comment

Sorry for the late answer. But i forgot my password and had to make a new account.

 

i will send attached my Test.vi and my C-Code.

 

it worked now when i wanted to transfer an array of structs with 7 integer.

 

But this was just a test. In fact i need an array of structs and each structs has 7 integer AND an char array (i want to get the serialnr of a device).

 

But if i make it, something is kind of wrong. There is also picture attached how it should look.

 

I dont know where my problem is maybe someone can help me.

 

greets sincerly

 

Well as has been many times stated by me and many others about interfacing external code there is a fundamental and very important difference about having a string that is fixed size or variable size.

 

Fixed size looks like this: 

type name[n]; 

and when used in a struct is fully inlined. This means that the struct is extended directly with n elements of type.

 

This however:

type *name;

will only place a pointer in the structure and will require the program(mer) to manage the actual memory himself.

 

Now a LabVIEW string is again a different beast that is not directly compatible with either C declaration, so adding a LabVIEW String to the cluster is really the totally wrong thing here.

 

If it was a pointer you would have to add an integer in there (32 bit if you intend to run it in LabVIEW 32 bit, and 64 bit if you intend to run it in LabVIEW 64 bit). Since it is inlined you have to place a cluster in there with 256 U8 elements. Not nice but that is how this is to be solved.

Link to comment

ok if i do it in the header like this:

 

char *serial_A;

 

make instead of strcpy a memcpy of the serial_a Variable into the struct like this

 

memcpy(pubtable.serialnr_A, sizeof(strlen(interntable.serialnr_A)), interntable.serialnr_A);

 

i will get a runtime error.

 

 

I want to make it as simple as possible and i think what you said about this pointer solution and in LabVIEW about an integer would be the easiest way. (instead of the 256 u8 elements).

Because then i have just 8 integer in my cluster which is in an array of 16 entrys and this should work.

 

But something got wrong with my C code.

 

i will put the C-File attached again.

Thanks for the fast help.

Project1.zip

Link to comment

Ok now it worked at least somehow.

 

I get the attached output.

In the bottom of the picture you see the block diagram

I think i have to add something in the block diagram, to convert something.

 

Because in the fourth and fifth row it is written "46022880" and "46022976" but it should be written "0204" (because i know the serialnumber of this device and its 0204)

 

how can i solve this problem? i tried to change the display format but this wasnt the solution.

 

Greets

post-52554-0-34926500-1407523737_thumb.p

Link to comment

Your screenshot shows that your cluster contains only 32-bit integer values. Why do you think you can put a string into one of them and get the right result?

 

You cannot copy strings in C using assignment. You must use strcpy() or another similar function. But before you do that you need to understand strings in C. Find a good C reference - I highly recommend the original K&R reference "The C Programming Language" but there are many others available.

Link to comment

I already tried it with this functions but then i get all the time a runtime error or buffer is too small.

 

Can you please help me? its urgent. if i would have time to read books then i wouldnt post it here and search for help :).

 

when i did it like above:

pubtable.serialnr_A = interntable.serialnr_A;

 

i get at least the adress of the value (it seems like that).

 

i know that you should not treat it like that.

But i need a way how to to give the string to the struct and then i make my memcpy for the struct to transfer it to labview.

 

So i dont know how the c string should look like (length and byte-size) that i can transfer it to labview.

 

And how could i make it in Labview to get the value if i would get this adress from the pointer?

Or is it only solveable with C?

 

if you can post a code-example how i can implement it by me in C and Labview i would be grateful.

 

Update:

Actually i saw that the result should be "0204". This means in the memory 0x30, 0x32, 0x30, 0x34. So it is in my opinion 4 byte big.

 

Is it somehow possible without transferring the wchar to a char in C that i can make this visibile in Labview?? or what can i do?

Edited by labviewbeginner1
Link to comment

This isn't a forum for learning C. Anything I would write here to explain how C strings and pointers work, someone has already written (in a book and elsewhere on the internet).

 

Briefly, in your struct, when you declare "char *serialnr_A" in your struct, you are allocating space only for a memory address. Sometime later you need to allocate the actual space for the serial number, and then assign the address of that space to the value serialnr_A. When you assign "pubtable.serialnr_A = interntable.serialnr_A;" you are copying the address stored in the internal table to the address stored in the public table. You aren't copying the serial number at all. In your LabVIEW code, you are passing the serialnr parameter as a 32-bit integer, and this happens not to crash because on a 32-bit operating system, a memory address is stored as a 32-bit value. Probably what you want to do instead is copy the data stored at that address, to the serial number parameter.

 

However that has a second problem:

If the serial number is stored as a string, then it is stored as a series of ASCII characters, and NOT as a number. If the serial number happens to be 4 bytes long then you can store it in a 32-bit (4-byte) integer, but when you display it as an integer (as you're doing in your cluster) you won't see the serial number, because then you're looking at the bits as one number rather than as 4 individual bytes. When you store "2" as a character, you're actually storing the decimal value 50 in a byte. Does that distinction make sense to you? So even once you get past the pointer/value problem, you still won't see the correct serial number in your cluster.

 

You have a couple of options and you need to figure out which one is best for you. For all of these, in order to display the serial number as a string in LabVIEW, you'll need to do some conversion in LabVIEW. Unless you want to rewrite your DLL to understand LabVIEW data types (you don't), you cannot pass a cluster that contains an embedded string to a DLL as a struct.

- If you know that the serial number is always 4 bytes, you simply store those 4 bytes in the serialNr value. When you want to display it on the screen as a string, you'll need to convert it to a 4-byte array of U8, then convert that to a string.

- Continue instead to store a pointer to the serial number in the struct, and then you'll need to use a call to MoveBlock to retrieve the actual string and copy it into a LabVIEW string. Also, this will break if you move to a 64-bit operating system.

- Define the serialNr parameter as a cluster of U8, instead of as a single I32, and copy the actual serial number values into it. To display as a string, you'll need to convert the U8 cluster to an array, and then convert the array of bytes to a string.

Link to comment
  • 2 months later...

Hi labviewbeginner, I hope this is not too late in reply.

 

I think the problem is with Byte Alignment (or Data Alignment), particularly with structs in C

 

See: http://digital.ni.com/public.nsf/allkb/F7E5C9169D09E98586256AF300717B33

 

Your C compiler is configured to use a different Data Alignment (1 byte, 2 byte, 4 bytes, 8 bytes, etc) than your LabVIEW version is set up to use.

 

See: http://en.wikipedia.org/wiki/Data_structure_alignment

 

although wikipedia is not that clear.

 

It simply means that the compiler will pad your struct sizes to fit into a multiple of the byte alignment, hence a different size is reported when you try to copy memory. And the error when the size is too big (unexpectedly).

 

Its a quick change in your C compiler....compile with the correct data alignment, or try them all to see whan you get the alignment the same as is generated by LabVIEW

 

Hope this helps

Link to comment

Hi labviewbeginner, I hope this is not too late in reply.

 

I think the problem is with Byte Alignment (or Data Alignment), particularly with structs in C

 

See: http://digital.ni.com/public.nsf/allkb/F7E5C9169D09E98586256AF300717B33

 

Your C compiler is configured to use a different Data Alignment (1 byte, 2 byte, 4 bytes, 8 bytes, etc) than your LabVIEW version is set up to use.

 

See: http://en.wikipedia.org/wiki/Data_structure_alignment

 

although wikipedia is not that clear.

 

It simply means that the compiler will pad your struct sizes to fit into a multiple of the byte alignment, hence a different size is reported when you try to copy memory. And the error when the size is too big (unexpectedly).

 

Its a quick change in your C compiler....compile with the correct data alignment, or try them all to see whan you get the alignment the same as is generated by LabVIEW

 

Hope this helps

 

Believe me, alignment is the smaller of the problems he is encountering. The bigger problem is the lack of understanding C pointers, strings and all that and that LabVIEW strings are something very different than a C string. That together with proper memory allocation and deallocation rules for any pointer you use.

 

I lost at some point my patience and found that ned was doing a better job in trying to teach him a little about C programming than I could bring up to do, so left it at that.

Link to comment

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.

×
×
  • Create New...

Important Information

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