OPCFoundation / UA-ModelCompiler

ModelCompiler converts XML files into C# and ANSI C
MIT License
151 stars 94 forks source link

Invalid node id references when compiling multiple XML informational models #177

Closed ancapaul closed 5 months ago

ancapaul commented 6 months ago

Hello,

When I compile multiple XML informational models where one of the informational models relies on the other. The resulting node set file contains node id references to the other informational model that are incorrect. All the node id references to the other informational model are “ns=2;i=0”

This is the informational model that contains all the types:

<?xml version="1.0" encoding="utf-8" ?>
<opc:ModelDesign
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:opc="http://opcfoundation.org/UA/ModelDesign.xsd"
  xmlns:ua="http://opcfoundation.org/UA/"
  xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
    xmlns="http://opcfoundation.org/UA/MachineFramework/"
  TargetNamespace="http://opcfoundation.org/UA/MachineFramework/">
  <opc:Namespaces>
    <opc:Namespace Name="OpcUa" Prefix="Opc.Ua" XmlNamespace="http://opcfoundation.org/UA/2008/02/Types.xsd">http://opcfoundation.org/UA/</opc:Namespace>
    <opc:Namespace Name="MachineFramework" Prefix="MachineFramework" XmlNamespace="http://opcfoundation.org/UA/MachineFramework/Types.xsd">http://opcfoundation.org/UA/MachineFramework/</opc:Namespace>
  </opc:Namespaces>

  <opc:DataType SymbolicName="LogLevelType" BaseType="ua:Enumeration">
    <opc:Fields>
      <opc:Field Name="Debug" Identifier="0" Value="0"/>
      <opc:Field Name="Trace" Identifier="1" Value="1"/>
      <opc:Field Name="Information" Identifier="2" Value="2"/>
      <opc:Field Name="Warning" Identifier="3" Value="3"/>
      <opc:Field Name="Error" Identifier="4" Value="4"/>
      <opc:Field Name="Critical" Identifier="5" Value="5"/>
    </opc:Fields>
  </opc:DataType>
  <opc:DataType SymbolicName="LogMessage" BaseType="ua:Structure">
    <opc:Description>Log message</opc:Description>
    <opc:Fields>
      <opc:Field Name="IsNull" DataType="ua:Boolean" />
      <opc:Field Name="Message" DataType="ua:String" />
      <opc:Field Name="DateTime" DataType="ua:DateTime" />
      <opc:Field Name="Context" DataType="ua:String" />
    </opc:Fields>
  </opc:DataType>

  <opc:Method SymbolicName="GetLogMessagesFunctionType">
    <opc:InputArguments>
      <opc:Argument Name="BeginDateTime" DataType="ua:DateTime" />
      <opc:Argument Name="EndDateTime" DataType="ua:DateTime" />
      <opc:Argument Name="Context" DataType="ua:String" />
    </opc:InputArguments>
    <opc:OutputArguments>
      <opc:Argument Name="LogMessage" DataType="LogMessage" ValueRank="Array" ArrayDimensions="100" />
    </opc:OutputArguments>
  </opc:Method>
  <opc:Method SymbolicName="LogFunctionType">
    <opc:InputArguments>
      <opc:Argument Name="Context" DataType="ua:String"/>
      <opc:Argument Name="LogLevel" DataType="LogLevelType"/>
      <opc:Argument Name="Message" DataType="ua:String">
        <opc:Description>Log message</opc:Description>
      </opc:Argument>
    </opc:InputArguments>
  </opc:Method>
  <opc:ObjectType SymbolicName="ILogger" BaseType="ua:BaseInterfaceType">
    <opc:Description>Logger</opc:Description>
    <opc:Children>
      <opc:Method SymbolicName="GetLogMessages" ModellingRule="Mandatory" TypeDefinition="GetLogMessagesFunctionType" />
      <opc:Method SymbolicName="Log" ModellingRule="Mandatory" TypeDefinition="LogFunctionType" />
      <opc:Property SymbolicName="LastLogMessage" DataType="LogMessage" AccessLevel="Read">
        <opc:Description>The last log message</opc:Description>
      </opc:Property>
    </opc:Children>
  </opc:ObjectType>
</opc:ModelDesign>

This is the informational model that relies on types defined in the above informational model:

<?xml version="1.0" encoding="utf-8" ?>
<opc:ModelDesign
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:opc="http://opcfoundation.org/UA/ModelDesign.xsd"
    xmlns:ua="http://opcfoundation.org/UA/"
    xmlns:mf="http://opcfoundation.org/UA/MachineFramework/"
    xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
    xmlns="http://opcfoundation.org/UA/MachineFramework/Fridge/"
    TargetNamespace="http://opcfoundation.org/UA/MachineFramework/Fridge/">
    <opc:Namespaces>
        <opc:Namespace Name="OpcUa" Prefix="Opc.Ua" XmlNamespace="http://opcfoundation.org/UA/2008/02/Types.xsd">http://opcfoundation.org/UA/</opc:Namespace>
        <opc:Namespace Name="Fridge" Prefix="Fridge">http://opcfoundation.org/UA/MachineFramework/Fridge/</opc:Namespace>
        <opc:Namespace Name="MachineFramework" Prefix="MachineFramework" FilePath="MachineFramework.xml" XmlNamespace="http://opcfoundation.org/UA/MachineFramework/Types.xsd">http://opcfoundation.org/UA/MachineFramework/</opc:Namespace>
    </opc:Namespaces>
    <!-- Machine Framework Folder Structure -->
    <opc:ObjectType SymbolicName="Control" BaseType="ua:FolderType">
        <opc:Children>
            <opc:Object SymbolicName="Logger" TypeDefinition="mf:ILogger" SupportsEvents="true" />
        </opc:Children>
    </opc:ObjectType>
</opc:ModelDesign>

The above two informational models are compiled using the following two commands:

Opc.Ua.ModelCompiler.exe compile^ -d2 .\MachineFramework.xml^ -cg MachineFrameworkIdentfiers.csv^ -version v105 -o2 .\MachineFrameworkOutput\

Opc.Ua.ModelCompiler.exe compile^ -d2 .\Fridge.xml^ -d2 .\MachineFramework.xml^ -cg FridgeIdentfiers.csv^ -version v105 -o2 .\FridgeOutput\

The resulting node set file for the fridge model has “ns=2;i=0” for all types that reference the machine framework informational model:

<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" LastModified="2024-05-06T02:11:39.8204392Z" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
  <NamespaceUris>
    <Uri>http://opcfoundation.org/UA/MachineFramework/Fridge/</Uri>
    <Uri>http://opcfoundation.org/UA/MachineFramework/</Uri>
  </NamespaceUris>
  <Models>
    <Model ModelUri="http://opcfoundation.org/UA/MachineFramework/Fridge/" Version="1.0.0" PublicationDate="2024-05-06T02:11:39.8204392Z" ModelVersion="1.0.0">
      <RequiredModel ModelUri="http://opcfoundation.org/UA/" XmlSchemaUri="http://opcfoundation.org/UA/2008/02/Types.xsd" />
      <RequiredModel ModelUri="http://opcfoundation.org/UA/MachineFramework/" XmlSchemaUri="http://opcfoundation.org/UA/MachineFramework/Types.xsd" />
    </Model>
  </Models>
  <Aliases>
    <Alias Alias="Boolean">i=1</Alias>
    <Alias Alias="SByte">i=2</Alias>
    <Alias Alias="Byte">i=3</Alias>
    <Alias Alias="Int16">i=4</Alias>
    <Alias Alias="UInt16">i=5</Alias>
    <Alias Alias="Int32">i=6</Alias>
    <Alias Alias="UInt32">i=7</Alias>
    <Alias Alias="Int64">i=8</Alias>
    <Alias Alias="UInt64">i=9</Alias>
    <Alias Alias="Float">i=10</Alias>
    <Alias Alias="Double">i=11</Alias>
    <Alias Alias="DateTime">i=13</Alias>
    <Alias Alias="String">i=12</Alias>
    <Alias Alias="ByteString">i=15</Alias>
    <Alias Alias="Guid">i=14</Alias>
    <Alias Alias="XmlElement">i=16</Alias>
    <Alias Alias="NodeId">i=17</Alias>
    <Alias Alias="ExpandedNodeId">i=18</Alias>
    <Alias Alias="QualifiedName">i=20</Alias>
    <Alias Alias="LocalizedText">i=21</Alias>
    <Alias Alias="StatusCode">i=19</Alias>
    <Alias Alias="Structure">i=22</Alias>
    <Alias Alias="Number">i=26</Alias>
    <Alias Alias="Integer">i=27</Alias>
    <Alias Alias="UInteger">i=28</Alias>
    <Alias Alias="HasComponent">i=47</Alias>
    <Alias Alias="HasProperty">i=46</Alias>
    <Alias Alias="Organizes">i=35</Alias>
    <Alias Alias="HasEventSource">i=36</Alias>
    <Alias Alias="HasNotifier">i=48</Alias>
    <Alias Alias="HasSubtype">i=45</Alias>
    <Alias Alias="HasTypeDefinition">i=40</Alias>
    <Alias Alias="HasModellingRule">i=37</Alias>
    <Alias Alias="HasEncoding">i=38</Alias>
    <Alias Alias="HasDescription">i=39</Alias>
    <Alias Alias="HasCause">i=53</Alias>
    <Alias Alias="ToState">i=52</Alias>
    <Alias Alias="FromState">i=51</Alias>
    <Alias Alias="HasEffect">i=54</Alias>
    <Alias Alias="HasTrueSubState">i=9004</Alias>
    <Alias Alias="HasFalseSubState">i=9005</Alias>
    <Alias Alias="HasDictionaryEntry">i=17597</Alias>
    <Alias Alias="HasCondition">i=9006</Alias>
    <Alias Alias="HasGuard">i=15112</Alias>
    <Alias Alias="HasAddIn">i=17604</Alias>
    <Alias Alias="HasInterface">i=17603</Alias>
  </Aliases>
  <UAObjectType NodeId="ns=1;i=1" BrowseName="1:Control">
    <DisplayName>Control</DisplayName>
    <References>
      <Reference ReferenceType="HasComponent">ns=1;i=7</Reference>
      <Reference ReferenceType="HasSubtype" IsForward="false">i=61</Reference>
    </References>
  </UAObjectType>
  <UAObject NodeId="ns=1;i=7" BrowseName="1:Logger" ParentNodeId="ns=1;i=1" EventNotifier="1">
    <DisplayName>Logger</DisplayName>
    <References>
      <Reference ReferenceType="HasComponent">ns=1;i=8</Reference>
      <Reference ReferenceType="HasComponent">ns=1;i=11</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=13</Reference>
      <Reference ReferenceType="HasTypeDefinition">ns=2;i=0</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1</Reference>
    </References>
  </UAObject>
  <UAMethod NodeId="ns=1;i=8" BrowseName="2:GetLogMessages" ParentNodeId="ns=1;i=7" MethodDeclarationId="ns=2;i=0">
    <DisplayName>GetLogMessages</DisplayName>
    <References>
      <Reference ReferenceType="HasProperty">ns=1;i=9</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=10</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=7</Reference>
    </References>
  </UAMethod>
  <UAVariable NodeId="ns=1;i=9" BrowseName="InputArguments" ParentNodeId="ns=1;i=8" DataType="i=296" ValueRank="1" ArrayDimensions="3">
    <DisplayName>InputArguments</DisplayName>
    <References>
      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=8</Reference>
    </References>
    <Value>
      <ListOfExtensionObject xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>BeginDateTime</Name>
              <DataType>
                <Identifier>i=13</Identifier>
              </DataType>
              <ValueRank>-1</ValueRank>
              <ArrayDimensions />
            </Argument>
          </Body>
        </ExtensionObject>
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>EndDateTime</Name>
              <DataType>
                <Identifier>i=13</Identifier>
              </DataType>
              <ValueRank>-1</ValueRank>
              <ArrayDimensions />
            </Argument>
          </Body>
        </ExtensionObject>
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>Context</Name>
              <DataType>
                <Identifier>i=12</Identifier>
              </DataType>
              <ValueRank>-1</ValueRank>
              <ArrayDimensions />
            </Argument>
          </Body>
        </ExtensionObject>
      </ListOfExtensionObject>
    </Value>
  </UAVariable>
  <UAVariable NodeId="ns=1;i=10" BrowseName="OutputArguments" ParentNodeId="ns=1;i=8" DataType="i=296" ValueRank="1" ArrayDimensions="1">
    <DisplayName>OutputArguments</DisplayName>
    <References>
      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=8</Reference>
    </References>
    <Value>
      <ListOfExtensionObject xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>LogMessage</Name>
              <DataType>
                <Identifier>ns=2;i=0</Identifier>
              </DataType>
              <ValueRank>1</ValueRank>
              <ArrayDimensions>
                <UInt32>100</UInt32>
              </ArrayDimensions>
            </Argument>
          </Body>
        </ExtensionObject>
      </ListOfExtensionObject>
    </Value>
  </UAVariable>
  <UAMethod NodeId="ns=1;i=11" BrowseName="2:Log" ParentNodeId="ns=1;i=7" MethodDeclarationId="ns=2;i=0">
    <DisplayName>Log</DisplayName>
    <References>
      <Reference ReferenceType="HasProperty">ns=1;i=12</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=7</Reference>
    </References>
  </UAMethod>
  <UAVariable NodeId="ns=1;i=12" BrowseName="InputArguments" ParentNodeId="ns=1;i=11" DataType="i=296" ValueRank="1" ArrayDimensions="3">
    <DisplayName>InputArguments</DisplayName>
    <References>
      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=11</Reference>
    </References>
    <Value>
      <ListOfExtensionObject xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>Context</Name>
              <DataType>
                <Identifier>i=12</Identifier>
              </DataType>
              <ValueRank>-1</ValueRank>
              <ArrayDimensions />
            </Argument>
          </Body>
        </ExtensionObject>
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>LogLevel</Name>
              <DataType>
                <Identifier>ns=2;i=0</Identifier>
              </DataType>
              <ValueRank>-1</ValueRank>
              <ArrayDimensions />
            </Argument>
          </Body>
        </ExtensionObject>
        <ExtensionObject>
          <TypeId>
            <Identifier>i=297</Identifier>
          </TypeId>
          <Body>
            <Argument>
              <Name>Message</Name>
              <DataType>
                <Identifier>i=12</Identifier>
              </DataType>
              <ValueRank>-1</ValueRank>
              <ArrayDimensions />
              <Description>
                <Text>Log message</Text>
              </Description>
            </Argument>
          </Body>
        </ExtensionObject>
      </ListOfExtensionObject>
    </Value>
  </UAVariable>
  <UAVariable NodeId="ns=1;i=13" BrowseName="2:LastLogMessage" ParentNodeId="ns=1;i=7" DataType="ns=2;i=0">
    <DisplayName>LastLogMessage</DisplayName>
    <Description>The last log message</Description>
    <References>
      <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
      <Reference ReferenceType="HasModellingRule">i=78</Reference>
      <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=7</Reference>
    </References>
  </UAVariable>
</UANodeSet>

Can you please tell me what I'm doing wrong.

Thank you.

opcfoundation-org commented 5 months ago

The dependent model CSV must have the same name and be in the same folder as the xml.

The MachineFrameworkIdentfiers.csv cannot be passed on the command line when building Fridge.xml

You also have the option of using MachineFramework.NodeSet2.xml instead of MachineFramework.xml

ancapaul commented 5 months ago

Hello,

Thanks for your response. I tried both your suggestions but it still doesn't work. The node id simply changed to "ns=1;i=1" for all types that refer to the MachineFramework.xml types.

opcfoundation-org commented 5 months ago

Try ordering your dependencies starting with the target schema at top with the UA schema at the bottom.

Confirm your CSV has the correct IDs in it.

When I run it on my machine it works.

ancapaul commented 5 months ago

I tried building using the following:

SET PATH=%PATH%;.\UA-ModelCompiler\build\bin\Release\net8.0\;

Opc.Ua.ModelCompiler.exe compile^
 -d2 .\MachineFramework.xml^
 -cg MachineFramework.csv^
 -version v105 -o2 .\MachineFrameworkOutput\
Opc.Ua.ModelCompiler.exe compile^
 -d2 .\Fridge.xml^
 -d2 .\MachineFrameworkOutput\MachineFramework.NodeSet2.xml^
 -cg Fridge.csv^
 -version v105 -o2 .\FridgeOutput\
pause

and

SET PATH=%PATH%;.\UA-ModelCompiler\build\bin\Release\net8.0\;

Opc.Ua.ModelCompiler.exe compile^
 -d2 .\MachineFramework.xml^
 -cg MachineFramework.csv^
 -version v105 -o2 .\MachineFrameworkOutput\
Opc.Ua.ModelCompiler.exe compile^
 -d2 .\Fridge.xml^
 -d2 .\MachineFramework.xml^
 -cg Fridge.csv^
 -version v105 -o2 .\FridgeOutput\
pause
ancapaul commented 5 months ago

Sorry. Yes it appears to be working but the node ID's in Fridge NodeSet file don't seem to match what is in the MachineFramework NodeSet file.

ancapaul commented 5 months ago

It appears that the "ns" part of the node id is wrong. In the Fridge NodeSet file it is "ns=2;i=23" and in the MachineFramework NodeSet file it is "ns=1;i=23"

opcfoundation-org commented 5 months ago

That is not wrong. The indexes are references to the URIs in the NamespaceUris array at the top of the NodeSet.

ancapaul commented 5 months ago

Thank you.