03-19-2018 01:15 PM
LV 2013, LVRT 2013
I have a mystery problem which can best be explained by a LabVIEW misbehavior, or at least a misunderstanding of LV behavior.
I'm on LVRT, on a PXI box, networked to a Windows host.
I have several classes (called "modules") which were built with the idea that each module had its own separate TCP connection to gizmos.
The classes are dynamically created and dynamically destroyed, as the user makes a certain DAQ configuration ACTIVE or INACTIVE.
I am now trying to fool a certain subclass of these into using a common connection.
In other words, instead of each module making a connection when it's born, and disconnecting when it dies, each instance calls a common CONNECTION MANAGER.
I'm trying to implement it so that the FIRST connection born makes the connection, and the subsequent ones born simply think they're connected. I do that by entering their individual ID tags into a list. If the list is EMPTY when you call CONNECT, then I really make a connection, otherwise I just add you to the list.
When you DISCONNECT, I remove your ID from the list. If that makes the list empty, then the connection is really broken, otherwise it is left alone.
In certain scenarios, this works fine. My test case has two INPUT modules, and one OUTPUT module. If I start RTAC (the RT program) with the INPUTs inactive, and the OUTPUT active, then all seems well. I can ACTIVATE/DEACTIVATE the input side, and it keeps the connection alive, and reconfigures as needed, and there is no problem. The inputs and outputs all behave as expected.
HOWEVER
If I START with all three active, then it works fine UNTIL I deactivate all the INPUTS. If I deactivate ONE of them, it's still OK. But as soon as I deactivate the second INPUT module (leaving the output module ACTIVE, the connection dies.
The other end of the connection is a program on the host which I control; it reports an error 66 (the other side broke the connection).
The ConnID does NOT go outside of the shift registers in pic 1 except to a SEND and a RECEIVE routine which are not capable of closing the connection.
This pic shows the DISCONNECT code: it removes one tag from the list and if the list goes empty, the connection is broken.
But the debug log shows that the list still contains ONE element, and does NOT show the DISCONNECT line.
I can put a loop after the first CASE statement that tests the ConnID every 2 mSec for 50 loops. If I do that, the debug log shows that the connection goes bad after 4-8 mSec after the second DISCONNECT call.
That tells me that, because the CPU is trapped in this one case (and this code is NOT reentrant), that the connection is being killed OUTSIDE of my code. There is no other answer.
The best explanation I have is this:
Input module 1 is created and starts running.
Input module 2 is created and starts running.
Output module 1 is created and starts running.
Input module 1 tries to connect. The list is empty, the connection is made.
Input module 2 tries to connect. The list is not empty, the connection is reported made.
Output module 1 tries to connect. The list is not empty, the connection is reported made.
< everything works OK >
The user deactivates module 2.
Input module 2 tries to disconnect. It is removed from the list, which is not empty. No disconnect by me.
< everything works OK >
The user deactivates module 1.
Input module 1 tries to disconnect. It is removed from the list, which is not empty. No disconnect by me.
BUT (and here comes the conjecture)
Because the connection was made as a result of the INPUT CLASS, LabVIEW (mistakenly) thinks that that class "owns" the connection.
When the class is destroyed, LabVIEW thinks "the silly human left a connection open; I'll close it for him" and does so.
That would explain everything that I see.
Is that a reasonable guess? Is there some sort of "context" which is given to a dynamically-instantiated class and cleaned up when it's gone?
How would I work around this, to do what I want?
Blog for (mostly LabVIEW) programmers: Tips And Tricks
03-19-2018 01:52 PM
Here's the CONNECTION code:
It just does the checking and makes the connection only if necessary.
There is a state machine within this loop (in another case, not shown), and that is called from another piece of code which runs as long as the main VI runs (IOW, it doesn't come and go with config changes).
Since the ConnID belongs to this VI, and this VI is used by a "permanent" VI, I don't understand why the ConnID gets attached to the temporary VI.
Blog for (mostly LabVIEW) programmers: Tips And Tricks
03-19-2018 01:56 PM - edited 03-19-2018 01:58 PM
@CoastalMaineBird wrote:
Is that a reasonable guess? Is there some sort of "context" which is given to a dynamically-instantiated class and cleaned up when it's gone?
How would I work around this, to do what I want?
That makes a certain amount of sense. I wonder if it would work to create a separate class to maintain the connection. Since all modules would reference the same class, the context should continue to exist...
DISCLAIMER: I do very little OOP, so take my opinion with a grain of salt.
Edit: Well, now I'm even less sure based on your latest post
03-19-2018 02:24 PM
I don't think it's related to being called from a CLASS, I think it's related to being called from a DYNAMICALLY LOADED piece of code, that actually terminates (runs out of things to do).
When that happens, it tries to "clean up" after me, and destroys the connection that it thinks I should have closed.
But why does it claim ownership of that? The code that actually made the connection is not closed.
Blog for (mostly LabVIEW) programmers: Tips And Tricks
03-19-2018 04:44 PM
Maybe its just me, but I am not sure if I fully understand your posts - you are using a lot of terminology that seems specific to you. For instance, what do you mean by input modules and output modules? What do you mean by active/inactive? I am assuming a class being active/inactive is not the same as being created/destroyed?
In either case, if I understood correctly, you are dynamically launching some module, which calls a global action engine, which opens a TCP reference. You launch more modules, which use the TCP reference, or at least the same global action engine. When the first module that called the action engine stops, the connection reference is lost. Is this true, or is it just when you get down to no input modules? (Whatever an input module is)
Assuming you dont have a bug somewhere else in your code and a TCP connection is closed when the caller goes idle, (I am not 100% on if this is the case, but it should be easy to verify with a simple test setup) then a simple solution would be to just have another, independent module create and own the reference. (Not sure why you are sharing a connection reference between modules anyways)
03-19-2018 04:55 PM
For instance, what do you mean by input modules and output modules?
--- A module is an instance of a particular class.
What do you mean by active/inactive?
--- The user sees them as ACTIVE or INACTIVE.
I am assuming a class being active/inactive is not the same as being created/destroyed?
--- No, it IS the same. User terminology vs. programmer terminology, sorry for mixing them.
you are dynamically launching some module, which calls a global action engine, which opens a TCP reference.
--- Yes.
You launch more modules, which use the TCP reference, or at least the same global action engine.
--- Yes.
When the first module that called the action engine stops, the connection reference is lost.
--- Yes.
Is this true, or is it just when you get down to no input modules?
--- That is true. I WANT it to disconnect ONLY when I get down to zero modules, but it disconnects sooner.
a simple solution would be to just have another, independent module create and own the reference.
--- And that module would have to exist for the whole program. That's OK, and it sounds like it would work, but what I don't understand is who "owns" the connection? Is it a thread, in the sense of an execution thread?
Blog for (mostly LabVIEW) programmers: Tips And Tricks
03-19-2018 05:01 PM
@CoastalMaineBird wrote:
you are dynamically launching some module, which calls a global action engine, which opens a TCP reference.
--- Yes.
You launch more modules, which use the TCP reference, or at least the same global action engine.
--- Yes.
When the first module that called the action engine stops, the connection reference is lost.
--- Yes.
Is your first call to the Action Engine from a class? Because that will definitely kill it.
03-19-2018 05:06 PM
Is your first call to the Action Engine from a class?
The first call that actually tries to open the connection is from a class.
The first call overall is an INIT function, called from the main program before anything else.
Blog for (mostly LabVIEW) programmers: Tips And Tricks
03-20-2018 04:43 AM
Are the VIs which actually open the connection in any way re-entrant. Could it be that the "ownership" is an unwanted "state" of a (possibly shared) re-entrant VI? Just a guess.
08-07-2018 03:08 PM
As this thread doesn't appear to have a solution, I'm adding in my two cents here.
It seems, OP, that you may be confusing the ideas of ownership, scope, and the garbage collection system.
When you open a serial or TCP connection in Labview, that connection is 'owned' by the entire application instance. It is only visible to the object but the lifetime of the connection is the lifetime of the application.
Your posts appear to be hinting at the idea of a Labview garbage collection system. .NET has a garbage collection system that cleans up references that may have been left open; this appears to be the behavior that you are anticipating to be the culprit. However, as far as I know, Labview does not have a garbage collection system to clear out references that are left open. It is entirely up to the user to close and destroy opened references.
Note that Labview is not an object-oriented language originally, so its behavior is not similar to that of .NET. Labview doesn't appear to have a garbage collection system, and the only difference between Object-oriented vis within a class and normal vis appear to be protection level.. Labview does not appear to assign a lifetime to references when they are within an object.
My suggestion to you is to open the 'TCP Multiple Connections' Example project, and create an object oriented example from it. Then attempt to share a connection reference between several objects, and see if this gives the behavior you're experiencing.