nauful / LibUA

Open-source OPC UA client and server library
Apache License 2.0
262 stars 94 forks source link

Bind a handler to a NodeMethod #23

Closed rjmmartins closed 4 years ago

rjmmartins commented 4 years ago

So I was trying to create a new method in the server just like:

   `NodeMethod method = new NodeMethod(new NodeId(2, "Trigger"),
            new QualifiedName("Trigger"), new LocalizedText("Trigger"),
            new LocalizedText("Trigger"), 0, 0, true, true);

        ItemsRoot.References.Add(new ReferenceNode(new NodeId(UAConst.Organizes), method.Id, false));
        method.References.Add(new ReferenceNode(new NodeId(UAConst.Organizes), ItemsRoot.Id, true));
        AddressSpaceTable.TryAdd(method.Id, method);`     

and it is added without a problem. However, I could not find a way to customize what happens when the method is invoked... Can someone help me?

Thanks

nauful commented 4 years ago

I didn't finish an interface for this, but here's what I suggest:

Look at DispatchMessage_CallRequest https://github.com/nauful/LibUA/blob/ea5b91c98cda4d64eb90efbd345d57f60b330b0b/NET%20Core/LibUA/NetDispatcher.cs#L2733

Where you see

for (uint i = 0; i < NoofMethodsToCall; i++)
{
    succeeded &= respBuf.Encode((UInt32)StatusCode.Good);
    // InputArgumentResults: Array of StatusCode
    succeeded &= respBuf.Encode((UInt32)0);
    // InputArgumentDiagnosticInfos
    succeeded &= respBuf.Encode((UInt32)0);
    // OutputArguments: Array of Variant
    succeeded &= respBuf.Encode((UInt32)0);
}

You can add a method to LibUA.Server.Application such as HandleMethodCall(CallMethodRequest req), then call it for each item in the reqs array.

If you want to encode a response, which is something I have not tested in a while, this may be how to encode the response:

for (uint i = 0; i < NoofMethodsToCall; i++)
{
    var resp = app.HandleMethodCall(reqs[i]);
    succeeded &= respBuf.Encode((UInt32)StatusCode.Good);
    // InputArgumentResults: Array of StatusCode
    succeeded &= respBuf.Encode((UInt32)resps.NumStatusCodes);
    ... Encode each status code, then 0, then the number of result variants, then each result variant
rjmmartins commented 4 years ago

Thank you for your response, it works just like that.

But it means that all methods will have the same "handle" right?

nauful commented 4 years ago

Methods are defined by MethodId in the CallMethodRequest class, which defines the method handle, and can have any user-defined value.

Edit for clarity: The client can send any NodeId value for MethodId in each CallMethodRequest and the server can decide (user-defined implementation) on what to do for that MethodId. You could just have a handler with a switch statement or table of functions depending on what MethodId is requested for multiple handlers. One common way is Dictionary<NodeId, function>.

rjmmartins commented 4 years ago

This is exactly what I was looking for, thank you!

In the Application.cs I added:

public delegate void MethodCallHandler(CallMethodRequest req);
public Dictionary<NodeId, MethodCallHandler> MethodMap;

In the NetDispatcher.cs:

for (uint i = 0; i < NoofMethodsToCall; i++)
{
    app.MethodMap.TryGetValue(reqs[i].MethodId, out Application.MethodCallHandler mch);
    mch?.Invoke(reqs[i]);

    succeeded &= respBuf.Encode((UInt32)StatusCode.Good);
    (...)
}

Then, where I create my methods (DemoApplication)

MethodMap.Add(method.Id, HandleMethodCall_Trigger);

private void HandleMethodCall_Trigger(CallMethodRequest req)
{
    //method logic
}

Thanks again for your help. Cheers

nauful commented 4 years ago

Glad to help.