Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModelCompiler invalid code for abstract datatypes #149

Closed
maxschiffer opened this issue Jan 29, 2024 · 5 comments
Closed

ModelCompiler invalid code for abstract datatypes #149

maxschiffer opened this issue Jan 29, 2024 · 5 comments

Comments

@maxschiffer
Copy link

Scenario:

Generating code from a NodeSet2.xml containing an abstract DataType and a non-abstract subtype of said abstract DataType.
In NodeSet2.xml the abstract DataType does not have any references to encoding nodes (Default Binary/XML).
In NodeSet2.xml the non-abstract DataType does have references to encoding nodes (Default Binary/XML).

Expected behavior:

Working Code ;)

Actual behavior:

The generated code for the abstract DataType looks like this:

/// <summary cref="IEncodeable.BinaryEncodingId" />
public virtual ExpandedNodeId BinaryEncodingId => throw new NotSupportedException();

/// <summary cref="IEncodeable.XmlEncodingId" />
public virtual ExpandedNodeId XmlEncodingId => throw new NotSupportedException();
            
/// <summary cref="IJsonEncodeable.JsonEncodingId" />
public virtual ExpandedNodeId JsonEncodingId => throw new NotSupportedException();

This causes the a .NET server using the OPCFoundation Stack (version 1.4.372.107) to crash since no encoding id is defined for the OPC DataType in the C# class (thats neither an interface nor abstract).

Find the files and a server sample attached.

IssueDemoServer.zip

@opcfoundation-org
Copy link
Contributor

Can you provides a simple model that demonstrates the issue?

@maxschiffer
Copy link
Author

In the original issue, in the IssueDemoServer.zip you find a simple model and server implementation demonstrating the issue.

@opcfoundation-org
Copy link
Contributor

It is not clear what the issue is.

Decoding/Encoding instances of the subtype works fine.
Obviously you cannot decode/encode instances of the abstract base type but that is expected for abstract types.

@maxschiffer
Copy link
Author

Obviously thats expected :D
Whats not expected is the model generator generating code that causes the server to crash, since the DataType class is not marked as abstract in the generated code. So when the nodemanager is importing all types contained in the assembly into the encoding factory it causes a crash due to missing encoding Ids.

From my sample I've copied the code snippet where the exception/crash occurs:
(IssueDemoServer.cs#14)

protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
{
    //THIS IS WHERE THE SERVER FAILS TO START --> SEE ./GeneratedCode/Demo.DataTypes.cs#81/84/87

    try
    {
        server.Factory.AddEncodeableTypes(GetType().Assembly);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    return base.CreateMasterNodeManager(server, configuration);
}

I would expect the model compiler to mark abstract datatypes differently or generate them differently so the stack can handle this appropriately and does not try to load the encoding information for an abstract type like this:
image

@maxschiffer
Copy link
Author

To clarify:
The generated code looks like this:

    [System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
    [DataContract(Namespace = Demo.Namespaces.Demo)]
    public partial class AbstractParentDataType : IEncodeable, IJsonEncodeable
    {

while it should look like this (added abstract keyword):

    [System.CodeDom.Compiler.GeneratedCodeAttribute("Opc.Ua.ModelCompiler", "1.0.0.0")]
    [DataContract(Namespace = Demo.Namespaces.Demo)]
    public abstract partial class AbstractParentDataType : IEncodeable, IJsonEncodeable
    {
        

So this (excerpt from EncodableFactory.cs#L447-455)

  for (int ii = 0; ii < systemTypes.Length; ii++)
  {
      if (systemTypes[ii].GetTypeInfo().IsAbstract)
      {
          continue;
      }

      AddEncodeableType(systemTypes[ii], unboundTypeIds);
  }

does not attempt to add abstract types to the encodable factory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants