Skip to content

A Method loaded from a NodeSet2 keeps InputArguments as an untyped child, so every Call with an argument fails with BadTooManyArguments #4422

Description

@romanett

Version: OPCFoundation.NetStandard.Opc.Ua.Server 2.0.0-preview.4

A Method that comes out of a NodeSet2 document loaded with AddRuntimeNodeSet /
AddRuntimeNodeSetAsync keeps its InputArguments Property as an untyped
PropertyState child. MethodState.InputArguments — the typed
PropertyState<ArrayOf<Argument>> that Call validates against — stays null, so the
server believes the Method declares no arguments and answers BadTooManyArguments to
every call that carries one.

The value itself imports correctly: a Client that reads the InputArguments Property gets
the arguments the document declares. Only the binding to the typed property is missing.

Repro

ModelControl.NodeSet2.xml, a Method with one String argument:

<UAMethod NodeId="ns=1;i=1010" BrowseName="1:Load" ParentNodeId="ns=1;i=1000">
  <DisplayName>Load</DisplayName>
  <References>
    <Reference ReferenceType="HasProperty">ns=1;i=1011</Reference>
    <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1000</Reference>
  </References>
</UAMethod>
<UAVariable NodeId="ns=1;i=1011" BrowseName="InputArguments" ParentNodeId="ns=1;i=1010"
            DataType="Argument" ValueRank="1" ArrayDimensions="1">
  <DisplayName>InputArguments</DisplayName>
  <References>
    <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
    <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=1010</Reference>
  </References>
  <Value>
    <ListOfExtensionObject xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
      <ExtensionObject>
        <TypeId><Identifier>i=297</Identifier></TypeId>
        <Body><Argument><Name>revision</Name>
          <DataType><Identifier>i=12</Identifier></DataType>
          <ValueRank>-1</ValueRank><ArrayDimensions />
        </Argument></Body>
      </ExtensionObject>
    </ListOfExtensionObject>
  </Value>
</UAVariable>

In the RuntimeNodeSetOptions.Configure hook:

MethodState load = builder.Node<MethodState>("ModelControl/Load").Node;

// load.InputArguments is null
// GetChildren reports one PropertyState named "InputArguments" with the right value

Calling Load("Rev1") then answers BadTooManyArguments; calling a Method that declares
no arguments works.

Workaround

NodeState.CreateChild with createOrReplace builds the typed Property and assigns it —
PropertyState<T> is abstract, so it cannot be constructed directly:

var children = new List<BaseInstanceState>();
method.GetChildren(context, children);

BaseVariableState imported = children
    .OfType<BaseVariableState>()
    .FirstOrDefault(c => c.BrowseName.Name == BrowseNames.InputArguments);

if (imported != null && method.InputArguments == null)
{
    bool decoded = imported.WrappedValue.TryGetValue(out ArrayOf<ExtensionObject> encoded);

    method.RemoveChild(imported);
    method.CreateChild(context, new QualifiedName(BrowseNames.InputArguments), true);

    method.InputArguments.NodeId = imported.NodeId;
    method.InputArguments.DisplayName = imported.DisplayName;
    method.InputArguments.Value = decoded ? ExtensionObject.ToArray<Argument>(encoded) : default;
}

Prior art

#1056 reported the same symptom in 2022 for a hand-rolled UANodeSet.Import and was
closed as not planned, with the note "I think we need to provide a helper function and
samples in the server to do this."

The situation has changed: AddRuntimeNodeSet is now a first-class API whose whole purpose
is to host a vendor NodeSet2 document, and a vendor document that declares Methods with
arguments is ordinary. Every server that loads one needs the six lines above, which reach
into CreateChild semantics most callers should not have to know about. Doing the binding
inside the NodeSet2 import — or shipping the helper #1056 asked for — would remove that.

OutputArguments presumably needs the same treatment; this sample only exercises inputs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugA bug was identified and should be fixed.server

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions