Methods... how to get them to execute my stuff instead of automatically generated stubs?

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

Moderator: uasdkcpp

Post Reply
tommys
Sr. Member
Sr. Member
Posts: 15
Joined: 03 Oct 2023, 16:42

Methods... how to get them to execute my stuff instead of automatically generated stubs?

Post by tommys »

Hi,

we have a rather big information model from which we automatically generate C++ code using UaModeler. Some of the generated classes have OPC UA Methods. Out of the box, these Methods are called when an OPC UA Client like UaExpert call them. So far, so good. The only problem is that the implementation of these Methods do nothing. Of course, they are automatically generated stub methods. So the real problem is what shall be done so my implementation specific logic is executed instead?

I have read the SDK class documentation for related classes and methods (NodeManager/NodeManagerUaNode::getMethodHandle, BaseObjectType::getMethodManager) and I have looked at the C++ server lesson 4 example, of course. Unfortunately, our code is automatically generated so the information in lesson 4 is not directly applicable. And from the information I've read it is still not clear to me what I shall do.
And I have read the source code finding my way down from the Call Service BeginCall stub to how the methodHandle is gotten from nodeManagerRoot and then how the methodManager of the methodHandle is used to call beginCall. Still I don't know what I shall do.

I thought that I could maybe subclass the automatically generated classes that have the OPC UA Methods, and simply override their methods since they are declared virtual. But no, then I get the problem to instantiate my custom classes and that is not possible really, at least not straighforward, because those objects are created in other parts of the automatically generated code.

What about overriding NodeManagerUaNode::getMethodHandle? I already have subclassed NodeManagerUaNode (or my automatically generated NodeManager in order to handle writing to variables by overriding beforeSetAttributeValue) but I think that the implementation in NodeManagerUaNode::getMethodHandle is rather complex with permission checks for example, so it does not feel a natural thing to do.

Hmm. I'm out of ideas, can you give me some advices maybe?

Regards,
/Tommy

tommys
Sr. Member
Sr. Member
Posts: 15
Joined: 03 Oct 2023, 16:42

Re: Methods... how to get them to execute my stuff instead of automatically generated stubs?

Post by tommys »

I found the answer myself, replying it here to maybe help other out:


Before issuing the beginCall method, the NodeManagerUaNode::getMethodHandle is called with the session, objectId and methodId. If permission checks are fine, it then checks with the object being called if it has a methodManager defined. If the object has a methodManager it creates a new MethodHandle and sets its m_methodManager to point to our object's methodManager. This methodHandle is returned by NodeManagerUaNode::getMethodHandle and used to actually do call our method: methodHandle->methodManager->beginCall(callContext, methodHandle);

So, all I had to do was to subclass MethodManager and override the beginCall method. Additionally, I had to inject a reference to the object instance of my automatically generated class: ModelMethodClass, which is used in the beginCall implementation:

Code: Select all

class MyMethodManager : public MethodManager
{
public:
    explicit MyMethodManager(ModelMethodClass* mmc) : m_mmc(mmc){}
    
    UaStatus beginCall(MethodManagerCallback * pCallback, 
                                            const ServiceContext & serviceContext, 
                                            OpcUa_UInt32 callbackHandle, 
                                            MethodHandle * pMethodHandle, 
                                            const UaVariantArray & inputArguments)
    {
        MethodHandleUaNode* pMethodHandleUaNode = static_cast<MethodHandleUaNode*>(pMethodHandle);
        UaMethod*           pMethod             = NULL;
 
        if (pMethodHandleUaNode)
        {
            pMethod = pMethodHandleUaNode->pUaMethod();
            if (pMethod)
            {
                // Check if we have the Measure method
                if (pMethod->nodeId() == m_mmc->getMeasure()->nodeId())
                {
                    // Here goes my code :-)
                }
    }
};
In appropriate location just create a MyMethodManager object and initiate it with the ModelMethodClass object: MyMethodManager* mmm = MyMethodManager(mmc);
Finally set ModelMethodClass's method manager: mmc->setMethodManager(mmm);

That's it.
/Tommy

Post Reply