Jump to content

LabVIEW Call Library Function Node - Union Pointer handling


Recommended Posts

I have code from another developer that allows me to interface with a server to get configuration data.  I have compiled the C code into a .SO on my Linux system without error - but in writing my LabVIEW CLFN, I am unsure what to do with a Union Pointer to pass the data.

 

The function in the C file:

void getValue(int so, CharacteristicHandle ch, Value *v)
{
    long longValue;
    unsigned long ulongValue;
    double doubleValue;
    char * c_ptr = 0;

    long msgType = g_val;

    sendLong( so, msgType );
    sendCHARhandle( so, ch );

    readLong( so, &longValue );
    v->order = longValue;

    readLong( so, &longValue );
    v->type = longValue;

    readLong( so, &longValue );
    v->label = longValue;

    switch( v->type )
    {
    case integer:
    {
        readLong( so, &longValue );
        v->v.l = longValue;

        break;
    }
    case uLong:
    {
        readLong( so, &ulongValue );
        v->v.ul = ulongValue;

        break;
    }
    case floating:
    {
        readDouble( so, &doubleValue );
        v->v.d = doubleValue;

        break;
    }
    case charString:
    {
        readString( so, &c_ptr );
        v->v.s = c_ptr;

        break;
    }
    }
    return;
}

 

The definition of "Value" from another header file:

/* Define a type to hold the value and type information for 
 * a characteristic. */
typedef struct Value
{
    union        /* This holds the value. */ 
    {
        long l;
        unsigned long ul;
        double d;
        char *s;      /* If a string user is responsible for 
                       * allocating memory. */
    } v;

    long order;
    valueType type;    /* Int,float, or string */
    valueLabel label;  /* Description of characteristic -- what is it? */
} Value;
 

I'm a newb at CLFN - the primitive datatypes I've got working for my other calls - this one I am not sure how to configure with the CLFN.  Is this possible to do with the CLFN?

Link to comment

I'm no expert with CLFNs but I know this much: LabVIEW does not have a concept for unions but you can always pass an array of bytes or a numeric of appropriate size and convert later. Just make sure that the container is large enough to contain the largest member of the union (should be 64 bit from what I can tell).

Conversion should be simple enough for numeric types but the pointer requires additional work. Here is some information for dereferencing pointers in LabVIEW (there is a section about dereferencing strings). https://forums.ni.com/t5/Developer-Center-Resources/Dereferencing-Pointers-from-C-C-DLLs-in-LabVIEW/ta-p/3522795

Link to comment

Just pass a 8 bytes wide element as an union (U64 / double / 8 x U8 cluster) and treat it according to the type field after the function call. But I also see, that you have to pass a struct (i.e., a cluster), not a single union. So you should bundle order, type and label fields to your cluster as well. I don't see a definition of valueType and valueLabel items of the Value struct. Assume, they are enum (I32) and long (I32), is that correct? I'm also kind of unsure, who is responsible to set the return type (long, unsigned long, double or string) - the caller or the callee?.. If it's the caller and you want the return to be a string, you have to allocate the necessary amount of memory for char *s (DSNewPtr and friends) and deallocate it later. If it's the callee, then when you're getting the return as a string, you have to deallocate that string, when done with it.

Link to comment

valueLabel is a typedef enum

 

typedef enum
{
    noValue = 0,
    InstrumentServiceName,
    NetworkPortNumber,
    ComponentID,
    ScanRate,
    Slot,
    NumChannels,
    ChannelNumber,
    CardNumber,
    FirstChannelNum,
    IEEE488_Address,
    VXI_Address,
    SerialNumber,
    SensorID,
    ScanListItem,
    Gain,
    Filter,
    Excitation,
    Cluster,
    Rack,
    PCUtype,
    EUtype,
    RefType,
    PCUpressure,
    MaxPressure,
    AcqMode,
    CalTolerance,
    CalPressure1,
    CalPressure2,
    CalPressure3,
    CalPressure4,
    CalPressure5,
    DataFormat,
    ThermoSensorType,
    ThermoSensorSubType,
    SensorType,
    MaxValue,
    SwitchState,
    OrderForAction,
    RcalFlag,
    CalDelayTime,
    CalShuttleDelayTime,
    nfr,
    frd,
    msd,
    MeasSetsPerSec,
    ServerName,
    RefPCUCRS,
    CalMode,
    ZeroEnable,
    NullEnable,
    ZeroMode,
    RezeroDelay,
    SensorSubType,
    ModuleType,
    ModuleMode,
    MeasSetsPerTempSet,
    CardType,
    Threshold,
    ControlInitState,
    ControlPolarity,
    StateEntryLogicType,
    StateEntryDelay,
    StateExitLogicType,
    StateExitDelay,
    TriggerType,
    TriggerEdge,
    ScansPerRDB,
    InterruptLevel,
    CSR_Address,
    Program_Address,
    ScannerRange,
    Hostname,
    ConnectVersion,
    From,
    To,
    ShuntValue,
    ValveConfig,
    NextToLastLabel,
    lastLabel,
    EndOfValList = 9999
} valueLabel;

 

and valueType is also defined as an Enum

typedef enum
{
    noValueType = 0,
    integer = 1,
    floating = 2,
    charString = 3,
    uLong = 4,
    maxValueType,
    EndOfValTypeList = 9999
} valueType;

 

Link to comment
On 5/6/2021 at 4:55 PM, dadreamer said:

So try to pass this cluster to your DLL (Adapt to Type -> Handles by Value) and see what will happen.

2021-05-06_19-49-04.jpg.ce6938d5b4fe58e097804a18aa798753.jpg

In theory you should receive NetworkPortNumber in lower half (I32) of v.

Note that "long order" is an int32 under Windows in any bitness, but an int64 under Linux for 64-bit!

And the i32 portion in v might be actually in the higher order half on Big Endian platforms. For current  LabVIEW versions that is however only relevant for VxWorks RT targets. All other supported platforms are Little Endian nowadays. 

Link to comment
Posted (edited)

Thanks Rolf!   I am on CentOS 7.6 64-bit on a PXIe-8135.

Got side tracked, gonna make a test code that feeds constants through that data structure and wrap that so I can know what to expect for data.

 

On 5/12/2021 at 1:04 PM, Rolf Kalbermatter said:

Note that "long order" is an int32 under Windows in any bitness, but an int64 under Linux for 64-bit!

And the i32 portion in v might be actually in the higher order half on Big Endian platforms. For current  LabVIEW versions that is however only relevant for VxWorks RT targets. All other supported platforms are Little Endian nowadays. 

Thanks, giving that a go this morning.

Edited by Ryan Vallieu
Link to comment

That info about Long on Linux being 64-bit cleared up a bunch of issues around the other function calls provided to me.  The old system being interfaced is a Solaris system returning 32-bit Long information and the sendLong function called out on the PXIe CentOS system was sending out 64-bit Long types to the Solaris system, so all the messages had 32 extra bits of information.  I've made the owner of the API aware of the issue that Long is not guaranteed to be 32-bit on a system so the API must be reworked.

Link to comment
15 hours ago, Ryan Vallieu said:

That info about Long on Linux being 64-bit cleared up a bunch of issues around the other function calls provided to me.  The old system being interfaced is a Solaris system returning 32-bit Long information and the sendLong function called out on the PXIe CentOS system was sending out 64-bit Long types to the Solaris system, so all the messages had 32 extra bits of information.  I've made the owner of the API aware of the issue that Long is not guaranteed to be 32-bit on a system so the API must be reworked.

It's only for Linux 64-bit a 64-bit value. And it's seems a bit of a GCC choice, while Microsoft chose to keep long as a 32-bit integer (and not support long long for some time instead insisting in their _int64 private type).

And while not sure about the original Sun Solaris versions which might only have existed as 32-bit anyways, the later Linux kernel based versions however almost certainly use the same logic as the other Linux versions, although Sun had a tendency of trying to customize it when they could, and sometimes even when they shouldn't :-).

 

Edited by Rolf Kalbermatter
  • Like 1
Link to comment
10 hours ago, Ryan Vallieu said:

GetValueByPointer.xnode to convert from string

Good work done! Another way would be to use MoveBlock function to read out the string data: How to determine string length when dereferencing string pointer using LabVIEW MoveBlock That way you could either read one byte at a time until you reach NULL byte or call StrLen, then allocate an U8 array of proper length and call MoveBlock finally. From what I can vaguely recall, GetValueByPointer XNode is not that fast as LabVIEW native internal functions (if that matters for you). Also I'm kind of unsure, whether you should deallocate that string, when you retrieved it in LabVIEW or the library deallocates it on its own (might be GC or some other technique used). Perhaps you could ask the developer about that or study the source codes. If you don't care, then just check for memory leaks, repeatedly retrieving a string (in a loop) and checking the memory taken by LabVIEW process.

Link to comment
3 hours ago, dadreamer said:

Good work done! Another way would be to use MoveBlock function to read out the string data: How to determine string length when dereferencing string pointer using LabVIEW MoveBlock That way you could either read one byte at a time until you reach NULL byte or call StrLen, then allocate an U8 array of proper length and call MoveBlock finally. From what I can vaguely recall, GetValueByPointer XNode is not that fast as LabVIEW native internal functions (if that matters for you). Also I'm kind of unsure, whether you should deallocate that string, when you retrieved it in LabVIEW or the library deallocates it on its own (might be GC or some other technique used). Perhaps you could ask the developer about that or study the source codes. If you don't care, then just check for memory leaks, repeatedly retrieving a string (in a loop) and checking the memory taken by LabVIEW process.

And I incidentally just had an application that I had inherited from someone and needed to debug where GetValuePointer.xnode would return with an error 7 : File Not found, when executed in a build app. Rather than digging into xnode handling and find out why on earth it was returning such an error (for a reportedly valid pointer created with DSNewPtr) I simply replaced the whole thing with a call to StrLen and MoveBlock and was done with it!

Edited by Rolf Kalbermatter
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.