05-04-2010 10:04 PM
I am trying to interface my application in LV 7.0 to some external hardware uing a USB comm port. The vendor furnishes a dll for function interface. I have successfully used several of the functions with the Call Library Function of LV to create a handle to the comm port, open it, flush the RX buffer and read back the port number and baud rate as tests. However, when I try to read the data of interest that is in a structure, I have not been able to make anything work. The type def for the structure is:
typedef struct sN2KMsg {
u32 Timestamp;
u32 PGN;
u8 Priority;
u8 SrcAddr;
u8 DestAddr;
u32 Length;
u8 Data[N2K_MAXLEN_TP];
} sN2KMsg;
and the function prototype is:
int ACommsN2K_Read (int Handle, sN2KMsg *msg) .
My attempts so far are in the simple attached vi. This is my first attempt to use the Call Library Function of LV and am relatively new to LV programming. Hopefully my problem is something simple or stupid I have or haven't done.
Thanks in advance for any help.
Hootowl
05-04-2010 11:03 PM - edited 05-04-2010 11:05 PM
Try this...
Oops, sorry, LabVIEW 7.0 eh? I'll see what I can do...
05-04-2010 11:20 PM
05-05-2010 06:36 AM
I double checked and the functions use stdCall for the calling convention. All the other functions work using the I16 data type for the handle and I tried I32 for the MSGerror with no change in result.
Hootowl
05-05-2010 06:11 PM
I've had no success in converting the vi I attached above down to LV7.0 (dont have early enough version installed).
I've had similar trouble with structures in dlls in the past. What I did was flatten the structures in the function prototype.
Here's an image of my suggested block diagram.
Here's my suggested function prototype:
int16_t ACommsN2K_Read(int16_t Handle, uint32_t TStamp, uint32_t PGN, uint8_t Priority, uint8_t SrcAddy, uint8_t DestAddy, uint32_t Length, uint8_t *Data);
05-06-2010 02:26 AM - edited 05-06-2010 02:31 AM
Troy K wrote:I've had no success in converting the vi I attached above down to LV7.0 (dont have early enough version installed).
I've had similar trouble with structures in dlls in the past. What I did was flatten the structures in the function prototype.
That is not gonna work! The structure is passed as pointer so you have to pass a pointer to a structure. What you have done is when the structure is passed directly. Then the C compiler treats each individual element of the structure as a seperate function parameter. Additional problems there would be how the elements smaller than the native integer size are passed. Most likely they are passed as individual parameters but it is also possible that the C compiler would pack them.
I see two potential problems with the original posters code:
1) int under 32 bit Windows is 200% for sure an int32 and not an int16. The other functions might work but this one might not.
2) Byte alignement in the structure. You have "length" parameter on an alignment that is not a multiple of its size. That is possible if you use so called packed data structures (in C you use #pragma pack(1)) and LabVIEW always uses that. Microsoft C however uses by default an alignment value of 8 which indicates to the compiler to align every element to the smaller of the two: it's own element size or that alignment. It is likely that the programmer of your DLL has not changed that default alignment but you only can be sure of that if you read the documentation and/or the header file to see if there is such statement.
If there is no indication that the DLL uses packed structures you would need to add a dummy filler byte in front of the length element to make the length be aligned correctly.
Last but not least you should check what value N2K_MAXLEN_TP is and adjust the cluster size in the right click pop-up menu of the Array to Cluster node accordingly (and hope that it doesn't need to be more than 256, which is the limit the Array to Cluster node allows).
05-06-2010 06:08 PM
rolfk wrote:
That is not gonna work!
Yep, you're absolutely right. That's not gonna work, sorry for the bum steer.
I just checked over the workaround I wrote two years ago an it looks like I remembered it wrong.
I'm working on the assumption that you have your data sizes correct.
You do need to flatten the cluster but it still needs to be a cluster.
For some reason LabVIEW cant (or couldn't at the time) properly adapt a cluster to a struct that contains an array.
If the array is a fixed size (which mine was and yours appears to be) you need to replace each array element with a numeric.
Like this...
Once again, sorry for the dodgy suggestion in my earlier post.
05-07-2010 02:06 AM - edited 05-07-2010 02:09 AM
Troy K wrote:You do need to flatten the cluster but it still needs to be a cluster.
For some reason LabVIEW cant (or couldn't at the time) properly adapt a cluster to a struct that contains an array.
If the array is a fixed size (which mine was and yours appears to be) you need to replace each array element with a numeric.
You need to make a distinction between arrays in a cluster that are variable sized and arrays that are fixed size. In the first case C does use a pointer in the cluster and in the second case it is inlined (flattened) into the structure.
LabVIEW always does use a handle for an array (which a string is too). A handle is not a pointer but a pointer to a relocatable pointer. A simple pointer as used in C is not easily relocatable since any client still holding the value of the old pointer will usually point into nirvana after a pointer has been reallocated, since the system often throws away the old pointer and creates a new one. This would make many things dataflow almost mandates hard to implement, so that is why handles were used.
So if the array is fixed size and therefore inlined you can simply replace it in LabVIEW by a cluster of the same size. And embedding a cluster inside a cluster is fully valid so there is no need to build an entire cluster by hand, but simply create an array and translate it to the right size element cluster using the Array To Cluster Node. That saves a lot of wirework. The limitation is that the Array To Cluster node only allows to create clusters with up to 256 elements. So if you need more you have to start to trick a bit. If it is only 512 you could get away by adding 2 * a 256 elements clusters right behind each other. Personally I usually do such structures by treating everything as a single array of uInt8 or whatever integer size is useful, and packing the information into the array before the call and out of the array after.
Or I resort to writing a wrapper DLL right away to make everything clear and shiny. If the C structure contains array (and string) pointers, the wrapper DLL is anyhow the highly advised solution. You can trick even here even more on the LabVIEW diagram and get away without wrapper DLL in some cases too, but first the C compiler knowledge needed to do that is in fact at least as detailed as would be required to write a wrapper DLL and usually even more than that, and doing C compiler stuff in the LabVIEW diagram gets a real maintenance nightmare sooner than later.