How to connect own Variables in UaModeler generated files?

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

Moderator: uasdkc

Post Reply
mLang_de
Sr. Member
Sr. Member
Posts: 14
Joined: 31 Jul 2013, 12:51

How to connect own Variables in UaModeler generated files?

Post by mLang_de »

Hello support team,

currently I try to connect a UaModeler generated Node with a real world data.
I already implemented all lessons from the SDK and look lesson No.3 for connecting Nodes with real world data.
But in the example you don't use automatic generatic files.
I try severals way with global variables, allocating memory...
What is the right way to solve my problem?
Here are a snippets from the generated code.

Code: Select all

   /* Wert 1, PropertyOf Device_Test */
    UaServer_CreateNumericNodeIdEx(&nodeInfo.TypeDefinition, OpcUaId_PropertyType, 0);
    UaServer_CreateNumericNodeIdEx(&nodeInfo.NodeId, Test_address_space_Objects_netPROXY_device_0_group_0_Objects_Device_Test_Wert_1, g_UaProviderTest_address_space_uNamespaceIndex1);
    nodeInfo.NodeClass = OpcUa_NodeClass_Variable;
    UaServer_CreateLocalizedText(&nodeInfo.sDisplayName, g_Test_address_spaceStringTable1[77], g_Test_address_spaceStringTable1[74]);
    UaServer_CreateQualifiedNameEx(&nodeInfo.sBrowseName, g_Test_address_spaceStringTable1[74], g_UaProviderTest_address_space_uNamespaceIndex1);
    UaServer_CreateLocalizedText(&nodeInfo.sDescription, g_Test_address_spaceStringTable1[77], g_Test_address_spaceStringTable1[0]);
    uStatus = UaProvider_Test_address_space_CreateNode(pAddressSpace,
        &pNode,
        pParent,
        &referenceTypeId,
        &nodeInfo);
    OpcUa_GotoErrorIfBad(uStatus);
    OpcUa_Variable_SetAccessLevel(pNode, OpcUa_AccessLevels_CurrentReadOrWrite);
#if UASERVER_SUPPORT_AUTHORIZATION == OPCUA_CONFIG_NO
    OpcUa_Variable_SetUserAccessLevel(pNode, OpcUa_AccessLevels_CurrentReadOrWrite);
#endif
    OpcUa_Variable_SetDataType_Numeric(pNode, OpcUaId_Double, 0);
    OpcUa_Variable_SetHistorizing(pNode, OpcUa_False);
    OpcUa_Variable_SetMinimumSamplingInterval(pNode, 0);
    OpcUa_Variable_SetValueRank(pNode, OpcUa_ValueRanks_Scalar);
    OpcUa_Variant_Initialize(&value);
    value.Datatype = OpcUaType_Double;
    value.ArrayType = OpcUa_VariantArrayType_Scalar;
    value.Value.Double = (OpcUa_Double) 0.0;

    /* HERE */

    OpcUa_Variable_AttachValue(pNode, &value);
I make serverals attempts at the marked place /* HERE */.
But the value is no pointer, in your example you work with pointer.

I'm looking forward to hearing from you.

King regards
mLang_de

mLang_de
Sr. Member
Sr. Member
Posts: 14
Joined: 31 Jul 2013, 12:51

Re: How to connect own Variables in UaModeler generated file

Post by mLang_de »

Hello again,

I will describe my problem more detailed.

I will add my own global variable at an Opc-Variable.
Here are the code for my global structs:

Code: Select all

/*============================================================================
 * Structs and global variables for managing custom user data
 *===========================================================================*/
/* Enum of custom user data types */
enum _UserDataType
{
    UserDataTemperature,
    UserDataMachine,
    UserDataMachineSwitch,
    UserDataObject,
    UserDataObjectElement
};
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 */
};
typedef struct _UserDataCommon UserDataCommon;


struct _NpxObjectElement
{
    /* User data header */
    UserDataType Type;

    /* Protocol information */
    OpcUa_Double      *pValue;
};
typedef struct _NpxObjectElement NpxObjectElement;

struct _NpxObject
{
    /* User data header */
    UserDataType Type;

    /* Protocol information */
    NpxObjectElement	*pElement;

    OpcUa_NodeId         nodeId;
};
typedef struct _NpxObject NpxObject;
In the file uaprovider_NAME.c are the global variables definitions:

Code: Select all

NpxObject			*g_pMyNpxObject = OpcUa_Null;
NpxObjectElement	 g_dMyNpxObjectElement;
I edit the functionparameters from the createAddressSpace-fct.. I think it isn't really necessary, but I take the custom_provider-example for assist.
Therefore I add the new created NpxObject-reference to my gloabal variables here:

Code: Select all

static OpcUa_StatusCode UaProvider_Hilscher_types_CreateAddressSpace()
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_Hilscher_types_CreateAddressSpace");
    NpxObject *pNewNpxObject = OpcUa_Null;
	uStatus = UaProvider_Hilscher_types_CreateAddressSpace1(&pNewNpxObject);
    OpcUa_GotoErrorIfBad(uStatus);

    g_pMyNpxObject = pNewNpxObject;
    g_pMyNpxObject->pElement = &g_dMyNpxObjectElement;

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
At the create_AddressSpace-fct I add at first the pointer for my struct. Therefore I add the global variables as extern (file: uaprovider_NAME_nodes1.c):

Code: Select all

OpcUa_StatusCode UaProvider_Hilscher_types_CreateAddressSpace1(NpxObject **a_ppNewNpxObject)
{
    UaServer_AddressSpace*      pAddressSpace       = &(g_pHilscher_typesProvider->AddressSpace);
    UaServer_AddressSpace*      pServerAddressSpace = OpcUa_Null;

    OpcUa_BaseNode        *pNode;
    OpcUa_BaseNode        *pParent;
    OpcUa_DataVariable    *pVariable = OpcUa_Null;
    OpcUa_NodeId           nodeId;
    OpcUa_NodeId           referenceTypeId;
    OpcUa_NodeId           dataTypeId;
    OpcUa_NodeId           typeNodeId;
    OpcUa_NodeId           parentNodeId;
    OpcUa_Variant          value;
    OpcUa_LocalizedText    localizedText;
    OpcUa_UInt32          *pArrayDimensions;
    OpcUa_Int32            i = 0;
    OpcUa_StatusCode       statusCode = 0;
    UaProvider_Hilscher_types_NodeInfo nodeInfo;

    NpxObject			  *pNewNpxObject 		= OpcUa_Null;
    NpxObjectElement 	  *pNewNpxObjectElement = OpcUa_Null;
    OpcUa_Variant         *pValue               = OpcUa_Null;
.
.
.
After the initialisation I create some object and folders. After that I will add my own defined struct. In the first part a object is created for organize the new object.:

Code: Select all

    /* Device Desciption, ComponentOf Objects */
    UaServer_CreateNumericNodeIdEx(&parentNodeId, Hilscher_types_Objects_netPROXY_device_0_group_0_Objects, g_UaProviderHilscher_types_uNamespaceIndex1);
    UaServer_GetNode(pAddressSpace,
                    &parentNodeId,
                    &pParent);

    UaServer_CreateNumericNodeIdEx(&nodeInfo.TypeDefinition, Hilscher_types_NpxDeviceDescriptionTypeId, g_UaProviderHilscher_types_uNamespaceIndex1);
    UaServer_CreateNumericNodeIdEx(&nodeInfo.NodeId, Hilscher_types_Objects_netPROXY_device_0_group_0_Objects_Device_Desciption, g_UaProviderHilscher_types_uNamespaceIndex1);
    nodeInfo.NodeClass = OpcUa_NodeClass_Object;
    UaServer_CreateLocalizedText(&nodeInfo.sDisplayName, g_Hilscher_typesStringTable1[77], g_Hilscher_typesStringTable1[12]);
    UaServer_CreateQualifiedNameEx(&nodeInfo.sBrowseName, g_Hilscher_typesStringTable1[12], g_UaProviderHilscher_types_uNamespaceIndex1);
    UaServer_CreateLocalizedText(&nodeInfo.sDescription, g_Hilscher_typesStringTable1[77], g_Hilscher_typesStringTable1[0]);
    uStatus = UaProvider_Hilscher_types_CreateNode(pAddressSpace,
        &pNode,
        pParent,
        &referenceTypeId,
        &nodeInfo);
    OpcUa_GotoErrorIfBad(uStatus);
    OpcUa_Object_SetEventNotifier(pNode, 0);
Now I will creat the own objects:

Code: Select all

    /******* NEW ****************/

    /* Create and initialize new object struct */
    /*NpxObject**/
    pNewNpxObject = (NpxObject*)OpcUa_Alloc(sizeof(NpxObject));
    OpcUa_ReturnErrorIfAllocFailed(pNewNpxObject);
    OpcUa_MemSet(pNewNpxObject, 0, sizeof(NpxObject));
    pNewNpxObject->Type = UserDataObject;

    /* Device Desciption, ComponentOf Objects */
    UaServer_CreateNumericNodeIdEx(&parentNodeId, Hilscher_types_Objects_netPROXY_device_0_group_0_Objects, g_UaProviderHilscher_types_uNamespaceIndex1);
    UaServer_GetNode(pAddressSpace,
                    &parentNodeId,
                    &pParent);

    UaServer_CreateNumericNodeIdEx(&nodeInfo.TypeDefinition, Hilscher_types_NpxDeviceDescriptionTypeId, g_UaProviderHilscher_types_uNamespaceIndex1);
    UaServer_CreateNumericNodeIdEx(&nodeInfo.NodeId, Hilscher_types_Objects_netPROXY_device_0_group_0_Objects_Device_Desciption+100, g_UaProviderHilscher_types_uNamespaceIndex1);
    nodeInfo.NodeClass = OpcUa_NodeClass_Object;
    UaServer_CreateLocalizedText(&nodeInfo.sDisplayName, g_Hilscher_typesStringTable1[77], "Mein Objekt");
    UaServer_CreateQualifiedNameEx(&nodeInfo.sBrowseName, "Mein_Objekt", g_UaProviderHilscher_types_uNamespaceIndex1);
    UaServer_CreateLocalizedText(&nodeInfo.sDescription, g_Hilscher_typesStringTable1[77], g_Hilscher_typesStringTable1[0]);
    uStatus = UaProvider_Hilscher_types_CreateNode(pAddressSpace,
        &pNode,
        pParent,
        &referenceTypeId,
        &nodeInfo);
    OpcUa_GotoErrorIfBad(uStatus);
    OpcUa_Object_SetEventNotifier(pNode, 0);

    OpcUa_NodeId_CopyTo(&nodeInfo.NodeId, &pNewNpxObject->nodeId);

    /* Set new object struct as user data of the new node */
    OpcUa_BaseNode_SetUserData(pNode, pNewNpxObject);


    /* Create and initialize new object element struct */
    pNewNpxObjectElement = (NpxObjectElement*)OpcUa_Alloc(sizeof(NpxObjectElement));
    OpcUa_ReturnErrorIfAllocFailed(pNewNpxObjectElement);
    OpcUa_MemSet(pNewNpxObjectElement, 0, sizeof(NpxObjectElement));
    pNewNpxObjectElement->Type = UserDataObjectElement;

    /* Create ObjectElement property */
    nodeInfo.NodeId.Identifier.Numeric++;
    UaServer_CreateNumericNodeIdEx(&nodeInfo.TypeDefinition, OpcUaId_PropertyType, 0);
    uStatus = UaServer_CreateDataVariable(pAddressSpace,
                                          &pVariable,
                                          pNode,
                                          nodeInfo.NodeId.Identifier.Numeric,
                                          g_UaProviderHilscher_types_uNamespaceIndex1,
                                          "Element_1");
    OpcUa_GotoErrorIfBad(uStatus);
    OpcUa_Variable_SetDataType_Numeric(pVariable, OpcUaId_Double, 0);
    pValue = OpcUa_Variable_GetValue(pVariable);
    pValue->Datatype = OpcUaType_Double;
    pValue->Value.Double = 48.0;

    OpcUa_BaseNode_SetUserData(pVariable, pNewNpxObjectElement);
    pNewNpxObject->pElement = pNewNpxObjectElement;


    *a_ppNewNpxObject = pNewNpxObject;


    /******* NEW ENDE ***********/

After compiling, I can browse the new objects and my global struct content all pointer-address's and nodeID's. Only my variable g_pMyNpxObject->pElement->pValue is everytime 0. I expect 48.
What and where is my mistake?
A Screenshot from the UaExpert I added as attachment.

Thanks in advance!

Kind regards
mLang_de

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

Re: How to connect own Variables in UaModeler generated file

Post by Support Team »

Hello,

could you please send your modified source files to support@unifiedautomation.com. It will be easier to help you, if we don't have to copy the posted code snippets into our code.

Best regards
Support Team

mLang_de
Sr. Member
Sr. Member
Posts: 14
Joined: 31 Jul 2013, 12:51

Re: How to connect own Variables in UaModeler generated file

Post by mLang_de »

Hello,

here is my solution that works.
The global struct are equal to my second post.
The first problem was a wrong global declaration.

In the file uaprovider_NAME.c are the global variables definitions:
OLD:

Code: Select all

NpxObject         *g_pMyNpxObject = OpcUa_Null;
NpxObjectElement    g_dMyNpxObjectElement;
NEW:

Code: Select all

NpxObject			*g_pMyNpxObject = OpcUa_Null;
OpcUa_Double		 g_dMyNpxObjectElementValue = 0.0;
Therefore I edited the createAddressSpace-fct:
OLD:

Code: Select all

    static OpcUa_StatusCode UaProvider_Hilscher_types_CreateAddressSpace()
    {
    OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_Hilscher_types_CreateAddressSpace");
        NpxObject *pNewNpxObject = OpcUa_Null;
        uStatus = UaProvider_Hilscher_types_CreateAddressSpace1(&pNewNpxObject);
        OpcUa_GotoErrorIfBad(uStatus);

        g_pMyNpxObject = pNewNpxObject;
        g_pMyNpxObject->pElement = &g_dMyNpxObjectElement;

    OpcUa_ReturnStatusCode;
    OpcUa_BeginErrorHandling;
    OpcUa_FinishErrorHandling;
    }
NEW:

Code: Select all

static OpcUa_StatusCode UaProvider_Hilscher_types_CreateAddressSpace()
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_Hilscher_types_CreateAddressSpace");
    NpxObject *pNewNpxObject = OpcUa_Null;
	uStatus = UaProvider_Hilscher_types_CreateAddressSpace1(&pNewNpxObject);
    OpcUa_GotoErrorIfBad(uStatus);

        g_pMyNpxObject = pNewNpxObject;
	g_pMyNpxObject->pElement->pValue = &g_dMyNpxObjectElementValue;
	g_dMyNpxObjectElementValue = 22.0;

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
The fourth, fifth and sixth code snippet I didn't modify.

At least I edited the read- und write-method.
uaprovider_NAME_read.c

Code: Select all

IFMETHODIMP(UaProvider_Hilscher_types_ReadAsync)(UaServer_ProviderReadContext* a_pReadCtx)
{
    OpcUa_ReadRequest       *pReq;
    OpcUa_ReadResponse      *pRes;
    int                     i;
    OpcUa_BaseNode          *pNode;
    OpcUa_NodeId            *pNodeId        = OpcUa_Null;
    OpcUa_ReadValueId       *pNodeToRead;
    UaServer_AddressSpace   *pAddressSpace  = &(g_pHilscher_typesProvider->AddressSpace);

    OpcUa_ReturnErrorIfArgumentNull(a_pReadCtx);

    pReq = a_pReadCtx->pRequest;
    pRes = a_pReadCtx->pResponse;

    /* we will send exactly one callback */
    UaServer_Atomic_Increment(&a_pReadCtx->nOutstandingCbs);

    for (i = 0; i < pReq->NoOfNodesToRead; i++)
    {
        pNodeToRead = &pReq->NodesToRead[i];
        pNodeId = &pNodeToRead->NodeId;

        if (pNodeId->NamespaceIndex != g_UaProviderHilscher_types_uNamespaceIndex1) continue;

        UaServer_GetNode(pAddressSpace, pNodeId, &pNode);
        if (pNode)
        {
        	UserDataCommon *pUserData = OpcUa_Null;

            /* check if value is readable */
            if (pNodeToRead->AttributeId == OpcUa_Attributes_Value
                && OpcUa_BaseNode_GetType(pNode) == eVariable
                && (OpcUa_Variable_GetAccessLevel(pNode) & OpcUa_AccessLevels_CurrentRead) == 0)
            {
                pRes->Results[i].StatusCode = OpcUa_BadNotReadable;
                continue;
            }
#if UASERVER_SUPPORT_AUTHORIZATION
            /* check if current user is allowed to read */
            if ((pNodeToRead->AttributeId == OpcUa_Attributes_Value
                 && !g_pHilscher_typesProviderCBInterface->UserMgt_IsReadable(OpcUa_BaseNode_GetINode(pNode), a_pReadCtx->pSession->UserIdentityData))
                || (pNodeToRead->AttributeId != OpcUa_Attributes_Value
                 && !g_pHilscher_typesProviderCBInterface->UserMgt_IsAttributeReadable(OpcUa_BaseNode_GetINode(pNode), a_pReadCtx->pSession->UserIdentityData)))
            {
                pRes->Results[i].StatusCode = OpcUa_BadNotReadable;
                continue;
            }
#endif
            pUserData = (UserDataCommon*)OpcUa_BaseNode_GetUserData(pNode);
            if (pUserData != OpcUa_Null &&
                a_pReadCtx->pRequest->NodesToRead[i].AttributeId == OpcUa_Attributes_Value)
            {
                switch (pUserData->Type)
                {
                case UserDataObject:
                    {
                    	NpxObject *pNpxObject = (NpxObject*)pUserData;
                        pRes->Results[i].Value.Datatype = OpcUaType_Double;
                        pRes->Results[i].Value.Value.Double = *pNpxObject->pElement->pValue;
                        pRes->Results[i].StatusCode = OpcUa_Good;
                    	*g_pMyNpxObject->pElement->pValue += 1;		// increment every read-time
                        break;
                    }
                default:
                    {
                        pRes->Results[i].StatusCode = OpcUa_BadNotReadable;
                        break;
                    }
                }

                /* Set timestamps */
                if (OpcUa_IsGood(pRes->Results[i].StatusCode))
                {
                    if (a_pReadCtx->pRequest->TimestampsToReturn == OpcUa_TimestampsToReturn_Source ||
                        a_pReadCtx->pRequest->TimestampsToReturn == OpcUa_TimestampsToReturn_Both)
                    {
                        pRes->Results[i].SourceTimestamp = OpcUa_DateTime_UtcNow();
                    }
                    if (a_pReadCtx->pRequest->TimestampsToReturn == OpcUa_TimestampsToReturn_Server ||
                        a_pReadCtx->pRequest->TimestampsToReturn == OpcUa_TimestampsToReturn_Both)
                    {
                        pRes->Results[i].ServerTimestamp = OpcUa_DateTime_UtcNow();
                    }
                }
            }
            else
            {
                /* Use the SDK's helper function to process the request */
                UaServer_ReadInternal(pNode, a_pReadCtx, i);
            }
        }
        else
        {
            pRes->Results[i].StatusCode = OpcUa_BadNodeIdUnknown;
        }
    }

    /* send callback */
    UaServer_ReadComplete(a_pReadCtx);

    return OpcUa_Good;
}

uaprovider_NAME_write.c:

Code: Select all

IFMETHODIMP(UaProvider_Hilscher_types_WriteAsync)(UaServer_ProviderWriteContext* a_pWriteCtx)
{
    OpcUa_WriteRequest  *pReq;
    OpcUa_WriteResponse *pRes;
    int                  i;
    OpcUa_BaseNode      *pNode;
    OpcUa_NodeId        *pNodeId;
    OpcUa_Variant       *pValue;
    OpcUa_WriteValue    *pNodeToWrite;
    UaServer_AddressSpace   *pAddressSpace      = &(g_pHilscher_typesProvider->AddressSpace);

    OpcUa_ReturnErrorIfArgumentNull(a_pWriteCtx);

    pReq = a_pWriteCtx->pRequest;
    pRes = a_pWriteCtx->pResponse;

    /* we will send exactly one callback */
    UaServer_Atomic_Increment(&a_pWriteCtx->nOutstandingCbs);

    for (i = 0; i < pReq->NoOfNodesToWrite; i++)
    {
        pNodeToWrite = &pReq->NodesToWrite[i];
        pNodeId = &pNodeToWrite->NodeId;

        if (pNodeId->NamespaceIndex != g_UaProviderHilscher_types_uNamespaceIndex1) continue;

        pValue = &pNodeToWrite->Value.Value;

        UaServer_GetNode(pAddressSpace, pNodeId, &pNode);
        if (pNode)
        {
        	UserDataCommon *pUserData = OpcUa_Null;

#if UASERVER_SUPPORT_AUTHORIZATION
            if ((pNodeToWrite->AttributeId == OpcUa_Attributes_Value
                 && !g_pHilscher_typesProviderCBInterface->UserMgt_IsWritable(OpcUa_BaseNode_GetINode(pNode), a_pWriteCtx->pSession->UserIdentityData))
                || (pNodeToWrite->AttributeId != OpcUa_Attributes_Value
                 && !g_pHilscher_typesProviderCBInterface->UserMgt_IsAttributeWritable(OpcUa_BaseNode_GetINode(pNode), a_pWriteCtx->pSession->UserIdentityData)))
            {
                pRes->Results[i] = OpcUa_BadNotWritable;
                continue;
            }
#endif

            pUserData = (UserDataCommon*)OpcUa_BaseNode_GetUserData(pNode);
			if (pUserData != OpcUa_Null &&
				a_pWriteCtx->pRequest->NodesToWrite[i].AttributeId == OpcUa_Attributes_Value)
			{
				OpcUa_Variant *pValue = &a_pWriteCtx->pRequest->NodesToWrite[i].Value.Value;

				switch (pUserData->Type)
				{
				case UserDataObject:
					{
						if (pValue->ArrayType == OpcUa_VariantArrayType_Scalar &&
							pValue->Datatype == OpcUaType_Double)
						{
							if(pValue->Value.Double < 500.0)
							{
								NpxObject *pNpxObject = (NpxObject*)pUserData;
								*pNpxObject->pElement->pValue = pValue->Value.Double;
								pRes->Results[i] = OpcUa_Good;
							}
							else
							{
								pRes->Results[i] = OpcUa_BadNotWritable;
							}
						}
						else
						{
							pRes->Results[i] = OpcUa_BadTypeMismatch;
						}
						break;
					}
				default:
					{
						pRes->Results[i] = OpcUa_BadNotWritable;
						break;
					}
				}


            //UaServer_WriteInternal(pNode, a_pWriteCtx, i);
			}
        }
        else
        {
            pRes->Results[i] = OpcUa_BadNodeIdUnknown;
        }
    }

    /* send callback */
    UaServer_WriteComplete(a_pWriteCtx);

    return OpcUa_Good;
}
For the moment my code does work. With the UaExpert I can read and write the OPC-Variables and my global variables, too.
I hope my solution isn't a random outcome. So I hope the support look at my code and give a statement about the code snippets.

Regards
mLang_de

Post Reply