LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

How do I pass this structure into my Call Library Function Node? Progress Made.

Solved!
Go to solution

System Info:

I am running LabVIEW 2017. I am working with an sbRIO running Linux RT.

 

Completed:

I've managed to create a shared object library in C to interface with my LabVIEW program. I've managed to use the Call Library Function node with many test C functions and LabVIEW block diagrams that I programmed. For example, I can pass numeric, char*, pointers, pointers to structures, and structures within structures to my Call Library Function node and get the function outputs back into LabVIEW.

 

Problem Information:

I am now trying to pass in a premade structure (serv_addr) for IPv6. The structure definition is spread out within several header files, which I have located the relevant parts and compiled below. My C function (.so) is also shown below. I've attached an image of the block diagram that I have currently tried. (This kind of block diagram for parsing the string into uInt8 has worked for my test code)  The htons function returns a uint16.

 

When I try to run my VI, the sbRIO disconnects, which happens if the cluster passed into the Call Library Function is the wrong order/data type. Could someone please tell me if they can spot any error in my block diagram? I'd really appreciate any insight you can give.

 

 

C CODE:

 

// Relevant Sections from Header Files

/* POSIX.1g specifies this type name for the `sa_family' member. */

typedef unsigned short int sa_family_t;

 

/* This macro is used to declare the initial common members

   of the data types used for socket addresses, `struct sockaddr',

   `struct sockaddr_in', `struct sockaddr_un', etc. */

 

#define     __SOCKADDR_COMMON(sa_prefix) \

sa_family_t sa_prefix##family

 

 

 

/* IPv6 address */

struct in6_addr

{

   union

     {

      uint8_t     __u6_addr8[16];

#if defined __USE_MISC || defined __USE_GNU

      uint16_t __u6_addr16[8];

      uint32_t __u6_addr32[4];

#endif

     } __in6_u;

#define s6_addr               __in6_u.__u6_addr8

#if defined __USE_MISC || defined __USE_GNU

# define s6_addr16            __in6_u.__u6_addr16

# define s6_addr32            __in6_u.__u6_addr32

#endif

};

 

extern const struct in6_addr in6addr_any;       /* :: */

extern const struct in6_addr in6addr_loopback;   /* ::1 */

#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }

#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }

 

 

 

struct sockaddr_in6

{

   __SOCKADDR_COMMON (sin6_);

   in_port_t sin6_port;      /* Transport layer port # */

   uint32_t sin6_flowinfo;   /* IPv6 flow information */

   struct in6_addr sin6_addr;      /* IPv6 address */

   uint32_t sin6_scope_id;   /* IPv6 scope-id */

};

 

 

 

// My Shared Object Library Function

void getServerAddrJ(int sockfd, int portNum, struct sockaddr_in6 *serv_addr) {

      // Parse Address

      bzero((char *) &serv_addr, sizeof(serv_addr));

      serv_addr->sin6_flowinfo = 0;

      serv_addr->sin6_family = AF_INET6; // AF_INET6 = 10;

      serv_addr->sin6_addr = in6addr_any;

      serv_addr->sin6_port = htons(portNum); // Convert the port number to network byte order

}

0 Kudos
Message 1 of 7
(3,082 Views)

The IPv6 address is NOT a string. It's a 16-byte number. You don't need to null-terminate it, because it's not a string. Also, I can't tell how many elements you configured the Array to Cluster for, but since you index element 17, it looks like you're making that more than the 16 bytes specified in the structure definition.

 

By analogy to IPv4: when you see an IP address such as 10.11.12.13, that gets converted internally into a single 32-bit (4-byte) number, basically (((10*256)+11)*256+12)*256+13. In fact, it's entirely valid to enter a single long number into your web browser. Try it out, this is Google: http://2899904068/

Similarly, an IPv6 address eventually gets converted to a 16-byte (128-bit) value. That's the value that needs to get saved in the in6_addr field. To convert between the more human-readable form, or a hostname, and an IPv6 address, use getaddrinfo. For an IPv4 address you can use gethostbyname, which is the C function that does the same thing as LabVIEW's "String to IP" function.

0 Kudos
Message 2 of 7
(3,043 Views)

Thanks for the great explanation.

 

So, if I understand correctly, if I am just allocating memory for my structure and defining the localhost IPv6 address in my .so file, I could just use a 16 element array of 8 bytes to pass into my function.

 

I've attached an image of what I tried. It didn't give me an error, but I just want to make sure my reasoning is correct.

 

Thanks again!

0 Kudos
Message 3 of 7
(3,016 Views)
Solution
Accepted by topic author JHugh

You're ending up with a reasonable result, but in order to generate a cluster of 16 U8 you're:

- starting with a string

- converting to an array of U8

- reshaping that array to 16 elements

- converting to a cluster (where you set the size to 16, I assume, making the previous array resize irrelevant)

- bundling the cluster inside another cluster unnecessarily

 

Make a simple cluster of 16 U8 (or, 4 U32) instead of all the conversions. Again, if you want to pass in an IPv6 address in the :: form, you'll need to call another function first to convert that to a 128-bit value (that is, to the value that you'll write to the address cluster).

0 Kudos
Message 4 of 7
(2,995 Views)

Thank you. I'll be sure to do it that way when I start passing in IPv6 addresses that I need.

 

BTW, I was passing a cluster into another cluster because the structure that I am dealing with in C has a structure within the top level structure.

0 Kudos
Message 5 of 7
(2,989 Views)

JHugh wrote:

BTW, I was passing a cluster into another cluster because the structure that I am dealing with in C has a structure within the top level structure.


If you're saying you wrapped the cluster inside another cluster to duplicate the union in the C struct, that's not necessary. There's no need to put a cluster inside a cluster when the only element of the outside cluster is the inside cluster, and the C definition doesn't do that either. Not that it matters in terms of memory layout; for the purposes of passing data to a DLL, increasing how many levels deep the cluster is nested has no effect on the data.

0 Kudos
Message 6 of 7
(2,985 Views)

My struct is like:

 

struct p_addr {

   char my_addr[128];

}

 

struct serv_addr {

   int flow;

   int port;

   struct p_addr sp_addr;

   int scope;

};

0 Kudos
Message 7 of 7
(2,970 Views)