OPCFoundation / UA-.NETStandard

OPC Unified Architecture .NET Standard
Other
1.95k stars 945 forks source link

Add custom object type from xml #651

Closed johnjoal closed 5 years ago

johnjoal commented 5 years ago

I am loading objects and custom types from xml file. I am using ImportXml method that was provided in other thread. The problem is, that custom object types are not added to servers ObjectTypes collection.

According to the code, AddPredefinedNode should also add types defined in xml, but I can't make it work. For example this xml below, with a simple object type, I can't see MyCustomType is added to the servers ObjectTypes.

<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" 
           xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" 
           xmlns:ua="http://unifiedautomation.com/Configuration/NodeSet.xsd" 
           xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <NamespaceUris>
        <Uri>http://myorg.org/UA/</Uri>
    </NamespaceUris>
    <Aliases>
        <Alias Alias="Int16">i=4</Alias>
        <Alias Alias="UInt32">i=7</Alias>
        <Alias Alias="Organizes">i=35</Alias>
        <Alias Alias="HasTypeDefinition">i=40</Alias>
        <Alias Alias="HasProperty">i=46</Alias>
    </Aliases>
    <UAObjectType BrowseName="1:MyCustomType" NodeId="ns=1;i=4002">
      <DisplayName>MyCustomType</DisplayName>
      <Description>MyCustomType</Description>
      <References>
        <Reference IsForward="false" ReferenceType="HasSubtype">i=58</Reference>
      </References>
    </UAObjectType>
</UANodeSet>

And here is a method to read xml:

private void ImportXml(IDictionary<NodeId, IList<IReference>> externalReferences, string resourcepath)
        {
            try
            {
                NodeStateCollection predefinedNodes = new NodeStateCollection();

                Stream stream = new FileStream(resourcepath, FileMode.Open);
                Opc.Ua.Export.UANodeSet nodeSet = Opc.Ua.Export.UANodeSet.Read(stream);

                nodeSet.Import(SystemContext, predefinedNodes);

                for (int ii = 0; ii < predefinedNodes.Count; ii++)
                {
                    AddPredefinedNode(SystemContext, predefinedNodes[ii]);
                }

                // ensure the reverse refernces exist.
                AddReverseReferences(externalReferences);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
NakWarsi commented 5 years ago

check if you have added http://myorg.org/UA/ in namespace.cs file / if namespace is defined in your node manager.

your namespace must be like this:-

public static partial class Namespaces
    {
        /// <summary>
        /// The namespace for the nodes provided by the server.
        /// </summary>
        public const string NodeTest = "http://myorg.org/UA/ ";
    }
johnjoal commented 5 years ago

Yes, namespace is defined and if I have objects like <UAVariable> or <UAObject> then they are added to the address space and I can navigate them with UAExpert, but not object type.

Xml with one variable 'Signal' and object type 'MyCustomType' (check pic below, there is no MyCustomType under Types > ObjectTypes > BaseObjectType)

<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
           xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd"
           xmlns:ua="http://unifiedautomation.com/Configuration/NodeSet.xsd"
           xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <NamespaceUris>
    <Uri>http://myorg.org/UA/</Uri>
  </NamespaceUris>
  <Aliases>
    <Alias Alias="Int16">i=4</Alias>
    <Alias Alias="UInt32">i=7</Alias>
    <Alias Alias="Organizes">i=35</Alias>
    <Alias Alias="HasTypeDefinition">i=40</Alias>
    <Alias Alias="HasProperty">i=46</Alias>
  </Aliases>
  <UAObjectType BrowseName="1:MyCustomType" NodeId="ns=1;i=4002">
    <DisplayName>MyCustomType</DisplayName>
    <Description>MyCustomType</Description>
    <References>
      <Reference IsForward="false" ReferenceType="HasSubtype">i=58</Reference>
    </References>
  </UAObjectType>
  <UAVariable BrowseName="1:Signal" DataType="String" NodeId="ns=1;i=4003">
    <DisplayName>Signal</DisplayName>
    <Description>Signal</Description>
    <References>
      <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
      <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
    </References>
    <Value>
      <uax:String>Null</uax:String>
    </Value>
  </UAVariable>
</UANodeSet>

image

NakWarsi commented 5 years ago

reference type "orgnizes" must be there try this is must work :-

    <DisplayName>MyCustomType</DisplayName>
    <Description>MyCustomType</Description>
    <References>
      <Reference IsForward="false" ReferenceType="HasSubtype">i=58</Reference>
      <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
    </References>
  </UAObjectType>
johnjoal commented 5 years ago

That is different, if I add Organizes it is added to the Objects collection.

What I need, is to add it to the Types collection, and if I load same xml with opc-modeler (https://github.com/FreeOpcUa/opcua-modeler) it looks good:

image

NichtEuler commented 5 years ago

Try adding namespaceUris.Add(Namespaces.NodeTest); to your NodeManagers constructor.

public NodeManager(IServerInternal server, ApplicationConfiguration configuration)
        :
            base(server, configuration, Namespaces.NodeTest)
        {
            SystemContext.NodeIdFactory = this;

            m_configuration = configuration.ParseExtension<ReferenceServerConfiguration>();

            if (m_configuration == null)
            {
                m_configuration = new ReferenceServerConfiguration();
            }
            List<string> namespaceUris = new List<string>();
            namespaceUris.Add(Namespaces.NodeTest);
            NamespaceUris = namespaceUris;

            m_namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(namespaceUris[0]);
            m_lastUsedId = 0;

        }

Also I would like to point you to #563. From my what I've read the go to way for implementing Types is to build them in a modeler and then generate the corresponding classes through the Modelcompiler (https://github.com/OPCFoundation/UA-ModelCompiler). I guess there must be a way to get the Types working solely through NodeSet2.xml but that is something I am not completely sure of. Glad if anyone could confirm or deny.

AlinMoldovean commented 5 years ago

It is also possible to handle the custom types only from NodeSet2.xml, but it is much more easier with the support of classes generated by Model Compiler