How to implement Query Service to Server?

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

Moderator: uasdkcpp

Post Reply
Lost+Found
Hero Member
Hero Member
Posts: 20
Joined: 07 Feb 2013, 11:01

How to implement Query Service to Server?

Post by Lost+Found »

Hello,

What steps should I take to implement the Query service to server? How can I add my own QueryFirst/QueryNext handlers? There is a hard-coded array of supported services UaServer_SupportedServices[] in UAModule/uaserver.cpp. Should this class be copy-pasted or is there an easier way to go?

Thank You

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

Re:How to implement Query Service to Server?

Post by Support Team »

Hello Toni,

This is the first request for supporting the QueryServiceSet in the C++ SDK.

We checked the necessary changes for passing on a Query request to the user specific code and there are several places that need to be extended.

There is the service handling in the Stub and the Server object, the continuation point handling in the session and the code that is necessary to pass on the query request to your code.

We can provide you a patch with a first prototype next week if you are interested.

What client would be used for testing on your side?

Best Regards,
Unified Automation Support Team
Last edited by Support Team on 07 Feb 2013, 10:48, edited 1 time in total.
Best regards
Unified Automation Support Team

Lost+Found
Hero Member
Hero Member
Posts: 20
Joined: 07 Feb 2013, 11:01

Re:How to implement Query Service to Server?

Post by Lost+Found »

Hi,
Thanks for quick reply. Yes, I'd be very in interested in this patch, but to which version would it be? I am currently developing over 1.2.1, and I am not sure if I can to migrate to 1.3.0. I suspect there may not be enough work resources left to adapt with the changes in the Event Management.

The query client would be a quick and dirty test client that is set up from scratch. If you can suggest some, I'd be interested aswell?-)

Thanks
Toni

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

Re:How to implement Query Service to Server?

Post by Support Team »

Hello Toni,

The patch will be part of the upcoming service release for version 1.3 (V1.3.1). But we applied the patch also to V1.2.1 and can provide you a patch for this version as well. Please send an email to support@unifiedautomation.com to get the patch.

Here are the steps to get the query call in your own code based on the \"server hello world\" example:

Step (1)
Copy patch \"patchQuery_CppSdk1_2_1.patch\" to \"SdkInstallsrc\"
Apply patch

Step(2)
Copy the following files to
SdkInstallincludeuaserver
From SdkInstallsrcuaserveruaservercppuamodule
  • uamonitoreditem.h
  • uaserver.h
  • uasubscription.h
  • uasubscriptionmanager.h
  • uatransactionmanager.h
From SdkInstallsrcuaserveruaservercppcoremodule
  • continuationpoint.h
  • session.h
Copy the following file to
SdkInstallincludeuabase
From SdkInstallsrcuabaseuabasecpp
  • uaarraytemplates.h
Step(3)
Compile SDK

Step(4)
Add the following classes to your project

Code header:

Code: Select all

#ifndef __QUERY_H__
#define __QUERY_H__

#include \"uatransactionmanager.h\"
#include \"uaserver.h\"
#include \"opcserver.h\"

// Class implementing beginQuery() by overwriting  UaTransactionManager::beginQuery()
class MyUaTransactionManager: public UaTransactionManager
{
public:
    MyUaTransactionManager(ServerManager*, UaSubscriptionManager*);
    virtual ~MyUaTransactionManager();

    // Overwrite UaTransactionManager::beginQuery()
    UaStatus beginQuery(UaQueryContext*);
};

// Class used to create the custom TransactionManager by overwriting UaServer::createUaTransactionManager
class MyUaServer: public UaServer
{
public:
    MyUaServer();
    virtual ~MyUaServer();
    UaTransactionManager* createUaTransactionManager(ServerManager*, UaSubscriptionManager*);
};

// Callback interface implementation for OpcServer class to create custom UaServer object
class MyOpcServerCallback: public OpcServerCallback
{
public:
    MyOpcServerCallback();
    virtual ~MyOpcServerCallback();
    Session* createSession(OpcUa_Int32 sessionID, const UaNodeId &authenticationToken);
    UaStatus logonSessionUser(Session* pSession, UaUserIdentityToken* pUserIdentityToken);
    UaServer* createUaServer();
};

#endif // __QUERY_H__
Code cpp:

Code: Select all

#include \"query.h\"

///////////////////////////////////////////////////////////
///// Class implementing the custom query function ////////
MyUaTransactionManager::MyUaTransactionManager(
    ServerManager* pServerManager, 
    UaSubscriptionManager* pUaSubscriptionManager)
: UaTransactionManager(pServerManager, pUaSubscriptionManager) 
{
}
MyUaTransactionManager::~MyUaTransactionManager()
{
}

UaStatus MyUaTransactionManager::beginQuery(UaQueryContext* pUaQueryContext)
{
    // Implement query here
    return OpcUa_BadServiceUnsupported;
}
///////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////
///// Helper class to create custom transaction manager ///
MyUaServer::MyUaServer()
{
}
MyUaServer::~MyUaServer()
{
}
UaTransactionManager* MyUaServer::createUaTransactionManager(
    ServerManager* pServerManager, 
    UaSubscriptionManager* pUaSubscriptionManager)
{
    // Create custom UaTransactionManager object
    return new MyUaTransactionManager(pServerManager, pUaSubscriptionManager);
}
///////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////
// -- Callback for OpcServer object to create custom ------
// -- objects and to implement user authentication --------
MyOpcServerCallback::MyOpcServerCallback()
{
}
MyOpcServerCallback::~MyOpcServerCallback()
{
}
Session* MyOpcServerCallback::createSession(
    OpcUa_Int32 sessionID, 
    const UaNodeId &authenticationToken)
{
    return new UaSession(sessionID, authenticationToken);
}
UaStatus MyOpcServerCallback::logonSessionUser(
    Session* pSession, 
    UaUserIdentityToken* pUserIdentityToken)
{
    // Implement user validation here if needed
    return OpcUa_BadUserAccessDenied;
}
UaServer* MyOpcServerCallback::createUaServer()
{
    // Create custom UaServer object
    return new MyUaServer();
}
///////////////////////////////////////////////////////////
Step(5)
Add the following code to servermain.cpp before calling pServer->start();

Code: Select all

        MyOpcServerCallback serverCallback;
        pServer->setCallback(&serverCallback);

        // Start server object
        ret = pServer->start();
Step(6)
Compile server
Set breakpoint in MyUaTransactionManager::beginQuery()
Call QueryFirst from a client
This query call should end up in MyUaTransactionManager::beginQuery()
Last edited by Support Team on 07 Feb 2013, 10:48, edited 1 time in total.
Best regards
Unified Automation Support Team

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

Re:How to implement Query Service to Server?

Post by Support Team »

Here is also some pseudo sample code. It is not a full implementation but it provides sample code for the context handling.

Fell free to post more sample code from your query implementation.

Code: Select all

// We need a user data class storing information for a continuation point
class MyContinuationPointData: public ContinuationPointUserDataBase
{
public:
    MyContinuationPointData(){}
    virtual ~MyContinuationPointData(){}

    // Replace the member with the information you 
    // need to continue
    int lastPosition;
};

UaStatus MyUaTransactionManager::beginQuery(UaQueryContext* pUaQueryContext)
{
    UaStatus ret;
	
    // Pseudo sample code for implementing the query
    // Should be executed in a worker thread
    //
    bool moreResults  = false;
    int  lastPosition = 0;

    // Check if it is a QueryNext
    if ( pUaQueryContext->getQueryType() == UaQueryContext::QueryNext )
    {
        // Get stored context of previous query
        ContinuationPointUserDataBase* pUserData = pUaQueryContext->m_pQueryContext->detachUserData();
        MyContinuationPointData* pCPData = (MyContinuationPointData*)pUserData;
        lastPosition = pCPData->lastPosition;
        delete pCPData;
    }

    // Check if it is a QueryFirst
    if ( pUaQueryContext->getQueryType() == UaQueryContext::QueryFirst )
    {
        UaQueryFirstContext* pQueryFirstContext = (UaQueryFirstContext*)pUaQueryContext;

        // Verify NodeTypes[]
        pUaQueryContext->m_pQueryContext->noOfNodeTypes();
        pUaQueryContext->m_pQueryContext->pNodeTypes();
        // Set parsing results
        pQueryFirstContext->m_parsingResults;
        
        // Verify Filter
        pUaQueryContext->m_pQueryContext->pFilter();
        // Set FilterResult
        pQueryFirstContext->m_filterResult;
    }


    // -------------------------------------
    // Execute or continue query
    // -------------------------------------

    // Set results
    pUaQueryContext->m_queryDataSets.create(1);

    if ( moreResults )
    {
        MyContinuationPointData* pCPData = new MyContinuationPointData;
        pCPData->lastPosition = lastPosition;
        pUaQueryContext->m_pQueryContext->setUserData(pCPData);
    }

    // Send the response to the client
    pUaQueryContext->sendResponse();

    // Delete query context if response was sent
    // Do not send a response or delete this object if UaTransactionManager::beginQuery returns an error
    delete pUaQueryContext;
	
    return ret;
}
Last edited by Support Team on 07 Feb 2013, 10:48, edited 1 time in total.
Best regards
Unified Automation Support Team

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

Re:How to implement Query Service to Server?

Post by Support Team »

We added Query support also to the client side but the patch there requires V1.3 of the C++ SDK.

Best Regards,
Unified Automation Support Team
Last edited by Support Team on 07 Feb 2013, 10:48, edited 1 time in total.
Best regards
Unified Automation Support Team

Lost+Found
Hero Member
Hero Member
Posts: 20
Joined: 07 Feb 2013, 11:01

Re:How to implement Query Service to Server?

Post by Lost+Found »

Thanks a lot for the great examples B)

They've helped to get started.

Toni

Lost+Found
Hero Member
Hero Member
Posts: 20
Joined: 07 Feb 2013, 11:01

Re:How to implement Query Service to Server?

Post by Lost+Found »

Hi,
I am not sure if this is a forgotten feature or misunderstanding on my behalf, but I cannot get the array size of the BrowsePath in UaSimpleAttributeOperand.

There is a setter for any index, and there is a getter for the whole array as a raw memory pointer. But I cannot iterate the raw array without knowing its size (NoOfBrowsePath).

Code: Select all

class UABASE_EXPORT UaSimpleAttributeOperand: public UaFilterOperand
{
 ...

    void setBrowsePathElement(OpcUa_UInt32 index, const UaQualifiedName& name, OpcUa_UInt32 arraySize);
    const OpcUa_QualifiedName* browsePath() const;
}
For now, I can work-around with getSimpleAttributeOperand(ptr, false);

Also, in FilterOperandType there is "SimpleAttribute", but not "Attribute". This is probably omited for a reason? I cannot diffrentiate the two in the spec, except for the editors annotation in the latter.

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

Re:How to implement Query Service to Server?

Post by Support Team »

You are right. The getter for the array size is missing.
We will add the getter to the next service release version.

To add the missing method you should add the following to the class declaration:

Code: Select all

    OpcUa_UInt32 noOfBrowsePath()const;
    const OpcUa_QualifiedName* browsePath() const;
and the following to the class implementation:

Code: Select all

OpcUa_UInt32 UaSimpleAttributeOperand::noOfBrowsePath()const
{
    return m_browsePath.length();
}
Best Regards,
Unified Automation Support Team
Best regards
Unified Automation Support Team

Post Reply