12-14-2010 09:55 AM
In my first case, I have a single VI compiled to a DLL. This VI does three different things, depending on the function call:
This is the first operation. It opens a connection to a UDP stream server. "ID" and "SA" are commands to tell the server to start send UDP data. The function call is:
callout(int Func, char *Address, char *DataOut, int Len);
Func is the operation I want to do, Address is the IP address to the server, DataOut is a container for the returned data and Len is the size of the container.
To start receiving data, I write:
callout(1, addr, data, 5000);
DataOut is ignored in this case. To retrieve UDP packets the next operation is used:
The call is:
callout(2, addr, data, 5000);
In this case, Address isn't used. 5000 bytes of data is copied to the location where data points.
And finally, this operation closes the stream:
callout(3, addr, data, 5000);
"SC" is the command to close the stream.
The above steps work just fine. The state of the VI is persistent between the function calls, and the connection saved in Connection ID is usable all the time.
What I'm trying to do now is streamline the operations by dividing this VI into three separate VIs. The first operation looks like this now:
In this case I try to save pointers to relevant objects in my C code:
TD1 *error;
LVRefNum *connectionid;
The new function call is:
callout_connect(addr, connectionid, error);
The next VI becomes:
With the function call:
callout_receive(connectionid, error, data, 5000);
And finally:
With the function call:
callout_close(addr, connectionid, error);
Needless to say, splitting up the VI into three separate entities has not worked so far. It compiles, sets up a connection, but somewhere the connection is then lost.
My suspicion is that I'm misunderstanding how the connection reference or the pointer to this reference is saved. Any suggestions?
Solved! Go to Solution.
12-14-2010 10:43 AM
What you are attempting to do is interesting. Not ever doing this myself, I would say that the problem is that you are passing a pointer that points to nothing and telling LV to fill it in. This would most likely cause your app to crash.
If I were doing this myself, I would not pass such things around across the boundry. I would probably keep these simple and pass base types around. To do this, I would for instance, pass an integer for the connection. This would be done by using a type 1 or 2 global or a local to store the connection in an array and then index that array when the caller wants to access the connection.
Attached is a generic memory allocation vi that I made for things like this.
A
12-14-2010 10:58 AM
I suspect that LV is cleaning up the resources after the first call completes.
By uisng the first version, LV keeps the resources open.
I prefer the first version.
What don't you like about it?
Ben
12-14-2010 10:58 AM
> Not ever doing this myself, I would say that the problem is that you are passing a pointer that points to nothing and telling LV to fill it in. This would most likely cause your app to crash.
That was one thing I suspected too, but it didn't crash. It doesn't matter, I've solved it by putting connection and error objects in global variables. It works just fine now and seems to be the most elegant solution.
12-14-2010 11:03 AM
> What don't you like about it?
It's clumsy, short and simple. Of course, I don't know if there's any performance hit putting things in global variables, but there doesn't seem to be.
12-14-2010 11:04 AM
Oh, if it is not crashing, then it is probably that you are either not initialising it, but it is a global, which would by default cause it to be initialised to NULL, or you are initialising it to NULL, or it could be a random number. In all cases, LV probably has some defensive code to protect itself from accessing an object that is invalid and keep from crashing.
Good to hear that you got it working though.
A
12-14-2010 11:06 AM
Clumys may be valid, but short and simple is usually the way to go. 🙂
A
12-14-2010 11:08 AM
@O.P. wrote:
> What don't you like about it?
It's clumsy, short and simple. Of course, I don't know if there's any performance hit putting things in global variables, but there doesn't seem to be.
If you replace the Global with a USR (see nugget thread) and the numeric with a type-def enum you have an Action Engine (see here for Nugget on Action Engines).
Short of LVOOP, its hard to get more elegant than that.
Ben
12-14-2010 03:20 PM
O.P. wrote:
In this case I try to save pointers to relevant objects in my C code:
TD1 *error;
LVRefNum *connectionid;
The new function call is:
callout_connect(addr, connectionid, error);
callout_receive(connectionid, error, data, 5000);
callout_close(addr, connectionid, error);
Needless to say, splitting up the VI into three separate entities has not worked so far. It compiles, sets up a connection, but somewhere the connection is then lost.
My suspicion is that I'm misunderstanding how the connection reference or the pointer to this reference is saved. Any suggestions?
Jumping in late here, but the problem is your C code. You're allocating a pointer to connectionid (and to error, for that matter) without actually allocating any space for them. You need to allocate those variables, then pass their address. The C code should be:
TD1 error;
LVRefNum connectionid;
callout_connect(addr, &connectionid, &error);
callout_receive(&connectionid, &error, data, 5000);
callout_close(addr, &connectionid, &error);
Better still would be to have connectionid be the return value of callout_connect, which you could then pass by value (instead of by reference) to the other functions. Note that a connection id is just a 32-bit value, so you can typecast the refnum to that type and back, just be careful that you typecast to and from the same type.
12-16-2010 02:08 AM
I'm not sure about passing LV Refnums by value. Definitely not possible if you leave them as LVRefnum in the CLN configuration but I'm not sure you can configure them to be handled as ints instead.