Set / GetUserData()

Questions regarding the use of the ANSI C SDK for Server or Client development or integration into customer products ...

Moderator: uasdkc

Post Reply
macsurfer
Full Member
Full Member
Posts: 8
Joined: 09 Aug 2013, 12:26

Set / GetUserData()

Post by macsurfer »

Hello Forum,

thanks for the help you gave me yesterday. There´s another question of mine and I thought it would be good to start a new topic.

Could you please explain the use of the OpcUa_BaseNode_SetUserData(...) and GetUserData()- Functions?
In example lesson03 these are used to "add a user data struct to a node" using the pointers pNewMachine and pNewHeaterSwitch.
But after this step I can´t follow the example, what are these pointers used for?

Code: Select all

/* Set new machine struct as user data of the new node */
OpcUa_BaseNode_SetUserData(pObject, pNewMachine);
...
I my code I could locate an error which has to do with an empty pointer of pUserData.
Right after that call I get an exception in the uaserver_monitoring.c
When I disable the HistorizeItem()- the error doesnt occur.
GetUserData_Code.jpg
uaserver_monitoring_Screen.jpg

In my own example there are only two nodes, MeinBasisObjekt and MeineVariable( See topic SetDataLogger() for Screenshot )
How should I use the SetUserData() and which local/ global variables would be needed?

I couldnt find anything in the Documentation:
SetUserData_Screen.jpg
Thanks in advance!

Michael

User avatar
Support Team
Hero Member
Hero Member
Posts: 3069
Joined: 18 Mar 2011, 15:09

Re: Set / GetUserData()

Post by Support Team »

Hello Michael,

you might want to have a look at the example lesson 7 - this is where the creation of a data logger and history variables is explained.

The UserData is a pointer to arbitrary data that can be set by a provider. In our example lesson 7 we store a pointer to a 'TemperatureSensor' structure which contains the handle that is needed for accessing the history of the variable and writing it to the data logger.

Best regards,
Unified Automation Support Team

macsurfer
Full Member
Full Member
Posts: 8
Joined: 09 Aug 2013, 12:26

Re: Set / GetUserData()

Post by macsurfer »

Hello Forum,

thanks for that quick reply.
I assume you meant the following code line in example 7:

Code: Select all

TemperatureSensor *pTemperatureSensor = (TemperatureSensor*) OpcUa_BaseNode_GetUserData(pNode);
When the node is created I have to use the OpcUa_BaseNode_SetUserData()- Fct to assign the UserData, not?

In another thread I asked if in that example 7 not only the temperature but also the heater switch could be historized, therefore a colleague changed that line to:

Code: Select all

pUserData = (UserDataCommon*)OpcUa_BaseNode_GetUserData(pNode);
The UserDataCommon struct now contains:

Code: Select all

struct _UserDataCommon
{
    UserDataType Type;
    /* Data logging information */
    OpcUa_NodeId nodeId;        /* nodeid of variable */
    OpcUa_Int    hDataLogItem;  /* handle of datalogitem */
};
typedef struct _UserDataCommon UserDataCommon;
My question has to do with the "counterpart" of GetUserData() which I find in custom_provider.c, but these lines I don´t understand:

Code: Select all

...
    OpcUa_NodeId_Initialize(&pNewSensor->nodeId);
    OpcUa_NodeId_CopyTo(a_pStartingNodeId, &pNewSensor->nodeId);

    OpcUa_BaseNode_SetUserData(pVariable, pNewSensor);

    *a_ppNewTemperatureSensor = pNewSensor;
I assume that pNewSensor is the UserData- Pointer but why is it assigned to *a_ppNewTemperatureSensor?
Is this only needed in the example?

In my adapted example with only one basenode and one variable the "CreateNode"- part looks like this:

Code: Select all

UaServer_CreateNumericNodeIdEx(&parentNodeId, NurEinKnoten_Objects_MeinBasisObjekt,     
    g_UaProviderNurEinKnoten_uNamespaceIndex1);
    UaServer_GetNode(pAddressSpace,
                    &parentNodeId,
                    &pParent);


    UaServer_CreateNumericNodeIdEx(&nodeInfo.TypeDefinition, OpcUaId_PropertyType, 0);
    UaServer_CreateNumericNodeIdEx(&nodeInfo.NodeId, NurEinKnoten_Objects_MeinBasisObjekt_MeineVariable,
    g_UaProviderNurEinKnoten_uNamespaceIndex1);
    nodeInfo.NodeClass = OpcUa_NodeClass_Variable;
    UaServer_CreateLocalizedText(&nodeInfo.sDisplayName, g_NurEinKnotenStringTable1[4], g_NurEinKnotenStringTable1[3]);
    UaServer_CreateQualifiedNameEx(&nodeInfo.sBrowseName, g_NurEinKnotenStringTable1[3],
    g_UaProviderNurEinKnoten_uNamespaceIndex1);
    UaServer_CreateLocalizedText(&nodeInfo.sDescription, g_NurEinKnotenStringTable1[4], g_NurEinKnotenStringTable1[0]);

    uStatus = UaProvider_NurEinKnoten_CreateNode(pAddressSpace,
        &pNode,
        pParent,
        &referenceTypeId,
        &nodeInfo);

// This part I tried to adapt like here with a new pointer
	/* Set new struct as user data of the new node and store it in the global variable*/
	//OpcUa_BaseNode_SetUserData(pNode, g_pMeineVariable);
But how is the right way to do it?

Thanks,
Michael

User avatar
Support Team
Hero Member
Hero Member
Posts: 3069
Joined: 18 Mar 2011, 15:09

Re: Set / GetUserData()

Post by Support Team »

Hello,

with OpcUa_BaseNode_SetUserData() to assign any user data to a node, with OpcUa_BaseNode_GetUserData() you can retrieve it.

Assigning the user data pointer to *a_ppNewTemperatureSensor is just done in the example, this might not be needed depending on your specific implementation.

Changing the UserDataCommon struct to contain data logger information could lead to a problem, as that would imply that every node containing user data is historized. You have to think of UserDataCommon as an 'abstract base class' and of types like TemperatureSensor as 'structs that inherit UserDataCommon'. This way we can
- retrieve the user data pointer (void*)
- cast it to UserDataCommon (as we know that every user data we have is 'derived' from UserDataCommon)
- look at the type and cast the pointer to the actual structure (e.g. if the type is UserDataTemperature, we can cast it to TemperatureSensor*)

In your example you will have to create the user data stored in g_pMeineVariable first before assigning it to the variable. This could be done as follows:

Code: Select all

    g_pMeineVariable = (HeaterSwitch*)OpcUa_Alloc(sizeof(HeaterSwitch));
    g_pMeineVariable->Type = UserDataHeaterSwitch;
    ...
    OpcUa_BaseNode_SetUserData(pNode, g_pMeineVariable);
It could be helpful to define a new user data type 'HeaterSwitch' that contains the item's data logger handle, you will then have to adapt the functions CustomProvider_HistoryReadRawModifiedAsync, CustomProvider_SampleData and CustomProvider_WriteAsync accordingly. In CustomProvider_SetupDataLogger() you will have to retrieve the node and create a data logger item for it, just like for the TemperatureSensor.

Best regards,
Unified Automation Support Team

macsurfer
Full Member
Full Member
Posts: 8
Joined: 09 Aug 2013, 12:26

Re: Set / GetUserData()

Post by macsurfer »

Hello,

finally, it´s done!
The historic access does work with the simple model that has been created with the UAModeller.
I filled in the additional code blocks and now it works just fine.
Though I am casting all fct calls to UserDataCommon, but maybe we can talk about that another time, for now I am happy with the solution we have found.

Code: Select all

pUserData = (UserDataCommon*)OpcUa_BaseNode_GetUserData(pNode);

Code: Select all

/* Enum of custom user data types */
enum _UserDataType
{
    UserDataMeineVariable
};
typedef enum _UserDataType UserDataType;

/* All user data structs contain the same header with type information.
 * This concept is application specific and only an example.
 * You can store whatever you like in UserData.
 */
struct _UserDataCommon
{
    UserDataType Type; /* Currently only the type info is needed in the common header */
    /* Data logging information */
    OpcUa_NodeId nodeId;        /* nodeid of variable */
    OpcUa_Int    hDataLogItem;  /* handle of datalogitem */
};
typedef struct _UserDataCommon UserDataCommon;

struct _MeineVariable
{
    /* User data header */
    UserDataType Type;
    /* Data logging information */
    OpcUa_NodeId nodeId;        /* nodeid of variable */
    OpcUa_Int    hDataLogItem;

    /* Protocol information */
    OpcUa_Double       *pUserValue;
};
typedef struct _MeineVariable MeineVariable;


/*============================================================================
 * Global pointer and variables */

extern MeineVariable		*g_pMeineVariable;	
extern OpcUa_Double			g_MeineVariableWert;

Thanks again for your help.

Michael
PrimaryKey DataType Value StatusCode SourceTimeStamp ServerTimeStamp
1 0x000b 100 0x00000000 2013-08-22T16:00:19.052Z 2013-08-22T16:00:19.052Z
1 0x000b 123 0x00000000 2013-08-22T16:01:27.250Z 2013-08-22T16:01:27.250Z
1 0x000b 500 0x00000000 2013-08-22T16:01:49.282Z 2013-08-22T16:01:49.282Z
1 0x000b 450 0x00000000 2013-08-22T16:02:00.798Z 2013-08-22T16:02:00.798Z
1 0x000b 420 0x00000000 2013-08-22T16:02:05.806Z 2013-08-22T16:02:05.806Z
1 0x000b 300 0x00000000 2013-08-22T16:02:09.811Z 2013-08-22T16:02:09.811Z
1 0x000b 900 0x00000000 2013-08-22T16:02:16.821Z 2013-08-22T16:02:16.821Z
1 0x000b 200 0x00000000 2013-08-22T16:02:23.832Z 2013-08-22T16:02:23.832Z
1 0x000b 123 0x00000000 2013-08-22T16:03:25.640Z 2013-08-22T16:03:25.640Z

Post Reply