python-opcua icon indicating copy to clipboard operation
python-opcua copied to clipboard

How to use custom structure as InputArgument in method?

Open AndreyKl4 opened this issue 5 years ago • 5 comments
trafficstars

Hi all!

i am a very new with OPC UA. Perhaps, my question is a stupid one but for me, it is really hard nut to crack=) I have defined my own structure as in example(https://github.com/FreeOpcUa/python-opcua/blob/master/examples/server-create-custom-structures.py) and after that i tried to set my custom structure as an InputArgument in my method:

   basic_var = server.nodes.objects.add_variable(ua.NodeId(namespaceidx=nodeid), 'BasicStruct',
                                                            ua.Variant(None, ua.VariantType.Null),
                                                            datatype=basic_struct.data_type)
    basic_var = ua.Argument()
    basic_var.Name = 'Test_structure'
    basic_var.DataType = basic_struct.data_type
    basic_var.ValueRank = -3 
    basic_var.ArrayDimensions = 0

 
    node_methods.add_method(nodeid, 'simple_callback', call_back ,[basic_var], [])  

In UaExpert Client I can monitor my structure as InputArgument of my method but when i click ''...'' i see empty ''value'' and '' name'' fields. Perhaps smb have already faced such problem and can help me with this issue=)

Thanks in advance!!!

Andrey

AndreyKl4 avatar Oct 27 '20 11:10 AndreyKl4

typically you pass the int value of the varianttype into the list like:

[ ua.VariantType.UInt16, ua.VariantType.UInt32, ua.VariantType.Float ]

i am still looking in the opcua specs about that topic...

def _create_method(parent, nodeid, qname, callback, inputs, outputs):
    addnode = ua.AddNodesItem()
    addnode.RequestedNewNodeId = nodeid
    addnode.BrowseName = qname
    addnode.NodeClass = ua.NodeClass.Method
    addnode.ParentNodeId = parent.nodeid
    addnode.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasComponent)
    #node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
    attrs = ua.MethodAttributes()
    attrs.Description = ua.LocalizedText(qname.Name)
    attrs.DisplayName = ua.LocalizedText(qname.Name)
    attrs.WriteMask = 0
    attrs.UserWriteMask = 0
    attrs.Executable = True
    attrs.UserExecutable = True
    addnode.NodeAttributes = attrs
    results = parent.server.add_nodes([addnode])
    results[0].StatusCode.check()
    method = node.Node(parent.server, results[0].AddedNodeId)
    if inputs:
        create_property(method,
                        ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex),
                        ua.QualifiedName("InputArguments", 0),
                        [_vtype_to_argument(vtype) for vtype in inputs], # <------------- below
                        varianttype=ua.VariantType.ExtensionObject,
                        datatype=ua.ObjectIds.Argument)
def _vtype_to_argument(vtype):
    if isinstance(vtype, ua.Argument): # <------------------ Case 1
        return vtype
    arg = ua.Argument()
    if isinstance(vtype, ua.VariantType): # <------------------ Case 2
        arg.DataType = ua.NodeId(vtype.value)
    else:
        arg.DataType = ua.NodeId(vtype) # <------------------ Case 3
    return arg

AndreasHeine avatar Oct 27 '20 12:10 AndreasHeine

I do not think using extension objects with methods is well tested and I'm not sure how the associated python function will react.

zerox1212 avatar Oct 27 '20 15:10 zerox1212

@AndreasHeine @zerox1212 Thanks for answers. As far as i understood it is a bit difficult to use such structure as input argument in method: basic_struct_name = 'basic_structure' basic_struct = ua_server.create_structure(basic_struct_name) basic_struct.add_field('var1', ua.VariantType.Int32) basic_struct.add_field('var2', ua.VariantType.Boolean) basic_struct.add_field('var3', ua.VariantType.String)

    # add an advance structure which uses our basic structure
    nested_struct_name = 'nested_structure'
    nested_struct = ua_server.create_structure(nested_struct_name)
    nested_struct.add_field('var4', ua.VariantType.String)
    nested_struct.add_field('array', ua.VariantType.Int32)
    # add simple structure as field
    nested_struct.add_field('Field', basic_struct)

    basic_var = server.nodes.objects.add_variable(ua.NodeId(namespaceidx=nodeid), 'BasicStruct',
                                                        ua.Variant(None, ua.VariantType.Null),
                                                        datatype=basic_struct.data_type)
    basic_var = ua.Argument()
    basic_var.Name = 'Test_structure'
    basic_var.DataType = basic_struct.data_type
    basic_var.ValueRank = -3 
    basic_var.ArrayDimensions = 0

   node_methods.add_method(nodeid, 'simple_callback', call_back ,[basic_var], []) 

For my project I need to generate such nested structures in my server and after that to use them as inputs arguments in my methods. If i use all input arguments with out nested structure (flat), it will be difficult to find out in Client which parameter is responsible for what, because of big number of input arguments=( That is why i want to create first of all a nested structure and after that to use it as input argument. Perhaps smb has an idea in what way i can solve it=)

AndreyKl4 avatar Oct 28 '20 08:10 AndreyKl4

@AndreyKl4 @zerox1212

https://github.com/FreeOpcUa/python-opcua/pull/1158

AndreasHeine avatar Nov 06 '20 13:11 AndreasHeine

I have implemented custom structures in one of my projects. You can have a look and check if that helps you. https://github.com/lmundeja/OPCUA-Webshop-MiniFactory/tree/main/OPCUA_Modbus

lmundeja avatar Nov 18 '20 16:11 lmundeja