LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

How to pass structure into dll?

Main Objective:

I have an sbRIO board, and I am trying to create TCP IPv6 protocols for it.

 

Completed:

I managed to connect my sbRIO board to LabVIEW and upload and run a simple dll using the Call Library Function Node with uint32 arguements.

 

Problem:

I have been trying to create VIs for the functions that I need in IPv6 i.e. bind, listen, accept, recv, send, connect, and close. However, I am having trouble passing a struct into the Call Library Function Node.

 

Background:

I've done some research (Examples in LabVIEW and the NI discussion boards), and I cannot find any good references on how to do this. Also, I am a relative novice at LabVIEW (8 months), the C language (basic functions + pointers/addresses), and I do not know any Linux (< 1 week research).

 

Failed Attempts:

My first attempt to pass a struct in the Call Library Function Node is with the bind function. Bind function reference:   http://man7.org/linux/man-pages/man2/bind.2.html

 

The function prototype is: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

 

Where I believe that sockaddr is a struct with the following parameters:

Sin6_flowinto = 0;                                                                    // Always 0

Sin6_family = AF_INET 6;                                                       // Defines IPv6 (not sure if it should be a numeric 10 instead of a string)

Sin6_addr = 2001:0DB8:AC10:FE01:0000:0000:0000:0000; // The IPv6 address that I am using

Sin6_port = 1050;                                                                   // The IPv6 port that I am using

 

Question:

How can I pass this structure into the Call Library Function Node, and if anyone knows C/Linux, am I even passing in the correct structure based off that reference?

 

 

I've attached what I've done so far to this thread.

 

Thanks.

0 Kudos
Message 1 of 13
(4,976 Views)

I don't have time to work through all the details here, but one thing I noticed immediately is that the library name should not be LabVIEW. You need to figure out which operating system library those functions are in, most likely libc.

0 Kudos
Message 2 of 13
(4,925 Views)

For this you'll have to consult the Linux headers and look at the actual definition of the structure. The complication here is that struct sockaddr* is only an incomplete definition. Depending on the actual protocol you want to pass information for, you'll have to use the correct sockaddr_XXX definition.

 

Also Linux does not use DLLs but shared object modules (*.so). (And VxWorks uses *.out modules).

 

From my Linux headers the struct to use for IPv4 is defined like this:

struct sockaddr_in {
    int16_t          sin_family;   // e.g. AF_INET
    uint16_t         sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // filler bytes, zero this if you want to 
};

struct in_addr { unsigned char s_addr[4]; /* IPv4 address */ };

struct in_addr is again a fixed array of 4 bytes.

 

For IPv6 however the structure is like this:

struct sockaddr_in6 {
     int16_t         sin6_family;   /* AF_INET6 */
     uint16_t        sin6_port;     /* port number */
     uint32_t        sin6_flowinfo; /* IPv6 flow information */
     struct in6_addr sin6_addr;     /* IPv6 address */
     uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
};

struct in6_addr {
     unsigned char   s6_addr[16];   /* IPv6 address */
};

The last parameter to the bind function should then receive the sizeof(struct sockaddr_in6) value.

 

Basically you can create a cluster in LabVIEW with the according elements and pass that to the function with the Adapt to Type setting for this parameter. Filling in the actual values for the elements is however tricky. Just remember that LabVIEW strings are not the same as C string pointers and even less like fixed size C string fields. In order to resemble the fixed size array elements in above struct you need to place a LabVIEW cluster with the number of elements of that size into the outer cluster.

 

While the definitions such as AF_INET, AF_INET6 are standardized by Posix, the actual numeric values they map to are not and can differ between socket implementations. There is a serious mismatch between those numbers between the WinSock and Linux socket implementation but even the numbers for Linux sockets which is just like the WinSock implementation derived from the Berkeley sockets implementation, are sometimes different to the numbers for  BSD Unix variants such as FreeBSD. In the LabVIEW world things get even more interesting if you also want to support the older PPC based realtime systems that run VXWorks as OS. This OS also has a socket library that is heavily inspired by the Berkeley socket implementation, but its functions are not all the same and the numeric values for all those constants are different again.

 

So you really will need to install the development headers for your operating system and get the actual numeric values from there and not just go and take them from a Windows or another Unix system.

 

Please check here that your sbRIO is really using NI Linux Realtime and not VxWorks.

 

Rolf Kalbermatter
My Blog
Message 3 of 13
(4,898 Views)

Thanks rolfk for the help.

 

Complete:

I checked with the webpage that you suggested, and my board (sbRIO 9607) is running NI Linux Realtime.

 

Questions:

1. I looked for the header (.h) file on my sbRIO 9607, and I could not find it. I did manage to find the libc.so.6 file in the directory /lib/libc.so.6, but I do not know how to open it or if I need to open it. Would it contain the structure definition that I am looking for?

 

2. If I am understand correctly, the header files would give valid numeric values for sin6_family, sin6_flowinfo etc.

 

3. What do you mean when you say that "In order to resemble the fixed size array elements in above struct you need to place a LabVIEW cluster with the number of elements of that size into the outer cluster?" Are there multiple clusters, one for sin6_family, one for sin6_port etc inside of a larger cluster (sockaddr_in6), which is fed into the LabVIEW block?

 

4. I am not too familiar with structures, but in the LabVIEW case, how would I ensure that I have allocated fixed memory for int16_t, uint16_t etc? Would it be as simple as casting an integer through a uint16 block before feeding it into the cluster?

 

Thank you.

0 Kudos
Message 4 of 13
(4,851 Views)

1) The development headers are not installed by default. You would need to do that with the opkg manager command on the cRIO or alternatively install the preconfigured Eclipse based C development tool chain on Windows that NI provides as a download. The .so file is a binary file like a DLL under Windows and contains no human readable parts. The structure definitions used in there are implied by the source code used to create the shared library, but this source code is NOT included in the shared library.

 

2) correct

 

3) I mean what I said. A fixed size array inside a struct in C is simply inlined in the struct. The only way to resemble that in LabVIEW is to place a cluster with the correct amount of elements inside the main cluster. E.g

 

 

struct
{
    int32_t num;
    char fixedSize[6];
} MyStruct;

will have to be created in LabVIEW as a cluster with an int32 element as first element followed by a cluster inside this cluster with 6 * uInt8 elements.

 

 

4) No the uint16_t elements in the struct definitions are simply uInt16 integers. They are always fixed size, according to the size definition used by the C compiler. While the standard C types int, short, long etc can change in byte size depending on the platform and C compiler used (but will always stay fixed size on a partular platform and compiler), int16_t and friends are newer definitions that are supposed to have the indicated number of bits from the type name independent of platform and C compiler (provided the C compiler toolchain supports the necessary C99 standard!). When a C programmer talks about fixed size arrays or strings he means an element like:

char string[16];
int array[256];

Unlike a pointer variable, these definitions instruct the C compiler to actually create an according string of 16 characters, or an array of 256 integer numbers.

 

Considering the pretty basic questions you have so far, I'm afraid this whole thing is going to be pretty hard to successfully tackle for you.

 

Interfacing external code is a pretty tricky exercise, and unfortunately even when it won't crash anymore you still have no guarantee that it will be correct if you don't understand the very basics of how a C compiler works and allocates structures and pointers in memory. If this is for a throw away school project, there is not much lost by trying anyhow, but if you intend to use this in an industrial setup I would recommend you to look for assistance from someone who has a real understanding of these things. Otherwise you are only creating a maintenance nightmare and a potential time bomb.

Rolf Kalbermatter
My Blog
0 Kudos
Message 5 of 13
(4,849 Views)

Thank you very much.

 

When I started this project, I knew it was going to be hard; however, I am the only person in my group who has some of the necessary background, so I need to figure it out. I really appreciate you helping me out.

 

I believe that I understand 2, 3, and 4 well enough to start writing the code. I've written/tested a VI that will calculate the sizeof a cluster in bytes. Now I just have one last question.

 

Where can I download the development headers?

 

Note that I've previously installed Eclipse from NI, but I cannot seem to find the headers on my disk. I also am uploading to my board via Ethernet so I do not have internet access while connected to my board.

0 Kudos
Message 6 of 13
(4,837 Views)

@JHugh wrote:

 

Where can I download the development headers?

 

Note that I've previously installed Eclipse from NI, but I cannot seem to find the headers on my disk. I also am uploading to my board via Ethernet so I do not have internet access while connected to my board.


Point 9 in this Getting Started document lists the system roots for various targets. You will probably find an "include" subdirectory at those locations, which hopefully contains the headers you're looking for.


GCentral
0 Kudos
Message 7 of 13
(4,828 Views)

@cbutcher wrote:

@JHugh wrote:

 

Where can I download the development headers?

 

Note that I've previously installed Eclipse from NI, but I cannot seem to find the headers on my disk. I also am uploading to my board via Ethernet so I do not have internet access while connected to my board.


Point 9 in this Getting Started document lists the system roots for various targets. You will probably find an "include" subdirectory at those locations, which hopefully contains the headers you're looking for.


Completed:

1. I have already read the document that you suggested, and it does not list any system root for my target.

 

2. I used the online file browser for my sbRIO and downloaded all of the files to my local harddrive. I performed a search on this root directory and confirmed that there are no header (.h) files of any kind of my sbRIO. I was mainly looking for socket.h (but, I also checked all .h files), since I believe it will contain the definitions that I am looking for.

 

3. I downloaded Cygwin and searched it for socket.h. I found it.

 

Question:

1. After reading through socket.h from my Cygwin directory, I managed to find the numeric value for AF_INET6. Using the int socket(int domain, int type, int protocol); in the Call Library Function Node in LabVIEW gives me an error if I use:

domain = 23;  // AF_INET6

type = 1;         // SOCK_STREAM

protocol = 0;   // DEFAULT

 

Reference: http://man7.org/linux/man-pages/man2/socket.2.html

 

Another LabVIEW developer (CLAD) told me to use:

domain = 10; // AF_CCITT in my socket.h file

 

I do not understand why he told me to use AF_CCITT when he understood that I was trying to program IPv6. Is AF_CCITT = AF_INET6 or are the header files different i.e. there is some hidden header file on my sbRIO that has these defined differently than Cygwin? If this is the case, how/where can I find/download the correct header files so that I can get these constant variables?

 

Thanks.

 

0 Kudos
Message 8 of 13
(4,816 Views)

You are very much over your head with this!

 

On your sbRIO you won't have any headers unless you install the whole gcc development package through opkg. If you don't know how to do this you definitely don't want to do it. With it you can use gcc directly on the target to compile C code.

 

The paths mentioned in the paper from NI refer to the Eclipse installation you claim to have done on your host computer and you will have to check there for the headers.

 

Just grabbing some random cygwin header files from the net is not gonna work. They are most likely for development under cygwin for Windows targets and therefore match the WinSock constants from the Microsoft SDKs, not the Linux headers from the NI Linux kernel. This setup, while apparently using the cygwin cross compile toolchain for the LabVIEW 2017 based development, does not have anything to do with cygwin for Windows development, other than that it makes use of the same toolchain but retargeted for cross compilation to the NI Linux Realtime system.

 

So if you installed the NI Eclipse development toolchain on your Windows computer, check in there for the appropriate include directory!

 

And no, whoever told you that you could use AF_CCITT in place of AF_INET6 does not understand socket programming at all.

Rolf Kalbermatter
My Blog
0 Kudos
Message 9 of 13
(4,804 Views)

@rolfk wrote:

You are very much over your head with this!

 

On your sbRIO you won't have any headers unless you install the whole gcc development package through opkg. If you don't know how to do this you definitely don't want to do it. With it you can use gcc directly on the target to compile C code.

 

The paths mentioned in the paper from NI refer to the Eclipse installation you claim to have done on your host computer and you will have to check there for the headers.

 

Just grabbing some random cygwin header files from the net is not gonna work. They are most likely for development under cygwin for Windows targets and therefore match the WinSock constants from the Microsoft SDKs, not the Linux headers from the NI Linux kernel. This setup, while apparently using the cygwin cross compile toolchain for the LabVIEW 2017 based development, does not have anything to do with cygwin for Windows development, other than that it makes use of the same toolchain but retargeted for cross compilation to the NI Linux Realtime system.

 

So if you installed the NI Eclipse development toolchain on your Windows computer, check in there for the appropriate include directory!

 

And no, whoever told you that you could use AF_CCITT in place of AF_INET6 does not understand socket programming at all.


I agree with you. This is much over my head, but I really want to learn this stuff.

 

So, I managed to find the socket.h and socket_type.h header files in the Eclipse directory that defines some of these values. In the file AF_INET6 = 10 and SOCK_STREAM = 1, which is as expected based off the socket function.

 

I tested the socket function from libc.so.6 in LabVIEW, and it worked. I got a socket_fd as an output from it.

 

Now I need to test the bind command, which contains that structure that I am having trouble with:

 

[CODE]    
struct sockaddr_in6 {
      sa_family_t     sin6_family;   /* AF_INET6 */
      in_port_t       sin6_port;     /* port number */
      uint32_t        sin6_flowinfo; /* IPv6 flow information */
      struct in6_addr sin6_addr;     /* IPv6 address */
      uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
};

struct in6_addr {
      unsigned char   s6_addr[16];   /* IPv6 address */
};
[/CODE]


Reference: http://man7.org/linux/man-pages/man2/bind.2.html


I know how to define: sin6_family = 10

 

I assume that I can define sin6_port = 3333 (arbitrary number between 1024 and 5000)

 

I also assume that s6_addr = 0000000000000001 since we only have 16 char available. I would enter it as an inner cluster in ASCII format i.e. 0 = (uInt8) 48 and 1 = (uInt8) 49.  Is this assumption correct or am I wrong to assume uInt8 is in ASCII?

 

What is sin6_flowinfo and sin6_scope_id and how should I define them? I mean I know that flowinfo is part of the IPv6 header, but can I just define this and sin6_scope_id as whatever I want, since I am binding the socket?

 

Thanks.

0 Kudos
Message 10 of 13
(4,789 Views)